diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | include/mod_gnutls.h.in | 17 | ||||
-rw-r--r-- | src/gnutls_io.c | 54 | ||||
-rw-r--r-- | src/mod_gnutls.c | 363 |
4 files changed, 362 insertions, 74 deletions
diff --git a/configure.ac b/configure.ac index b504eca..a16da41 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -1,4 +1,4 @@ | |||
1 | AC_INIT(mod_gnutls, 0.1.1) | 1 | AC_INIT(mod_gnutls, 0.2.0) |
2 | OOO_CONFIG_NICE(config.nice) | 2 | OOO_CONFIG_NICE(config.nice) |
3 | MOD_GNUTLS_VERSION=AC_PACKAGE_VERSION | 3 | MOD_GNUTLS_VERSION=AC_PACKAGE_VERSION |
4 | AC_PREREQ(2.53) | 4 | AC_PREREQ(2.53) |
diff --git a/include/mod_gnutls.h.in b/include/mod_gnutls.h.in index d9b989e..6eff460 100644 --- a/include/mod_gnutls.h.in +++ b/include/mod_gnutls.h.in | |||
@@ -27,14 +27,15 @@ | |||
27 | #include "apr_tables.h" | 27 | #include "apr_tables.h" |
28 | #include "ap_release.h" | 28 | #include "ap_release.h" |
29 | 29 | ||
30 | #include <gcrypt.h> | ||
31 | #include <gnutls/gnutls.h> | ||
32 | #include <gnutls/x509.h> | ||
33 | |||
30 | #ifndef __mod_gnutls_h_inc | 34 | #ifndef __mod_gnutls_h_inc |
31 | #define __mod_gnutls_h_inc | 35 | #define __mod_gnutls_h_inc |
32 | 36 | ||
33 | #define HAVE_APR_MEMCACHE @have_apr_memcache@ | 37 | #define HAVE_APR_MEMCACHE @have_apr_memcache@ |
34 | 38 | ||
35 | #include <gcrypt.h> | ||
36 | #include <gnutls/gnutls.h> | ||
37 | |||
38 | module AP_MODULE_DECLARE_DATA gnutls_module; | 39 | module AP_MODULE_DECLARE_DATA gnutls_module; |
39 | 40 | ||
40 | #define GNUTLS_OUTPUT_FILTER_NAME "gnutls_output_filter" | 41 | #define GNUTLS_OUTPUT_FILTER_NAME "gnutls_output_filter" |
@@ -70,9 +71,14 @@ typedef enum | |||
70 | 71 | ||
71 | typedef struct | 72 | typedef struct |
72 | { | 73 | { |
74 | gnutls_certificate_request_t client_verify_mode; | ||
75 | } mod_gnutls_dirconf_rec; | ||
76 | |||
77 | typedef struct | ||
78 | { | ||
73 | gnutls_certificate_credentials_t certs; | 79 | gnutls_certificate_credentials_t certs; |
74 | char *key_file; | 80 | gnutls_x509_crt_t cert_x509; |
75 | char *cert_file; | 81 | gnutls_x509_privkey_t privkey_x509; |
76 | int enabled; | 82 | int enabled; |
77 | int ciphers[16]; | 83 | int ciphers[16]; |
78 | int key_exchange[16]; | 84 | int key_exchange[16]; |
@@ -85,6 +91,7 @@ typedef struct | |||
85 | const char* cache_config; | 91 | const char* cache_config; |
86 | const char* rsa_params_file; | 92 | const char* rsa_params_file; |
87 | const char* dh_params_file; | 93 | const char* dh_params_file; |
94 | gnutls_certificate_request_t client_verify_mode; | ||
88 | } mod_gnutls_srvconf_rec; | 95 | } mod_gnutls_srvconf_rec; |
89 | 96 | ||
90 | typedef struct { | 97 | typedef struct { |
diff --git a/src/gnutls_io.c b/src/gnutls_io.c index f761f96..f081284 100644 --- a/src/gnutls_io.c +++ b/src/gnutls_io.c | |||
@@ -353,13 +353,12 @@ static apr_status_t gnutls_io_input_getline(mod_gnutls_handle_t * ctxt, | |||
353 | return APR_SUCCESS; | 353 | return APR_SUCCESS; |
354 | } | 354 | } |
355 | 355 | ||
356 | 356 | static int gnutls_do_handshake(mod_gnutls_handle_t * ctxt) | |
357 | static void gnutls_do_handshake(mod_gnutls_handle_t * ctxt) | ||
358 | { | 357 | { |
359 | int ret; | 358 | int ret; |
360 | int errcode; | 359 | int errcode; |
361 | if (ctxt->status != 0) { | 360 | if (ctxt->status != 0) { |
362 | return; | 361 | return 0; |
363 | } | 362 | } |
364 | 363 | ||
365 | tryagain: | 364 | tryagain: |
@@ -388,11 +387,37 @@ tryagain: | |||
388 | gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, | 387 | gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL, |
389 | gnutls_error_to_alert(ret, NULL)); | 388 | gnutls_error_to_alert(ret, NULL)); |
390 | gnutls_deinit(ctxt->session); | 389 | gnutls_deinit(ctxt->session); |
391 | return; | 390 | return ret; |
392 | } | 391 | } |
393 | else { | 392 | else { |
393 | /* all done with the handshake */ | ||
394 | ctxt->status = 1; | 394 | ctxt->status = 1; |
395 | return; /* all done with the handshake */ | 395 | return ret; |
396 | } | ||
397 | } | ||
398 | |||
399 | int mod_gnutls_rehandshake(mod_gnutls_handle_t * ctxt) | ||
400 | { | ||
401 | int rv; | ||
402 | |||
403 | rv = gnutls_rehandshake(ctxt->session); | ||
404 | |||
405 | if (rv != 0) { | ||
406 | /* the client did not want to rehandshake. goodbye */ | ||
407 | ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server, | ||
408 | "GnuTLS: Client Refused Rehandshake request."); | ||
409 | return -1; | ||
410 | } | ||
411 | |||
412 | ctxt->status = 0; | ||
413 | |||
414 | gnutls_do_handshake(ctxt); | ||
415 | |||
416 | if (ctxt->status == 1) { | ||
417 | return 0; | ||
418 | } | ||
419 | else { | ||
420 | return -1; | ||
396 | } | 421 | } |
397 | } | 422 | } |
398 | 423 | ||
@@ -414,26 +439,7 @@ apr_status_t mod_gnutls_filter_input(ap_filter_t* f, | |||
414 | } | 439 | } |
415 | 440 | ||
416 | if (ctxt->status == 0) { | 441 | if (ctxt->status == 0) { |
417 | char* server_name; | ||
418 | int server_type; | ||
419 | int data_len = 256; | ||
420 | |||
421 | gnutls_do_handshake(ctxt); | 442 | gnutls_do_handshake(ctxt); |
422 | |||
423 | /** | ||
424 | * Due to issues inside the GnuTLS API, we cannot currently do TLS 1.1 | ||
425 | * Server Name Indication. | ||
426 | */ | ||
427 | server_name = apr_palloc(ctxt->c->pool, data_len); | ||
428 | if (gnutls_server_name_get(ctxt->session, server_name, &data_len, &server_type, 0) == 0) { | ||
429 | if (server_type == GNUTLS_NAME_DNS) { | ||
430 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, | ||
431 | ctxt->c->base_server, | ||
432 | "GnuTLS: TLS 1.1 Server Name: " | ||
433 | "%s", server_name); | ||
434 | |||
435 | } | ||
436 | } | ||
437 | } | 443 | } |
438 | 444 | ||
439 | if (ctxt->status < 0) { | 445 | if (ctxt->status < 0) { |
diff --git a/src/mod_gnutls.c b/src/mod_gnutls.c index 71076ef..be150a9 100644 --- a/src/mod_gnutls.c +++ b/src/mod_gnutls.c | |||
@@ -17,6 +17,8 @@ | |||
17 | 17 | ||
18 | #include "mod_gnutls.h" | 18 | #include "mod_gnutls.h" |
19 | 19 | ||
20 | extern server_rec *ap_server_conf; | ||
21 | |||
20 | #if APR_HAS_THREADS | 22 | #if APR_HAS_THREADS |
21 | GCRY_THREAD_OPTION_PTHREAD_IMPL; | 23 | GCRY_THREAD_OPTION_PTHREAD_IMPL; |
22 | #endif | 24 | #endif |
@@ -97,7 +99,7 @@ static gnutls_datum load_params(const char* file, server_rec* s, | |||
97 | "GnuTLS failed to read params file at: %s", file); | 99 | "GnuTLS failed to read params file at: %s", file); |
98 | return ret; | 100 | return ret; |
99 | } | 101 | } |
100 | 102 | apr_file_close(fp); | |
101 | ret.data[br] = '\0'; | 103 | ret.data[br] = '\0'; |
102 | ret.size = br; | 104 | ret.size = br; |
103 | 105 | ||
@@ -127,7 +129,7 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, | |||
127 | } | 129 | } |
128 | 130 | ||
129 | 131 | ||
130 | if (!first_run) { | 132 | { |
131 | gnutls_datum pdata; | 133 | gnutls_datum pdata; |
132 | apr_pool_t* tpool; | 134 | apr_pool_t* tpool; |
133 | s = base_server; | 135 | s = base_server; |
@@ -194,32 +196,44 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog, | |||
194 | sc->cache_type = sc_base->cache_type; | 196 | sc->cache_type = sc_base->cache_type; |
195 | sc->cache_config = sc_base->cache_config; | 197 | sc->cache_config = sc_base->cache_config; |
196 | 198 | ||
197 | if (sc->cert_file != NULL && sc->key_file != NULL) { | 199 | gnutls_certificate_set_rsa_export_params(sc->certs, |
198 | |||
199 | rv = gnutls_certificate_set_x509_key_file(sc->certs, sc->cert_file, | ||
200 | sc->key_file, | ||
201 | GNUTLS_X509_FMT_PEM); | ||
202 | if (rv != 0) { | ||
203 | ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, | ||
204 | "[GnuTLS] - Host '%s:%d' has an invalid key or certificate:" | ||
205 | "(%s,%s) (%d) %s", | ||
206 | s->server_hostname, s->port, sc->cert_file, sc->key_file, | ||
207 | rv, gnutls_strerror(rv)); | ||
208 | } | ||
209 | else { | ||
210 | gnutls_certificate_set_rsa_export_params(sc->certs, | ||
211 | rsa_params); | 200 | rsa_params); |
212 | gnutls_certificate_set_dh_params(sc->certs, dh_params); | 201 | gnutls_certificate_set_dh_params(sc->certs, dh_params); |
213 | } | 202 | |
214 | } | 203 | if (sc->cert_x509 == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) { |
215 | else if (sc->enabled == GNUTLS_ENABLED_TRUE) { | ||
216 | ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, | 204 | ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, |
217 | "[GnuTLS] - Host '%s:%d' is missing a " | 205 | "[GnuTLS] - Host '%s:%d' is missing a " |
218 | "Cert and Key File!", | 206 | "Certificate File!", |
219 | s->server_hostname, s->port); | 207 | s->server_hostname, s->port); |
208 | exit(-1); | ||
209 | } | ||
210 | |||
211 | if (sc->privkey_x509 == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) { | ||
212 | ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, | ||
213 | "[GnuTLS] - Host '%s:%d' is missing a " | ||
214 | "Private Key File!", | ||
215 | s->server_hostname, s->port); | ||
216 | exit(-1); | ||
217 | } | ||
218 | { | ||
219 | int rv; | ||
220 | int data_len = 255; | ||
221 | char crt_name[255]; | ||
222 | for (rv = 0; rv < data_len; rv++) { | ||
223 | crt_name[rv] = '\0'; | ||
224 | } | ||
225 | rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509, | ||
226 | GNUTLS_OID_X520_COMMON_NAME, 0, 0, | ||
227 | crt_name, &data_len); | ||
228 | |||
229 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, | ||
230 | s, | ||
231 | "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X sc: 0x%08X", crt_name, rv, | ||
232 | gnutls_pk_algorithm_get_name(gnutls_x509_privkey_get_pk_algorithm(sc->privkey_x509)), | ||
233 | (unsigned int)s, (unsigned int)sc); | ||
220 | } | 234 | } |
221 | } | 235 | } |
222 | } /* first_run */ | 236 | } |
223 | 237 | ||
224 | ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION); | 238 | ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION); |
225 | 239 | ||
@@ -273,27 +287,112 @@ static apr_port_t mod_gnutls_hook_default_port(const request_rec * r) | |||
273 | return 443; | 287 | return 443; |
274 | } | 288 | } |
275 | 289 | ||
276 | /* TODO: Complete support for Server Name Indication */ | 290 | #define MAX_HOST_LEN 255 |
277 | static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st* ret) | 291 | static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st* ret) |
278 | { | 292 | { |
279 | char* server_name; | 293 | int rv; |
280 | int server_type; | 294 | int sni_type; |
281 | int data_len = 256; | 295 | int data_len = MAX_HOST_LEN; |
282 | mod_gnutls_handle_t *ctxt; | 296 | char sni_name[MAX_HOST_LEN]; |
297 | char crt_name[MAX_HOST_LEN]; | ||
298 | mod_gnutls_handle_t *ctxt; | ||
299 | mod_gnutls_srvconf_rec *tsc; | ||
300 | server_rec* s; | ||
301 | |||
283 | ctxt = gnutls_transport_get_ptr(session); | 302 | ctxt = gnutls_transport_get_ptr(session); |
303 | |||
304 | sni_type = gnutls_certificate_type_get(session); | ||
305 | if (sni_type != GNUTLS_CRT_X509) { | ||
306 | /* In theory, we could support OpenPGP Certificates. Theory != code. */ | ||
307 | ap_log_error(APLOG_MARK, APLOG_CRIT, 0, | ||
308 | ctxt->c->base_server, | ||
309 | "GnuTLS: Only x509 Certificates are currently supported."); | ||
310 | return -1; | ||
311 | } | ||
284 | 312 | ||
285 | ret->type = GNUTLS_CRT_X509; | 313 | ret->type = GNUTLS_CRT_X509; |
286 | ret->ncerts = 1; | 314 | ret->ncerts = 1; |
287 | server_name = apr_palloc(ctxt->c->pool, data_len); | 315 | ret->deinit_all = 0; |
288 | if (gnutls_server_name_get(ctxt->session, server_name, &data_len, &server_type, 0) == 0) { | 316 | |
289 | if (server_type == GNUTLS_NAME_DNS) { | 317 | rv = gnutls_server_name_get(ctxt->session, sni_name, |
290 | ap_log_error(APLOG_MARK, APLOG_INFO, 0, | 318 | &data_len, &sni_type, 0); |
319 | |||
320 | if (rv != 0) { | ||
321 | goto use_default_crt; | ||
322 | } | ||
323 | |||
324 | if (sni_type != GNUTLS_NAME_DNS) { | ||
325 | ap_log_error(APLOG_MARK, APLOG_CRIT, 0, | ||
326 | ctxt->c->base_server, | ||
327 | "GnuTLS: Unknown type '%d' for SNI: " | ||
328 | "'%s'", sni_type, sni_name); | ||
329 | goto use_default_crt; | ||
330 | } | ||
331 | |||
332 | /** | ||
333 | * Code in the Core already sets up the c->base_server as the base | ||
334 | * for this IP/Port combo. Trust that the core did the 'right' thing. | ||
335 | */ | ||
336 | for (s = ap_server_conf; s; s = s->next) { | ||
337 | |||
338 | tsc = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config, | ||
339 | &gnutls_module); | ||
340 | if (tsc->enabled != GNUTLS_ENABLED_TRUE) { | ||
341 | continue; | ||
342 | } | ||
343 | |||
344 | data_len = MAX_HOST_LEN; | ||
345 | rv = gnutls_x509_crt_get_dn_by_oid(tsc->cert_x509, | ||
346 | GNUTLS_OID_X520_COMMON_NAME, 0, 0, | ||
347 | crt_name, &data_len); | ||
348 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, | ||
349 | ctxt->c->base_server, | ||
350 | "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X s->n: 0x%08X sc: 0x%08X", crt_name, rv, | ||
351 | gnutls_pk_algorithm_get_name(gnutls_x509_privkey_get_pk_algorithm(ctxt->sc->privkey_x509)), | ||
352 | (unsigned int)s, (unsigned int)s->next, (unsigned int)tsc); | ||
353 | |||
354 | if (rv != 0) { | ||
355 | continue; | ||
356 | } | ||
357 | |||
358 | /* The CN can contain a * -- this will match those too. */ | ||
359 | if (ap_strcasecmp_match(sni_name, crt_name) == 0) { | ||
360 | /* found a match */ | ||
361 | ret->cert.x509 = &tsc->cert_x509; | ||
362 | ret->key.x509 = tsc->privkey_x509; | ||
363 | #if MOD_GNUTLS_DEBUG | ||
364 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, | ||
291 | ctxt->c->base_server, | 365 | ctxt->c->base_server, |
292 | "GnuTLS: Virtual Host: " | 366 | "GnuTLS: Virtual Host: " |
293 | "%s", server_name); | 367 | "'%s' == '%s'", crt_name, sni_name); |
368 | #endif | ||
369 | return 0; | ||
294 | } | 370 | } |
295 | } | 371 | } |
296 | 372 | ||
373 | |||
374 | /** | ||
375 | * If the client does not support the Server Name Indication, give the default | ||
376 | * certificate for this server. | ||
377 | */ | ||
378 | use_default_crt: | ||
379 | data_len = MAX_HOST_LEN; | ||
380 | rv = gnutls_x509_crt_get_dn_by_oid(ctxt->sc->cert_x509, | ||
381 | GNUTLS_OID_X520_COMMON_NAME, 0, 0, | ||
382 | crt_name, &data_len); | ||
383 | |||
384 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, | ||
385 | ctxt->c->base_server, | ||
386 | "GnuTLS: x509 cn: %s/%d pk: %s", crt_name, rv, | ||
387 | gnutls_pk_algorithm_get_name(gnutls_x509_privkey_get_pk_algorithm(ctxt->sc->privkey_x509))); | ||
388 | |||
389 | ret->cert.x509 = &ctxt->sc->cert_x509; | ||
390 | ret->key.x509 = ctxt->sc->privkey_x509; | ||
391 | #if MOD_GNUTLS_DEBUG | ||
392 | ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, | ||
393 | ctxt->c->base_server, | ||
394 | "GnuTLS: Using Default Certificate."); | ||
395 | #endif | ||
297 | return 0; | 396 | return 0; |
298 | } | 397 | } |
299 | 398 | ||
@@ -330,12 +429,12 @@ static mod_gnutls_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c) | |||
330 | 429 | ||
331 | gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE, sc->certs); | 430 | gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE, sc->certs); |
332 | 431 | ||
333 | gnutls_certificate_server_set_request(ctxt->session, GNUTLS_CERT_IGNORE); | 432 | gnutls_certificate_server_set_request(ctxt->session, sc->client_verify_mode); |
334 | 433 | ||
335 | mod_gnutls_cache_session_init(ctxt); | 434 | mod_gnutls_cache_session_init(ctxt); |
336 | 435 | ||
337 | /* TODO: Finish Support for Server Name Indication */ | 436 | /* TODO: Finish Support for Server Name Indication */ |
338 | /* gnutls_certificate_server_set_retrieve_function(sc->certs, cert_retrieve_fn); */ | 437 | gnutls_certificate_server_set_retrieve_function(sc->certs, cert_retrieve_fn); |
339 | return ctxt; | 438 | return ctxt; |
340 | } | 439 | } |
341 | 440 | ||
@@ -412,26 +511,101 @@ static int mod_gnutls_hook_fixups(request_rec *r) | |||
412 | return OK; | 511 | return OK; |
413 | } | 512 | } |
414 | 513 | ||
514 | static int load_datum_from_file(apr_pool_t* pool, | ||
515 | const char* file, | ||
516 | gnutls_datum_t* data) | ||
517 | { | ||
518 | apr_file_t* fp; | ||
519 | apr_finfo_t finfo; | ||
520 | apr_status_t rv; | ||
521 | apr_size_t br = 0; | ||
522 | |||
523 | rv = apr_file_open(&fp, file, APR_READ|APR_BINARY, APR_OS_DEFAULT, | ||
524 | pool); | ||
525 | if (rv != APR_SUCCESS) { | ||
526 | return rv; | ||
527 | } | ||
528 | |||
529 | rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp); | ||
530 | |||
531 | if (rv != APR_SUCCESS) { | ||
532 | return rv; | ||
533 | } | ||
534 | |||
535 | data->data = apr_palloc(pool, finfo.size+1); | ||
536 | rv = apr_file_read_full(fp, data->data, finfo.size, &br); | ||
537 | |||
538 | if (rv != APR_SUCCESS) { | ||
539 | return rv; | ||
540 | } | ||
541 | apr_file_close(fp); | ||
542 | |||
543 | data->data[br] = '\0'; | ||
544 | data->size = br; | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
415 | static const char *gnutls_set_cert_file(cmd_parms * parms, void *dummy, | 549 | static const char *gnutls_set_cert_file(cmd_parms * parms, void *dummy, |
416 | const char *arg) | 550 | const char *arg) |
417 | { | 551 | { |
552 | int ret; | ||
553 | gnutls_datum_t data; | ||
554 | const char* file; | ||
555 | apr_pool_t* spool; | ||
418 | mod_gnutls_srvconf_rec *sc = | 556 | mod_gnutls_srvconf_rec *sc = |
419 | (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server-> | 557 | (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server-> |
420 | module_config, | 558 | module_config, |
421 | &gnutls_module); | 559 | &gnutls_module); |
422 | sc->cert_file = ap_server_root_relative(parms->pool, arg); | 560 | apr_pool_create(&spool, parms->pool); |
561 | |||
562 | file = ap_server_root_relative(spool, arg); | ||
563 | |||
564 | if (load_datum_from_file(spool, file, &data) != 0) { | ||
565 | return apr_psprintf(parms->pool, "GnuTLS: Error Reading " | ||
566 | "Certificate '%s'", file); | ||
567 | } | ||
568 | |||
569 | gnutls_x509_crt_init(&sc->cert_x509); | ||
570 | ret = gnutls_x509_crt_import(sc->cert_x509, &data, GNUTLS_X509_FMT_PEM); | ||
571 | if (ret != 0) { | ||
572 | return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " | ||
573 | "Certificate'%s': (%d) %s", file, ret, | ||
574 | gnutls_strerror(ret)); | ||
575 | } | ||
576 | |||
577 | //apr_pool_destroy(spool); | ||
423 | return NULL; | 578 | return NULL; |
424 | } | 579 | } |
425 | 580 | ||
426 | static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy, | 581 | static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy, |
427 | const char *arg) | 582 | const char *arg) |
428 | { | 583 | { |
584 | int ret; | ||
585 | gnutls_datum_t data; | ||
586 | const char* file; | ||
587 | apr_pool_t* spool; | ||
429 | mod_gnutls_srvconf_rec *sc = | 588 | mod_gnutls_srvconf_rec *sc = |
430 | (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server-> | 589 | (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server-> |
431 | module_config, | 590 | module_config, |
432 | &gnutls_module); | 591 | &gnutls_module); |
592 | apr_pool_create(&spool, parms->pool); | ||
433 | 593 | ||
434 | sc->key_file = ap_server_root_relative(parms->pool, arg); | 594 | file = ap_server_root_relative(spool, arg); |
595 | |||
596 | if (load_datum_from_file(spool, file, &data) != 0) { | ||
597 | return apr_psprintf(parms->pool, "GnuTLS: Error Reading " | ||
598 | "Private Key '%s'", file); | ||
599 | } | ||
600 | |||
601 | gnutls_x509_privkey_init(&sc->privkey_x509); | ||
602 | ret = gnutls_x509_privkey_import(sc->privkey_x509, &data, GNUTLS_X509_FMT_PEM); | ||
603 | if (ret != 0) { | ||
604 | return apr_psprintf(parms->pool, "GnuTLS: Failed to Import " | ||
605 | "Private Key '%s': (%d) %s", file, ret, | ||
606 | gnutls_strerror(ret)); | ||
607 | } | ||
608 | //apr_pool_destroy(spool); | ||
435 | return NULL; | 609 | return NULL; |
436 | } | 610 | } |
437 | 611 | ||
@@ -471,6 +645,64 @@ static const char *gnutls_set_cache(cmd_parms * parms, void *dummy, | |||
471 | return NULL; | 645 | return NULL; |
472 | } | 646 | } |
473 | 647 | ||
648 | static const char *gnutls_set_cache_timeout(cmd_parms * parms, void *dummy, | ||
649 | const char *arg) | ||
650 | { | ||
651 | int argint; | ||
652 | mod_gnutls_srvconf_rec *sc = | ||
653 | (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server-> | ||
654 | module_config, | ||
655 | &gnutls_module); | ||
656 | |||
657 | argint = atoi(arg); | ||
658 | |||
659 | if (argint < 0) { | ||
660 | return "GnuTLSCacheTimeout: Invalid argument"; | ||
661 | } | ||
662 | else if (argint == 0) { | ||
663 | sc->cache_timeout = 0; | ||
664 | } | ||
665 | else { | ||
666 | sc->cache_timeout = apr_time_from_sec(argint); | ||
667 | } | ||
668 | |||
669 | return NULL; | ||
670 | } | ||
671 | |||
672 | |||
673 | static const char *gnutls_set_client_verify(cmd_parms * parms, void *dummy, | ||
674 | const char *arg) | ||
675 | { | ||
676 | gnutls_certificate_request_t mode; | ||
677 | |||
678 | if (strcasecmp("none", arg) == 0 || strcasecmp("ignore", arg) == 0) { | ||
679 | mode = GNUTLS_CERT_IGNORE; | ||
680 | } | ||
681 | else if (strcasecmp("optional", arg) == 0 || strcasecmp("request", arg) == 0) { | ||
682 | mode = GNUTLS_CERT_REQUEST; | ||
683 | } | ||
684 | else if (strcasecmp("optional", arg) == 0) { | ||
685 | mode = GNUTLS_CERT_REQUIRE; | ||
686 | } | ||
687 | else { | ||
688 | return "GnuTLSClientVerify: Invalid argument"; | ||
689 | } | ||
690 | |||
691 | /* This was set from a directory context */ | ||
692 | if (parms->path) { | ||
693 | mod_gnutls_dirconf_rec *dc = (mod_gnutls_dirconf_rec *)dummy; | ||
694 | dc->client_verify_mode = mode; | ||
695 | } | ||
696 | else { | ||
697 | mod_gnutls_srvconf_rec *sc = | ||
698 | (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server-> | ||
699 | module_config, | ||
700 | &gnutls_module); | ||
701 | sc->client_verify_mode = mode; | ||
702 | } | ||
703 | |||
704 | return NULL; | ||
705 | } | ||
474 | static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy, | 706 | static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy, |
475 | const char *arg) | 707 | const char *arg) |
476 | { | 708 | { |
@@ -492,6 +724,10 @@ static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy, | |||
492 | } | 724 | } |
493 | 725 | ||
494 | static const command_rec gnutls_cmds[] = { | 726 | static const command_rec gnutls_cmds[] = { |
727 | AP_INIT_TAKE1("GnuTLSClientVerify", gnutls_set_client_verify, | ||
728 | NULL, | ||
729 | RSRC_CONF|OR_AUTHCFG, | ||
730 | "Set Verification Requirements of the Client Certificate"), | ||
495 | AP_INIT_TAKE1("GnuTLSCertificateFile", gnutls_set_cert_file, | 731 | AP_INIT_TAKE1("GnuTLSCertificateFile", gnutls_set_cert_file, |
496 | NULL, | 732 | NULL, |
497 | RSRC_CONF, | 733 | RSRC_CONF, |
@@ -500,6 +736,10 @@ static const command_rec gnutls_cmds[] = { | |||
500 | NULL, | 736 | NULL, |
501 | RSRC_CONF, | 737 | RSRC_CONF, |
502 | "SSL Server Certificate file"), | 738 | "SSL Server Certificate file"), |
739 | AP_INIT_TAKE1("GnuTLSCacheTimeout", gnutls_set_cache_timeout, | ||
740 | NULL, | ||
741 | RSRC_CONF, | ||
742 | "Cache Timeout"), | ||
503 | AP_INIT_TAKE2("GnuTLSCache", gnutls_set_cache, | 743 | AP_INIT_TAKE2("GnuTLSCache", gnutls_set_cache, |
504 | NULL, | 744 | NULL, |
505 | RSRC_CONF, | 745 | RSRC_CONF, |
@@ -518,6 +758,29 @@ static const command_rec gnutls_cmds[] = { | |||
518 | * "CA"), | 758 | * "CA"), |
519 | */ | 759 | */ |
520 | 760 | ||
761 | int mod_gnutls_hook_authz(request_rec *r) | ||
762 | { | ||
763 | return OK; | ||
764 | #if 0 | ||
765 | mod_gnutls_handle_t *ctxt; | ||
766 | mod_gnutls_dirconf_rec *dc = ap_get_module_config(r->per_dir_config, | ||
767 | &gnutls_module); | ||
768 | |||
769 | ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module); | ||
770 | |||
771 | if (dc->client_verify_mode == -1 || | ||
772 | dc->client_verify_mode == GNUTLS_CERT_IGNORE || | ||
773 | ctxt->sc->client_verify_mode > dc->client_verify_mode) { | ||
774 | return DECLINED; | ||
775 | } | ||
776 | |||
777 | gnutls_certificate_server_set_request(ctxt->session, dc->client_verify_mode); | ||
778 | if (mod_gnutls_rehandshake(ctxt) != 0) { | ||
779 | return HTTP_FORBIDDEN; | ||
780 | } | ||
781 | #endif | ||
782 | } | ||
783 | |||
521 | static void gnutls_hooks(apr_pool_t * p) | 784 | static void gnutls_hooks(apr_pool_t * p) |
522 | { | 785 | { |
523 | ap_hook_pre_connection(mod_gnutls_hook_pre_connection, NULL, NULL, | 786 | ap_hook_pre_connection(mod_gnutls_hook_pre_connection, NULL, NULL, |
@@ -537,8 +800,10 @@ static void gnutls_hooks(apr_pool_t * p) | |||
537 | APR_HOOK_MIDDLE); | 800 | APR_HOOK_MIDDLE); |
538 | ap_hook_pre_config(mod_gnutls_hook_pre_config, NULL, NULL, | 801 | ap_hook_pre_config(mod_gnutls_hook_pre_config, NULL, NULL, |
539 | APR_HOOK_MIDDLE); | 802 | APR_HOOK_MIDDLE); |
540 | 803 | ||
541 | ap_hook_fixups(mod_gnutls_hook_fixups, NULL, NULL, APR_HOOK_MIDDLE); | 804 | ap_hook_auth_checker(mod_gnutls_hook_authz, NULL, NULL, APR_HOOK_MIDDLE); |
805 | |||
806 | ap_hook_fixups(mod_gnutls_hook_fixups, NULL, NULL, APR_HOOK_REALLY_FIRST); | ||
542 | 807 | ||
543 | /* TODO: HTTP Upgrade Filter */ | 808 | /* TODO: HTTP Upgrade Filter */ |
544 | /* ap_register_output_filter ("UPGRADE_FILTER", | 809 | /* ap_register_output_filter ("UPGRADE_FILTER", |
@@ -560,18 +825,21 @@ static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s) | |||
560 | sc->enabled = GNUTLS_ENABLED_FALSE; | 825 | sc->enabled = GNUTLS_ENABLED_FALSE; |
561 | 826 | ||
562 | gnutls_certificate_allocate_credentials(&sc->certs); | 827 | gnutls_certificate_allocate_credentials(&sc->certs); |
563 | sc->key_file = NULL; | 828 | sc->privkey_x509 = NULL; |
564 | sc->cert_file = NULL; | 829 | sc->cert_x509 = NULL; |
565 | sc->cache_timeout = apr_time_from_sec(3600); | 830 | sc->cache_timeout = apr_time_from_sec(300); |
566 | sc->cache_type = mod_gnutls_cache_dbm; | 831 | sc->cache_type = mod_gnutls_cache_dbm; |
567 | sc->cache_config = ap_server_root_relative(p, "conf/gnutls_cache"); | 832 | sc->cache_config = ap_server_root_relative(p, "conf/gnutls_cache"); |
568 | 833 | ||
569 | /* TODO: Make this Configurable ! */ | 834 | /* TODO: Make this Configurable. But it isn't configurable in mod_ssl? */ |
570 | sc->dh_params_file = ap_server_root_relative(p, "conf/dhfile"); | 835 | sc->dh_params_file = ap_server_root_relative(p, "conf/dhfile"); |
571 | sc->rsa_params_file = ap_server_root_relative(p, "conf/rsafile"); | 836 | sc->rsa_params_file = ap_server_root_relative(p, "conf/rsafile"); |
572 | 837 | ||
838 | /* Finish SSL Client Certificate Support */ | ||
839 | sc->client_verify_mode = GNUTLS_CERT_IGNORE; | ||
840 | |||
573 | /* TODO: Make this Configurable ! */ | 841 | /* TODO: Make this Configurable ! */ |
574 | /* meh. mod_ssl uses a flex based parser for this part.. sigh */ | 842 | /* mod_ssl uses a flex based parser for this part.. sigh */ |
575 | i = 0; | 843 | i = 0; |
576 | sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC; | 844 | sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC; |
577 | sc->ciphers[i++] = GNUTLS_CIPHER_AES_128_CBC; | 845 | sc->ciphers[i++] = GNUTLS_CIPHER_AES_128_CBC; |
@@ -616,11 +884,18 @@ static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s) | |||
616 | return sc; | 884 | return sc; |
617 | } | 885 | } |
618 | 886 | ||
887 | void *gnutls_config_dir_create(apr_pool_t *p, char *dir) | ||
888 | { | ||
889 | mod_gnutls_dirconf_rec *dc = apr_palloc(p, sizeof(*dc)); | ||
619 | 890 | ||
891 | dc->client_verify_mode = -1; | ||
892 | |||
893 | return dc; | ||
894 | } | ||
620 | 895 | ||
621 | module AP_MODULE_DECLARE_DATA gnutls_module = { | 896 | module AP_MODULE_DECLARE_DATA gnutls_module = { |
622 | STANDARD20_MODULE_STUFF, | 897 | STANDARD20_MODULE_STUFF, |
623 | NULL, | 898 | gnutls_config_dir_create, |
624 | NULL, | 899 | NULL, |
625 | gnutls_config_server_create, | 900 | gnutls_config_server_create, |
626 | NULL, | 901 | NULL, |