aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Nokis Mavrogiannopoulos 2008-02-20 18:50:41 +0000
committerGravatar Nokis Mavrogiannopoulos 2008-02-20 18:50:41 +0000
commit75636faa679422cb7e360167d03163ca1985f772 (patch)
tree7a36b807e12e1e6c1e7da965d8ed667b3619287a /src
parent56dd14991416e369ad7657add7e61495d18ae6b2 (diff)
added tags0.5.0-alpha
Diffstat (limited to 'src')
-rw-r--r--src/gnutls_cache.c84
-rw-r--r--src/gnutls_config.c131
-rw-r--r--src/gnutls_hooks.c273
-rw-r--r--src/mod_gnutls.c28
4 files changed, 408 insertions, 108 deletions
diff --git a/src/gnutls_cache.c b/src/gnutls_cache.c
index 86b843e..83e7bb5 100644
--- a/src/gnutls_cache.c
+++ b/src/gnutls_cache.c
@@ -34,18 +34,16 @@
34 34
35 35
36#define MC_TAG "mod_gnutls:" 36#define MC_TAG "mod_gnutls:"
37#define MC_TAG_LEN \ 37#define MC_TAG_LEN sizeof(MC_TAG)
38 (sizeof(MC_TAG))
39#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN) 38#define STR_SESSION_LEN (GNUTLS_SESSION_ID_STRING_LEN + MC_TAG_LEN)
40 39
41#if 0 40char *mgs_session_id2sz(unsigned char *id, int idlen,
42static char *gnutls_session_id2sz(unsigned char *id, int idlen,
43 char *str, int strsize) 41 char *str, int strsize)
44{ 42{
45 char *cp; 43 char *cp;
46 int n; 44 int n;
47 45
48 cp = apr_cpystrn(str, MC_TAG, MC_TAG_LEN); 46 cp = str;
49 for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) { 47 for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) {
50 apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]); 48 apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]);
51 cp += 2; 49 cp += 2;
@@ -53,7 +51,27 @@ static char *gnutls_session_id2sz(unsigned char *id, int idlen,
53 *cp = '\0'; 51 *cp = '\0';
54 return str; 52 return str;
55} 53}
56#endif 54
55
56/* Name the Session ID as:
57 * server:port.SessionID
58 * to disallow resuming sessions on different servers
59 */
60static int mgs_session_id2dbm(conn_rec* c, unsigned char *id, int idlen,
61 apr_datum_t* dbmkey)
62{
63char buf[STR_SESSION_LEN];
64char *sz;
65
66 sz = mgs_session_id2sz(id, idlen, buf, sizeof(buf));
67 if (sz == NULL)
68 return -1;
69
70 dbmkey->dptr = apr_psprintf(c->pool, "%s:%d.%s", c->base_server->server_hostname, c->base_server->port, sz);
71 dbmkey->dsize = strlen( dbmkey->dptr);
72
73 return 0;
74}
57 75
58#define CTIME "%b %d %k:%M:%S %Y %Z" 76#define CTIME "%b %d %k:%M:%S %Y %Z"
59char *mgs_time2sz(time_t in_time, char *str, int strsize) 77char *mgs_time2sz(time_t in_time, char *str, int strsize)
@@ -70,24 +88,23 @@ char *mgs_time2sz(time_t in_time, char *str, int strsize)
70 return str; 88 return str;
71} 89}
72 90
73char *mgs_session_id2sz(unsigned char *id, int idlen, 91#if HAVE_APR_MEMCACHE
74 char *str, int strsize) 92/* Name the Session ID as:
93 * server:port.SessionID
94 * to disallow resuming sessions on different servers
95 */
96static char* mgs_session_id2mc(conn_rec* c, unsigned char *id, int idlen)
75{ 97{
76 char *cp; 98char buf[STR_SESSION_LEN];
77 int n; 99char *sz;
78 100
79 cp = str; 101 sz = mgs_session_id2sz(id, idlen, buf, sizeof(buf));
80 for (n = 0; n < idlen && n < GNUTLS_MAX_SESSION_ID; n++) { 102 if (sz == NULL)
81 apr_snprintf(cp, strsize - (cp-str), "%02X", id[n]); 103 return NULL;
82 cp += 2; 104
83 } 105 return apr_psprintf(c->pool, MC_TAG"%s:%d.%s", c->base_server->server_hostname, c->base_server->port, sz);
84 *cp = '\0';
85 return str;
86} 106}
87 107
88
89#if HAVE_APR_MEMCACHE
90
91/** 108/**
92 * GnuTLS Session Cache using libmemcached 109 * GnuTLS Session Cache using libmemcached
93 * 110 *
@@ -184,11 +201,10 @@ static int mc_cache_store(void* baton, gnutls_datum_t key,
184{ 201{
185 apr_status_t rv = APR_SUCCESS; 202 apr_status_t rv = APR_SUCCESS;
186 mgs_handle_t *ctxt = baton; 203 mgs_handle_t *ctxt = baton;
187 char buf[STR_SESSION_LEN];
188 char* strkey = NULL; 204 char* strkey = NULL;
189 apr_uint32_t timeout; 205 apr_uint32_t timeout;
190 206
191 strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); 207 strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
192 if(!strkey) 208 if(!strkey)
193 return -1; 209 return -1;
194 210
@@ -211,13 +227,12 @@ static gnutls_datum_t mc_cache_fetch(void* baton, gnutls_datum_t key)
211{ 227{
212 apr_status_t rv = APR_SUCCESS; 228 apr_status_t rv = APR_SUCCESS;
213 mgs_handle_t *ctxt = baton; 229 mgs_handle_t *ctxt = baton;
214 char buf[STR_SESSION_LEN];
215 char* strkey = NULL; 230 char* strkey = NULL;
216 char* value; 231 char* value;
217 apr_size_t value_len; 232 apr_size_t value_len;
218 gnutls_datum_t data = { NULL, 0 }; 233 gnutls_datum_t data = { NULL, 0 };
219 234
220 strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); 235 strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
221 if (!strkey) { 236 if (!strkey) {
222 return data; 237 return data;
223 } 238 }
@@ -252,10 +267,9 @@ static int mc_cache_delete(void* baton, gnutls_datum_t key)
252{ 267{
253 apr_status_t rv = APR_SUCCESS; 268 apr_status_t rv = APR_SUCCESS;
254 mgs_handle_t *ctxt = baton; 269 mgs_handle_t *ctxt = baton;
255 char buf[STR_SESSION_LEN];
256 char* strkey = NULL; 270 char* strkey = NULL;
257 271
258 strkey = gnutls_session_id2sz(key.data, key.size, buf, sizeof(buf)); 272 strkey = mgs_session_id2mc(ctxt->c, key.data, key.size);
259 if(!strkey) 273 if(!strkey)
260 return -1; 274 return -1;
261 275
@@ -366,8 +380,8 @@ static gnutls_datum_t dbm_cache_fetch(void* baton, gnutls_datum_t key)
366 mgs_handle_t *ctxt = baton; 380 mgs_handle_t *ctxt = baton;
367 apr_status_t rv; 381 apr_status_t rv;
368 382
369 dbmkey.dptr = (void*)key.data; 383 if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
370 dbmkey.dsize = key.size; 384 return data;
371 385
372 rv = apr_dbm_open(&dbm, ctxt->sc->cache_config, 386 rv = apr_dbm_open(&dbm, ctxt->sc->cache_config,
373 APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool); 387 APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool);
@@ -413,9 +427,9 @@ static int dbm_cache_store(void* baton, gnutls_datum_t key,
413 mgs_handle_t *ctxt = baton; 427 mgs_handle_t *ctxt = baton;
414 apr_status_t rv; 428 apr_status_t rv;
415 apr_time_t expiry; 429 apr_time_t expiry;
416 430
417 dbmkey.dptr = (char *)key.data; 431 if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
418 dbmkey.dsize = key.size; 432 return -1;
419 433
420 /* create DBM value */ 434 /* create DBM value */
421 dbmval.dsize = data.size + sizeof(apr_time_t); 435 dbmval.dsize = data.size + sizeof(apr_time_t);
@@ -467,9 +481,9 @@ static int dbm_cache_delete(void* baton, gnutls_datum_t key)
467 apr_datum_t dbmkey; 481 apr_datum_t dbmkey;
468 mgs_handle_t *ctxt = baton; 482 mgs_handle_t *ctxt = baton;
469 apr_status_t rv; 483 apr_status_t rv;
470 484
471 dbmkey.dptr = (char *)key.data; 485 if (mgs_session_id2dbm(ctxt->c, key.data, key.size, &dbmkey) < 0)
472 dbmkey.dsize = key.size; 486 return -1;
473 487
474 rv = apr_dbm_open(&dbm, ctxt->sc->cache_config, 488 rv = apr_dbm_open(&dbm, ctxt->sc->cache_config,
475 APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool); 489 APR_DBM_RWCREATE, SSL_DBM_FILE_MODE, ctxt->c->pool);
diff --git a/src/gnutls_config.c b/src/gnutls_config.c
index 7b5a42b..f08512e 100644
--- a/src/gnutls_config.c
+++ b/src/gnutls_config.c
@@ -1,5 +1,6 @@
1/** 1/**
2 * Copyright 2004-2005 Paul Querna 2 * Copyright 2004-2005 Paul Querna
3 * Copyright 2007 Nikos Mavrogiannopoulos
3 * 4 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License. 6 * you may not use this file except in compliance with the License.
@@ -151,15 +152,10 @@ const char *mgs_set_cert_file(cmd_parms * parms, void *dummy,
151 "Certificate '%s'", file); 152 "Certificate '%s'", file);
152 } 153 }
153 154
154 ret = gnutls_x509_crt_init(&sc->cert_x509); 155 sc->certs_x509_num = MAX_CHAIN_SIZE;
155 if (ret < 0) {
156 return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize"
157 ": (%d) %s", ret, gnutls_strerror(ret));
158 }
159
160 ret = 156 ret =
161 gnutls_x509_crt_import(sc->cert_x509, &data, GNUTLS_X509_FMT_PEM); 157 gnutls_x509_crt_list_import(sc->certs_x509, &sc->certs_x509_num, &data, GNUTLS_X509_FMT_PEM, 0);
162 if (ret != 0) { 158 if (ret < 0) {
163 return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " 159 return apr_psprintf(parms->pool, "GnuTLS: Failed to Import "
164 "Certificate '%s': (%d) %s", file, ret, 160 "Certificate '%s': (%d) %s", file, ret,
165 gnutls_strerror(ret)); 161 gnutls_strerror(ret));
@@ -207,6 +203,84 @@ const char *mgs_set_key_file(cmd_parms * parms, void *dummy,
207 return NULL; 203 return NULL;
208} 204}
209 205
206const char *mgs_set_pgpcert_file(cmd_parms * parms, void *dummy,
207 const char *arg)
208{
209 int ret;
210 gnutls_datum_t data;
211 const char *file;
212 apr_pool_t *spool;
213 mgs_srvconf_rec *sc =
214 (mgs_srvconf_rec *) ap_get_module_config(parms->server->
215 module_config,
216 &gnutls_module);
217 apr_pool_create(&spool, parms->pool);
218
219 file = ap_server_root_relative(spool, arg);
220
221 if (load_datum_from_file(spool, file, &data) != 0) {
222 return apr_psprintf(parms->pool, "GnuTLS: Error Reading "
223 "Certificate '%s'", file);
224 }
225
226 ret = gnutls_openpgp_crt_init( &sc->cert_pgp);
227 if (ret < 0) {
228 return apr_psprintf(parms->pool, "GnuTLS: Failed to Init "
229 "PGP Certificate: (%d) %s", ret,
230 gnutls_strerror(ret));
231 }
232
233 ret =
234 gnutls_openpgp_crt_import(sc->cert_pgp, &data, GNUTLS_OPENPGP_FMT_BASE64);
235 if (ret < 0) {
236 return apr_psprintf(parms->pool, "GnuTLS: Failed to Import "
237 "PGP Certificate '%s': (%d) %s", file, ret,
238 gnutls_strerror(ret));
239 }
240
241 apr_pool_destroy(spool);
242 return NULL;
243}
244
245const char *mgs_set_pgpkey_file(cmd_parms * parms, void *dummy,
246 const char *arg)
247{
248 int ret;
249 gnutls_datum_t data;
250 const char *file;
251 apr_pool_t *spool;
252 mgs_srvconf_rec *sc =
253 (mgs_srvconf_rec *) ap_get_module_config(parms->server->
254 module_config,
255 &gnutls_module);
256 apr_pool_create(&spool, parms->pool);
257
258 file = ap_server_root_relative(spool, arg);
259
260 if (load_datum_from_file(spool, file, &data) != 0) {
261 return apr_psprintf(parms->pool, "GnuTLS: Error Reading "
262 "Private Key '%s'", file);
263 }
264
265 ret = gnutls_openpgp_privkey_init(&sc->privkey_pgp);
266 if (ret < 0) {
267 return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize"
268 ": (%d) %s", ret, gnutls_strerror(ret));
269 }
270
271 ret =
272 gnutls_openpgp_privkey_import(sc->privkey_pgp, &data,
273 GNUTLS_OPENPGP_FMT_BASE64, NULL, 0);
274 if (ret != 0) {
275 return apr_psprintf(parms->pool, "GnuTLS: Failed to Import "
276 "PGP Private Key '%s': (%d) %s", file, ret,
277 gnutls_strerror(ret));
278 }
279 apr_pool_destroy(spool);
280 return NULL;
281}
282
283
210const char *mgs_set_srp_tpasswd_file(cmd_parms * parms, void *dummy, 284const char *mgs_set_srp_tpasswd_file(cmd_parms * parms, void *dummy,
211 const char *arg) 285 const char *arg)
212{ 286{
@@ -355,6 +429,44 @@ const char *mgs_set_client_ca_file(cmd_parms * parms, void *dummy,
355 return NULL; 429 return NULL;
356} 430}
357 431
432const char *mgs_set_keyring_file(cmd_parms * parms, void *dummy,
433 const char *arg)
434{
435 int rv;
436 const char *file;
437 apr_pool_t *spool;
438 gnutls_datum_t data;
439
440 mgs_srvconf_rec *sc =
441 (mgs_srvconf_rec *) ap_get_module_config(parms->server->
442 module_config,
443 &gnutls_module);
444 apr_pool_create(&spool, parms->pool);
445
446 file = ap_server_root_relative(spool, arg);
447
448 if (load_datum_from_file(spool, file, &data) != 0) {
449 return apr_psprintf(parms->pool, "GnuTLS: Error Reading "
450 "Keyring File '%s'", file);
451 }
452
453 rv = gnutls_openpgp_keyring_init(&sc->pgp_list);
454 if (rv < 0) {
455 return apr_psprintf(parms->pool, "GnuTLS: Failed to initialize"
456 "keyring: (%d) %s", rv, gnutls_strerror(rv));
457 }
458
459 rv = gnutls_openpgp_keyring_import(sc->pgp_list, &data, GNUTLS_OPENPGP_FMT_BASE64);
460 if (rv < 0) {
461 return apr_psprintf(parms->pool, "GnuTLS: Failed to load "
462 "Keyring File '%s': (%d) %s", file, rv,
463 gnutls_strerror(rv));
464 }
465
466 apr_pool_destroy(spool);
467 return NULL;
468}
469
358const char *mgs_set_enabled(cmd_parms * parms, void *dummy, 470const char *mgs_set_enabled(cmd_parms * parms, void *dummy,
359 const char *arg) 471 const char *arg)
360{ 472{
@@ -440,7 +552,8 @@ void *mgs_config_server_create(apr_pool_t * p, server_rec * s)
440 sc->srp_tpasswd_conf_file = NULL; 552 sc->srp_tpasswd_conf_file = NULL;
441 sc->srp_tpasswd_file = NULL; 553 sc->srp_tpasswd_file = NULL;
442 sc->privkey_x509 = NULL; 554 sc->privkey_x509 = NULL;
443 sc->cert_x509 = NULL; 555 memset( sc->certs_x509, 0, sizeof(sc->certs_x509));
556 sc->certs_x509_num = 0;
444 sc->cache_timeout = apr_time_from_sec(300); 557 sc->cache_timeout = apr_time_from_sec(300);
445 sc->cache_type = mgs_cache_dbm; 558 sc->cache_type = mgs_cache_dbm;
446 sc->cache_config = ap_server_root_relative(p, "conf/gnutls_cache"); 559 sc->cache_config = ap_server_root_relative(p, "conf/gnutls_cache");
diff --git a/src/gnutls_hooks.c b/src/gnutls_hooks.c
index 4364add..26917b8 100644
--- a/src/gnutls_hooks.c
+++ b/src/gnutls_hooks.c
@@ -36,7 +36,10 @@ static int mpm_is_threaded;
36 36
37static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt); 37static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt);
38/* use side==0 for server and side==1 for client */ 38/* use side==0 for server and side==1 for client */
39static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, 39static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert,
40 int side,
41 int export_certificates_enabled);
42static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert,
40 int side, 43 int side,
41 int export_certificates_enabled); 44 int export_certificates_enabled);
42 45
@@ -68,9 +71,23 @@ int ret;
68 mpm_is_threaded = 0; 71 mpm_is_threaded = 0;
69#endif 72#endif
70 73
74 if (gnutls_check_version(LIBGNUTLS_VERSION)==NULL) {
75 fprintf(stderr, "gnutls_check_version() failed. Required: gnutls-%s Found: gnutls-%s\n",
76 LIBGNUTLS_VERSION, gnutls_check_version(NULL));
77 return -3;
78 }
79
71 ret = gnutls_global_init(); 80 ret = gnutls_global_init();
72 if (ret < 0) /* FIXME: can we print here? */ 81 if (ret < 0) {
73 exit(ret); 82 fprintf(stderr, "gnutls_global_init: %s\n", gnutls_strerror(ret));
83 return -3;
84 }
85
86 ret = gnutls_global_init_extra();
87 if (ret < 0) {
88 fprintf(stderr, "gnutls_global_init_extra: %s\n", gnutls_strerror(ret));
89 return -3;
90 }
74 91
75 apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config, 92 apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config,
76 apr_pool_cleanup_null); 93 apr_pool_cleanup_null);
@@ -82,19 +99,18 @@ int ret;
82 99
83 gnutls_global_set_log_level(9); 100 gnutls_global_set_log_level(9);
84 gnutls_global_set_log_function(gnutls_debug_log_all); 101 gnutls_global_set_log_function(gnutls_debug_log_all);
102 apr_file_printf(debug_log_fp, "gnutls: %s\n", gnutls_check_version(NULL));
85#endif 103#endif
86 104
87 return OK; 105 return OK;
88} 106}
89 107
90/* We don't support openpgp certificates, yet */
91const static int cert_type_prio[2] = { GNUTLS_CRT_X509, 0 };
92
93static int mgs_select_virtual_server_cb(gnutls_session_t session) 108static int mgs_select_virtual_server_cb(gnutls_session_t session)
94{ 109{
95 mgs_handle_t *ctxt; 110 mgs_handle_t *ctxt;
96 mgs_srvconf_rec *tsc; 111 mgs_srvconf_rec *tsc;
97 int ret; 112 int ret;
113 int cprio[2];
98 114
99 ctxt = gnutls_transport_get_ptr(session); 115 ctxt = gnutls_transport_get_ptr(session);
100 116
@@ -126,17 +142,22 @@ static int mgs_select_virtual_server_cb(gnutls_session_t session)
126 * negotiation. 142 * negotiation.
127 */ 143 */
128 ret = gnutls_priority_set(session, ctxt->sc->priorities); 144 ret = gnutls_priority_set(session, ctxt->sc->priorities);
129 gnutls_certificate_type_set_priority(session, cert_type_prio);
130
131
132 /* actually it shouldn't fail since we have checked at startup */ 145 /* actually it shouldn't fail since we have checked at startup */
133 if (ret < 0) 146 if (ret < 0)
134 return ret; 147 return ret;
135 148
136 /* allow separate caches per virtual host. Actually allowing the same is a 149 /* If both certificate types are not present disallow them from
137 * bad idea, since they might have different security requirements. 150 * being negotiated.
138 */ 151 */
139 mgs_cache_session_init(ctxt); 152 if (ctxt->sc->certs_x509[0] != NULL && ctxt->sc->cert_pgp == NULL) {
153 cprio[0] = GNUTLS_CRT_X509;
154 cprio[1] = 0;
155 gnutls_certificate_type_set_priority( session, cprio);
156 } else if (ctxt->sc->cert_pgp != NULL && ctxt->sc->certs_x509[0]==NULL) {
157 cprio[0] = GNUTLS_CRT_OPENPGP;
158 cprio[1] = 0;
159 gnutls_certificate_type_set_priority( session, cprio);
160 }
140 161
141 return 0; 162 return 0;
142} 163}
@@ -147,15 +168,31 @@ static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st * ret)
147 168
148 ctxt = gnutls_transport_get_ptr(session); 169 ctxt = gnutls_transport_get_ptr(session);
149 170
150 ret->type = GNUTLS_CRT_X509; 171 if (gnutls_certificate_type_get( session) == GNUTLS_CRT_X509) {
151 ret->ncerts = 1; 172 ret->type = GNUTLS_CRT_X509;
152 ret->deinit_all = 0; 173 ret->ncerts = ctxt->sc->certs_x509_num;
174 ret->deinit_all = 0;
175
176 ret->cert.x509 = ctxt->sc->certs_x509;
177 ret->key.x509 = ctxt->sc->privkey_x509;
178
179 return 0;
180 } else if (gnutls_certificate_type_get( session) == GNUTLS_CRT_OPENPGP) {
181 ret->type = GNUTLS_CRT_OPENPGP;
182 ret->ncerts = 1;
183 ret->deinit_all = 0;
184
185 ret->cert.pgp = ctxt->sc->cert_pgp;
186 ret->key.pgp = ctxt->sc->privkey_pgp;
187
188 return 0;
189
190 }
153 191
154 ret->cert.x509 = &ctxt->sc->cert_x509; 192 return GNUTLS_E_INTERNAL_ERROR;
155 ret->key.x509 = ctxt->sc->privkey_x509;
156 return 0;
157} 193}
158 194
195/* 2048-bit group parameters from SRP specification */
159const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n" 196const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n"
160 "MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n" 197 "MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n"
161 "gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n" 198 "gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n"
@@ -171,7 +208,7 @@ const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n"
171 * Returns negative on error. 208 * Returns negative on error.
172 */ 209 */
173static int read_crt_cn(server_rec * s, apr_pool_t * p, 210static int read_crt_cn(server_rec * s, apr_pool_t * p,
174 gnutls_x509_crt cert, char **cert_cn) 211 gnutls_x509_crt_t cert, char **cert_cn)
175{ 212{
176 int rv = 0, i; 213 int rv = 0, i;
177 size_t data_len; 214 size_t data_len;
@@ -179,6 +216,7 @@ static int read_crt_cn(server_rec * s, apr_pool_t * p,
179 216
180 *cert_cn = NULL; 217 *cert_cn = NULL;
181 218
219 data_len = 0;
182 rv = gnutls_x509_crt_get_dn_by_oid(cert, 220 rv = gnutls_x509_crt_get_dn_by_oid(cert,
183 GNUTLS_OID_X520_COMMON_NAME, 221 GNUTLS_OID_X520_COMMON_NAME,
184 0, 0, NULL, &data_len); 222 0, 0, NULL, &data_len);
@@ -189,8 +227,8 @@ static int read_crt_cn(server_rec * s, apr_pool_t * p,
189 GNUTLS_OID_X520_COMMON_NAME, 0, 227 GNUTLS_OID_X520_COMMON_NAME, 0,
190 0, *cert_cn, &data_len); 228 0, *cert_cn, &data_len);
191 } else { /* No CN return subject alternative name */ 229 } else { /* No CN return subject alternative name */
192 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, 230 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
193 "No common name found in certificate for '%s:%d'. Looking for subject alternative name.", 231 "No common name found in certificate for '%s:%d'. Looking for subject alternative name...",
194 s->server_hostname, s->port); 232 s->server_hostname, s->port);
195 rv = 0; 233 rv = 0;
196 /* read subject alternative name */ 234 /* read subject alternative name */
@@ -218,9 +256,33 @@ static int read_crt_cn(server_rec * s, apr_pool_t * p,
218 } 256 }
219 257
220 return rv; 258 return rv;
259}
260
261static int read_pgpcrt_cn(server_rec * s, apr_pool_t * p,
262 gnutls_openpgp_crt_t cert, char **cert_cn)
263{
264 int rv = 0;
265 size_t data_len;
266
267
268 *cert_cn = NULL;
269
270 data_len = 0;
271 rv = gnutls_openpgp_crt_get_name(cert, 0, NULL, &data_len);
272
273 if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
274 *cert_cn = apr_palloc(p, data_len);
275 rv = gnutls_openpgp_crt_get_name(cert, 0, *cert_cn, &data_len);
276 } else { /* No CN return subject alternative name */
277 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
278 "No name found in PGP certificate for '%s:%d'.",
279 s->server_hostname, s->port);
280 }
221 281
282 return rv;
222} 283}
223 284
285
224int 286int
225mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, 287mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
226 apr_pool_t * ptemp, server_rec * base_server) 288 apr_pool_t * ptemp, server_rec * base_server)
@@ -334,7 +396,7 @@ mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
334 } 396 }
335 } 397 }
336 398
337 if (sc->cert_x509 == NULL 399 if (sc->certs_x509[0] == NULL
338 && sc->enabled == GNUTLS_ENABLED_TRUE) { 400 && sc->enabled == GNUTLS_ENABLED_TRUE) {
339 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, 401 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
340 "[GnuTLS] - Host '%s:%d' is missing a " 402 "[GnuTLS] - Host '%s:%d' is missing a "
@@ -353,7 +415,10 @@ mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
353 } 415 }
354 416
355 if (sc->enabled == GNUTLS_ENABLED_TRUE) { 417 if (sc->enabled == GNUTLS_ENABLED_TRUE) {
356 rv = read_crt_cn(s, p, sc->cert_x509, &sc->cert_cn); 418 rv = read_crt_cn(s, p, sc->certs_x509[0], &sc->cert_cn);
419 if (rv < 0 && sc->cert_pgp != NULL) /* try openpgp certificate */
420 rv = read_pgpcrt_cn(s, p, sc->cert_pgp, &sc->cert_cn);
421
357 if (rv < 0) { 422 if (rv < 0) {
358 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, 423 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
359 "[GnuTLS] - Cannot find a certificate for host '%s:%d'!", 424 "[GnuTLS] - Cannot find a certificate for host '%s:%d'!",
@@ -482,15 +547,6 @@ mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
482 547
483 ctxt = gnutls_transport_get_ptr(session); 548 ctxt = gnutls_transport_get_ptr(session);
484 549
485 sni_type = gnutls_certificate_type_get(session);
486 if (sni_type != GNUTLS_CRT_X509) {
487 /* In theory, we could support OpenPGP Certificates. Theory != code. */
488 ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
489 ctxt->c->base_server,
490 "GnuTLS: Only x509 Certificates are currently supported.");
491 return NULL;
492 }
493
494 rv = gnutls_server_name_get(ctxt->session, sni_name, 550 rv = gnutls_server_name_get(ctxt->session, sni_name,
495 &data_len, &sni_type, 0); 551 &data_len, &sni_type, 0);
496 552
@@ -591,6 +647,8 @@ static mgs_handle_t *create_gnutls_handle(apr_pool_t * pool, conn_rec * c)
591 gnutls_handshake_set_post_client_hello_function(ctxt->session, 647 gnutls_handshake_set_post_client_hello_function(ctxt->session,
592 mgs_select_virtual_server_cb); 648 mgs_select_virtual_server_cb);
593 649
650 mgs_cache_session_init(ctxt);
651
594 return ctxt; 652 return ctxt;
595} 653}
596 654
@@ -686,7 +744,11 @@ int mgs_hook_fixups(request_rec * r)
686 tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf)); 744 tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
687 apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp)); 745 apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
688 746
689 mgs_add_common_cert_vars(r, ctxt->sc->cert_x509, 0, 747 if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509)
748 mgs_add_common_cert_vars(r, ctxt->sc->certs_x509[0], 0,
749 ctxt->sc->export_certificates_enabled);
750 else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP)
751 mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_pgp, 0,
690 ctxt->sc->export_certificates_enabled); 752 ctxt->sc->export_certificates_enabled);
691 753
692 return rv; 754 return rv;
@@ -748,7 +810,7 @@ int mgs_hook_authz(request_rec * r)
748 */ 810 */
749#define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT") 811#define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT")
750static void 812static void
751mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side, 813mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side,
752 int export_certificates_enabled) 814 int export_certificates_enabled)
753{ 815{
754 unsigned char sbuf[64]; /* buffer to hold serials */ 816 unsigned char sbuf[64]; /* buffer to hold serials */
@@ -795,7 +857,7 @@ mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side,
795 apr_psprintf(r->pool, "%u", ret)); 857 apr_psprintf(r->pool, "%u", ret));
796 858
797 apr_table_setn(env, 859 apr_table_setn(env,
798 apr_pstrcat(r->pool, MGS_SIDE, "_S_TYPE", NULL), "X.509"); 860 apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL), "X.509");
799 861
800 tmp = 862 tmp =
801 mgs_time2sz(gnutls_x509_crt_get_expiration_time 863 mgs_time2sz(gnutls_x509_crt_get_expiration_time
@@ -837,34 +899,99 @@ mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side,
837 899
838 if (ret == GNUTLS_SAN_DNSNAME) { 900 if (ret == GNUTLS_SAN_DNSNAME) {
839 apr_table_setn(env, 901 apr_table_setn(env,
840 apr_psprintf(r->pool, "%s_S_SAN%u", MGS_SIDE, i), 902 apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i),
841 apr_psprintf(r->pool, "DNSNAME:%s", tmp2)); 903 apr_psprintf(r->pool, "DNSNAME:%s", tmp2));
842 } else if (ret == GNUTLS_SAN_RFC822NAME) { 904 } else if (ret == GNUTLS_SAN_RFC822NAME) {
843 apr_table_setn(env, 905 apr_table_setn(env,
844 apr_psprintf(r->pool, "%s_S_SAN%u", MGS_SIDE, i), 906 apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i),
845 apr_psprintf(r->pool, "RFC822NAME:%s", tmp2)); 907 apr_psprintf(r->pool, "RFC822NAME:%s", tmp2));
846 } else if (ret == GNUTLS_SAN_URI) { 908 } else if (ret == GNUTLS_SAN_URI) {
847 apr_table_setn(env, 909 apr_table_setn(env,
848 apr_psprintf(r->pool, "%s_S_SAN%u", MGS_SIDE, i), 910 apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i),
849 apr_psprintf(r->pool, "URI:%s", tmp2)); 911 apr_psprintf(r->pool, "URI:%s", tmp2));
850 } else { 912 } else {
851 apr_table_setn(env, 913 apr_table_setn(env,
852 apr_psprintf(r->pool, "%s_S_SAN%u", MGS_SIDE, i), 914 apr_psprintf(r->pool, "%s_S_AN%u", MGS_SIDE, i),
853 "UNSUPPORTED"); 915 "UNSUPPORTED");
854 } 916 }
855 } 917 }
856 } 918 }
919}
857 920
921static void
922mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side,
923 int export_certificates_enabled)
924{
925 unsigned char sbuf[64]; /* buffer to hold serials */
926 char buf[AP_IOBUFSIZE];
927 const char *tmp;
928 size_t len;
929 int ret;
858 930
859} 931 apr_table_t *env = r->subprocess_env;
860 932
933 if (export_certificates_enabled != 0) {
934 char cert_buf[10 * 1024];
935 len = sizeof(cert_buf);
936
937 if (gnutls_openpgp_crt_export
938 (cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0)
939 apr_table_setn(env,
940 apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL),
941 apr_pstrmemdup(r->pool, cert_buf, len));
942
943 }
944
945 len = sizeof(buf);
946 gnutls_openpgp_crt_get_name(cert, 0, buf, &len);
947 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_NAME", NULL),
948 apr_pstrmemdup(r->pool, buf, len));
949
950 len = sizeof(sbuf);
951 gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len);
952 tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
953 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_FINGERPRINT", NULL),
954 apr_pstrdup(r->pool, tmp));
955
956 ret = gnutls_openpgp_crt_get_version(cert);
957 if (ret > 0)
958 apr_table_setn(env,
959 apr_pstrcat(r->pool, MGS_SIDE, "_M_VERSION", NULL),
960 apr_psprintf(r->pool, "%u", ret));
961
962 apr_table_setn(env,
963 apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL), "OPENPGP");
964
965 tmp =
966 mgs_time2sz(gnutls_openpgp_crt_get_expiration_time
967 (cert), buf, sizeof(buf));
968 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL),
969 apr_pstrdup(r->pool, tmp));
970
971 tmp =
972 mgs_time2sz(gnutls_openpgp_crt_get_creation_time
973 (cert), buf, sizeof(buf));
974 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL),
975 apr_pstrdup(r->pool, tmp));
976
977 ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL);
978 if (ret >= 0) {
979 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY", NULL),
980 gnutls_pk_algorithm_get_name(ret));
981 }
982
983}
861 984
985/* TODO: Allow client sending a X.509 certificate chain */
862static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) 986static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt)
863{ 987{
864 const gnutls_datum_t *cert_list; 988 const gnutls_datum_t *cert_list;
865 unsigned int cert_list_size, status, expired; 989 unsigned int cert_list_size, status, expired;
866 int rv, ret; 990 int rv, ret;
867 gnutls_x509_crt_t cert; 991 union {
992 gnutls_x509_crt_t x509;
993 gnutls_openpgp_crt_t pgp;
994 } cert;
868 apr_time_t activation_time, expiration_time, cur_time; 995 apr_time_t activation_time, expiration_time, cur_time;
869 996
870 cert_list = 997 cert_list =
@@ -890,32 +1017,56 @@ static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt)
890 return HTTP_FORBIDDEN; 1017 return HTTP_FORBIDDEN;
891 } 1018 }
892 1019
893 gnutls_x509_crt_init(&cert); 1020 if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509) {
894 rv = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER); 1021 gnutls_x509_crt_init(&cert.x509);
1022 rv = gnutls_x509_crt_import(cert.x509, &cert_list[0], GNUTLS_X509_FMT_DER);
1023 } else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP) {
1024 gnutls_openpgp_crt_init(&cert.pgp);
1025 rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0], GNUTLS_OPENPGP_FMT_RAW);
1026 } else return HTTP_FORBIDDEN;
1027
895 if (rv < 0) { 1028 if (rv < 0) {
896 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 1029 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
897 "GnuTLS: Failed to Verify Peer: " 1030 "GnuTLS: Failed to Verify Peer: "
898 "Failed to import peer certificates."); 1031 "Failed to import peer certificates.");
899 ret = HTTP_FORBIDDEN; 1032 ret = HTTP_FORBIDDEN;
900 goto exit; 1033 goto exit;
901 } 1034 }
902 1035
903 apr_time_ansi_put(&expiration_time, 1036 if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509) {
904 gnutls_x509_crt_get_expiration_time(cert)); 1037 apr_time_ansi_put(&expiration_time,
905 apr_time_ansi_put(&activation_time, 1038 gnutls_x509_crt_get_expiration_time(cert.x509));
906 gnutls_x509_crt_get_activation_time(cert)); 1039 apr_time_ansi_put(&activation_time,
1040 gnutls_x509_crt_get_activation_time(cert.x509));
907 1041
908 rv = gnutls_x509_crt_verify(cert, ctxt->sc->ca_list, 1042 rv = gnutls_x509_crt_verify(cert.x509, ctxt->sc->ca_list,
909 ctxt->sc->ca_list_size, 0, &status); 1043 ctxt->sc->ca_list_size, 0, &status);
1044 } else {
1045 apr_time_ansi_put(&expiration_time,
1046 gnutls_openpgp_crt_get_expiration_time(cert.pgp));
1047 apr_time_ansi_put(&activation_time,
1048 gnutls_openpgp_crt_get_creation_time(cert.pgp));
1049
1050 rv = gnutls_openpgp_crt_verify_ring(cert.pgp, ctxt->sc->pgp_list,
1051 0, &status);
1052 }
910 1053
911 if (rv < 0) { 1054 if (rv < 0) {
912 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 1055 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
913 "GnuTLS: Failed to Verify Peer certificate: (%d) %s", 1056 "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
914 rv, gnutls_strerror(rv)); 1057 rv, gnutls_strerror(rv));
1058 if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1059 ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1060 "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?");
915 ret = HTTP_FORBIDDEN; 1061 ret = HTTP_FORBIDDEN;
916 goto exit; 1062 goto exit;
917 } 1063 }
918 1064
1065 /* TODO: X509 CRL Verification. */
1066 /* May add later if anyone needs it.
1067 */
1068 /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1069
919 expired = 0; 1070 expired = 0;
920 cur_time = apr_time_now(); 1071 cur_time = apr_time_now();
921 if (activation_time > cur_time) { 1072 if (activation_time > cur_time) {
@@ -950,16 +1101,11 @@ static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt)
950 "GnuTLS: Peer Certificate is revoked."); 1101 "GnuTLS: Peer Certificate is revoked.");
951 } 1102 }
952 1103
953 /* TODO: Further Verification. */ 1104 if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509)
954 /* Revocation is X.509 non workable paradigm, I really doubt implementation 1105 mgs_add_common_cert_vars(r, cert.x509, 1,
955 * is worth doing --nmav 1106 ctxt->sc->export_certificates_enabled);
956 */ 1107 else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP)
957/// ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); 1108 mgs_add_common_pgpcert_vars(r, cert.pgp, 1,
958
959// mgs_hook_fixups(r);
960// rv = mgs_authz_lua(r);
961
962 mgs_add_common_cert_vars(r, cert, 1,
963 ctxt->sc->export_certificates_enabled); 1109 ctxt->sc->export_certificates_enabled);
964 1110
965 { 1111 {
@@ -983,7 +1129,10 @@ static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt)
983 } 1129 }
984 1130
985 exit: 1131 exit:
986 gnutls_x509_crt_deinit(cert); 1132 if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_X509)
1133 gnutls_x509_crt_deinit(cert.x509);
1134 else if (gnutls_certificate_type_get( ctxt->session) == GNUTLS_CRT_OPENPGP)
1135 gnutls_openpgp_crt_deinit(cert.pgp);
987 return ret; 1136 return ret;
988 1137
989 1138
diff --git a/src/mod_gnutls.c b/src/mod_gnutls.c
index a6e5528..014bfc8 100644
--- a/src/mod_gnutls.c
+++ b/src/mod_gnutls.c
@@ -64,6 +64,14 @@ static const command_rec mgs_config_cmds[] = {
64 NULL, 64 NULL,
65 RSRC_CONF, 65 RSRC_CONF,
66 "Set the CA File to verify Client Certificates"), 66 "Set the CA File to verify Client Certificates"),
67 AP_INIT_TAKE1("GnuTLSX509CAFile", mgs_set_client_ca_file,
68 NULL,
69 RSRC_CONF,
70 "Set the CA File to verify Client Certificates"),
71 AP_INIT_TAKE1("GnuTLSPGPKeyringFile", mgs_set_keyring_file,
72 NULL,
73 RSRC_CONF,
74 "Set the Keyring File to verify Client Certificates"),
67 AP_INIT_TAKE1("GnuTLSDHFile", mgs_set_dh_file, 75 AP_INIT_TAKE1("GnuTLSDHFile", mgs_set_dh_file,
68 NULL, 76 NULL,
69 RSRC_CONF, 77 RSRC_CONF,
@@ -75,11 +83,27 @@ static const command_rec mgs_config_cmds[] = {
75 AP_INIT_TAKE1("GnuTLSCertificateFile", mgs_set_cert_file, 83 AP_INIT_TAKE1("GnuTLSCertificateFile", mgs_set_cert_file,
76 NULL, 84 NULL,
77 RSRC_CONF, 85 RSRC_CONF,
78 "SSL Server Key file"), 86 "SSL Server X509 Certificate file"),
79 AP_INIT_TAKE1("GnuTLSKeyFile", mgs_set_key_file, 87 AP_INIT_TAKE1("GnuTLSKeyFile", mgs_set_key_file,
80 NULL, 88 NULL,
81 RSRC_CONF, 89 RSRC_CONF,
82 "SSL Server SRP Password file"), 90 "SSL Server X509 Private Key file"),
91 AP_INIT_TAKE1("GnuTLSX509CertificateFile", mgs_set_cert_file,
92 NULL,
93 RSRC_CONF,
94 "SSL Server X509 Certificate file"),
95 AP_INIT_TAKE1("GnuTLSX509KeyFile", mgs_set_key_file,
96 NULL,
97 RSRC_CONF,
98 "SSL Server X509 Private Key file"),
99 AP_INIT_TAKE1("GnuTLSPGPCertificateFile", mgs_set_pgpcert_file,
100 NULL,
101 RSRC_CONF,
102 "SSL Server PGP Certificate file"),
103 AP_INIT_TAKE1("GnuTLSPGPKeyFile", mgs_set_pgpkey_file,
104 NULL,
105 RSRC_CONF,
106 "SSL Server PGP Private key file"),
83 AP_INIT_TAKE1("GnuTLSSRPPasswdFile", mgs_set_srp_tpasswd_file, 107 AP_INIT_TAKE1("GnuTLSSRPPasswdFile", mgs_set_srp_tpasswd_file,
84 NULL, 108 NULL,
85 RSRC_CONF, 109 RSRC_CONF,