aboutsummaryrefslogtreecommitdiffstats
path: root/src/mod_gnutls.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_gnutls.c')
-rw-r--r--src/mod_gnutls.c385
1 files changed, 30 insertions, 355 deletions
diff --git a/src/mod_gnutls.c b/src/mod_gnutls.c
index e696ec6..14bf319 100644
--- a/src/mod_gnutls.c
+++ b/src/mod_gnutls.c
@@ -15,201 +15,12 @@
15 * 15 *
16 */ 16 */
17 17
18#include "httpd.h" 18#include "mod_gnutls.h"
19#include "http_config.h"
20#include "http_protocol.h"
21#include "http_connection.h"
22#include "http_core.h"
23#include "http_log.h"
24#include "apr_buckets.h"
25#include "apr_strings.h"
26#include "apr_tables.h"
27
28#include <gcrypt.h>
29#include <gnutls/gnutls.h>
30 19
31#if APR_HAS_THREADS 20#if APR_HAS_THREADS
32GCRY_THREAD_OPTION_PTHREAD_IMPL; 21GCRY_THREAD_OPTION_PTHREAD_IMPL;
33#endif 22#endif
34 23
35module AP_MODULE_DECLARE_DATA gnutls_module;
36
37#ifdef GNUTLS_AS_FILTER
38#define GNUTLS_OUTPUT_FILTER_NAME "GnuTLS Output Filter"
39#define GNUTLS_INPUT_FILTER_NAME "GnuTLS Input Filter"
40#endif
41
42#define GNUTLS_ENABLED_FALSE 0
43#define GNUTLS_ENABLED_TRUE 1
44
45
46typedef struct {
47 gnutls_certificate_credentials_t certs;
48 gnutls_anon_server_credentials_t anoncred;
49 char *key_file;
50 char *cert_file;
51 int enabled;
52 int ciphers[16];
53 int key_exchange[16];
54 int macs[16];
55 int protocol[16];
56 int compression[16];
57} gnutls_srvconf_rec;
58
59typedef struct gnutls_handle_t gnutls_handle_t;
60struct gnutls_handle_t
61{
62 gnutls_srvconf_rec *sc;
63 gnutls_session_t session;
64 ap_filter_t *input_filter;
65 apr_bucket_brigade *input_bb;
66 apr_read_type_e input_block;
67 int status;
68 int non_https;
69};
70
71static apr_status_t gnutls_filter_input(ap_filter_t * f,
72 apr_bucket_brigade * bb,
73 ap_input_mode_t mode,
74 apr_read_type_e block,
75 apr_off_t readbytes)
76{
77 apr_status_t status = APR_SUCCESS;
78 gnutls_handle_t *ctxt = (gnutls_handle_t *) f->ctx;
79
80 if (f->c->aborted) {
81 apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
82 APR_BRIGADE_INSERT_TAIL(bb, bucket);
83 return APR_ECONNABORTED;
84 }
85
86
87 for (b = APR_BRIGADE_FIRST(bb);
88 b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
89 if (APR_BUCKET_IS_EOS(b)) {
90 /* end of connection */
91 }
92 else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
93 == APR_SUCCESS) {
94 /* more data */
95 }
96 }
97
98 return status;
99}
100
101#define HANDSHAKE_ATTEMPTS 10
102
103static apr_status_t gnutls_filter_output(ap_filter_t * f,
104 apr_bucket_brigade * bb)
105{
106 int ret, i;
107 const char *buf = 0;
108 apr_size_t bytes = 0;
109 gnutls_handle_t *ctxt = (gnutls_handle_t *) f->ctx;
110 apr_status_t status = APR_SUCCESS;
111 apr_read_type_e rblock = APR_NONBLOCK_READ;
112
113 if (f->c->aborted) {
114 apr_brigade_cleanup(bb);
115 return APR_ECONNABORTED;
116 }
117
118 if(ctxt->status == 0) {
119 for (i=HANDSHAKE_ATTEMPTS; i>0; i--){
120 ret = gnutls_handshake(ctxt->session);
121
122 if(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN){
123 continue;
124 }
125
126 if (ret < 0) {
127 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
128 ret = gnutls_alert_get(ctxt->session);
129 ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server,
130 "GnuTLS: Hanshake Alert (%d) '%s'.\n", ret, gnutls_alert_get_name(ret));
131 }
132
133 if (gnutls_error_is_fatal(ret) != 0) {
134 gnutls_deinit(ctxt->session);
135 ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server,
136 "GnuTLS: Handshake Failed (%d) '%s'",ret, gnutls_strerror(ret));
137 ctxt->status = -1;
138 break;
139 }
140 }
141 else {
142 ctxt->status = 1;
143 break; /* all done with the handshake */
144 }
145 }
146 }
147
148 if (ctxt->status < 0) {
149 return ap_pass_brigade(f->next, bb);
150 }
151
152 while (!APR_BRIGADE_EMPTY(bb)) {
153 apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
154 if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
155 /** TODO: GnuTLS doesn't have a special flush method? **/
156 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
157 return status;
158 }
159 break;
160 }
161 else if(AP_BUCKET_IS_EOC(bucket)) {
162 gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
163
164 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
165 return status;
166 }
167 break;
168 }
169 else {
170 /* filter output */
171 const char *data;
172 apr_size_t len;
173
174 status = apr_bucket_read(bucket, &data, &len, rblock);
175
176 if (APR_STATUS_IS_EAGAIN(status)) {
177 rblock = APR_BLOCK_READ;
178 continue; /* and try again with a blocking read. */
179 }
180
181 rblock = APR_NONBLOCK_READ;
182
183 if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
184 break;
185 }
186
187 ret = gnutls_record_send(ctxt->session, data, len);
188 status = ssl_filter_write(f, data, len);
189 if(ret < 0) {
190 /* error sending output */
191 }
192 else if ((apr_size_t)res != len) {
193 /* not all of the data was sent. */
194 /* mod_ssl basicly errors out here.. this doesn't seem right? */
195 }
196 else {
197 /* send complete */
198
199 }
200
201 apr_bucket_delete(bucket);
202
203 if (status != APR_SUCCESS) {
204 break;
205 }
206
207 }
208 }
209
210 return status;
211}
212
213static apr_status_t gnutls_cleanup_pre_config(void *data) 24static apr_status_t gnutls_cleanup_pre_config(void *data)
214{ 25{
215 gnutls_global_deinit(); 26 gnutls_global_deinit();
@@ -253,7 +64,7 @@ static int gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
253 64
254 for (s = base_server; s; s = s->next) { 65 for (s = base_server; s; s = s->next) {
255 sc = (gnutls_srvconf_rec *) ap_get_module_config(s->module_config, 66 sc = (gnutls_srvconf_rec *) ap_get_module_config(s->module_config,
256 &gnutls_module); 67 &gnutls_module);
257 if (sc->cert_file != NULL && sc->key_file != NULL) { 68 if (sc->cert_file != NULL && sc->key_file != NULL) {
258 gnutls_certificate_set_x509_key_file(sc->certs, sc->cert_file, 69 gnutls_certificate_set_x509_key_file(sc->certs, sc->cert_file,
259 sc->key_file, 70 sc->key_file,
@@ -261,7 +72,7 @@ static int gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
261// gnutls_certificate_set_rsa_export_params(sc->certs, rsa_params); 72// gnutls_certificate_set_rsa_export_params(sc->certs, rsa_params);
262// gnutls_certificate_set_dh_params(sc->certs, dh_params); 73// gnutls_certificate_set_dh_params(sc->certs, dh_params);
263 } 74 }
264 else if(sc->enabled == GNUTLS_ENABLED_TRUE ){ 75 else if (sc->enabled == GNUTLS_ENABLED_TRUE) {
265 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, 76 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
266 "[GnuTLS] - Host '%s:%d' is missing a Cert and Key File!", 77 "[GnuTLS] - Host '%s:%d' is missing a Cert and Key File!",
267 s->server_hostname, s->port); 78 s->server_hostname, s->port);
@@ -277,7 +88,7 @@ static const char *gnutls_hook_http_method(const request_rec * r)
277{ 88{
278 gnutls_srvconf_rec *sc = 89 gnutls_srvconf_rec *sc =
279 (gnutls_srvconf_rec *) ap_get_module_config(r->server->module_config, 90 (gnutls_srvconf_rec *) ap_get_module_config(r->server->module_config,
280 &gnutls_module); 91 &gnutls_module);
281 92
282 if (sc->enabled == GNUTLS_ENABLED_FALSE) { 93 if (sc->enabled == GNUTLS_ENABLED_FALSE) {
283 return NULL; 94 return NULL;
@@ -290,7 +101,7 @@ static apr_port_t gnutls_hook_default_port(const request_rec * r)
290{ 101{
291 gnutls_srvconf_rec *sc = 102 gnutls_srvconf_rec *sc =
292 (gnutls_srvconf_rec *) ap_get_module_config(r->server->module_config, 103 (gnutls_srvconf_rec *) ap_get_module_config(r->server->module_config,
293 &gnutls_module); 104 &gnutls_module);
294 105
295 if (sc->enabled == GNUTLS_ENABLED_FALSE) { 106 if (sc->enabled == GNUTLS_ENABLED_FALSE) {
296 return 0; 107 return 0;
@@ -299,152 +110,13 @@ static apr_port_t gnutls_hook_default_port(const request_rec * r)
299 return 443; 110 return 443;
300} 111}
301 112
302/**
303 * From mod_ssl / ssl_engine_io.c
304 * This function will read from a brigade and discard the read buckets as it
305 * proceeds. It will read at most *len bytes.
306 */
307static apr_status_t brigade_consume(apr_bucket_brigade * bb,
308 apr_read_type_e block,
309 char *c, apr_size_t * len)
310{
311 apr_size_t actual = 0;
312 apr_status_t status = APR_SUCCESS;
313
314 while (!APR_BRIGADE_EMPTY(bb)) {
315 apr_bucket *b = APR_BRIGADE_FIRST(bb);
316 const char *str;
317 apr_size_t str_len;
318 apr_size_t consume;
319
320 /* Justin points out this is an http-ism that might
321 * not fit if brigade_consume is added to APR. Perhaps
322 * apr_bucket_read(eos_bucket) should return APR_EOF?
323 * Then this becomes mainline instead of a one-off.
324 */
325 if (APR_BUCKET_IS_EOS(b)) {
326 status = APR_EOF;
327 break;
328 }
329
330 /* The reason I'm not offering brigade_consume yet
331 * across to apr-util is that the following call
332 * illustrates how borked that API really is. For
333 * this sort of case (caller provided buffer) it
334 * would be much more trivial for apr_bucket_consume
335 * to do all the work that follows, based on the
336 * particular characteristics of the bucket we are
337 * consuming here.
338 */
339 status = apr_bucket_read(b, &str, &str_len, block);
340
341 if (status != APR_SUCCESS) {
342 if (APR_STATUS_IS_EOF(status)) {
343 /* This stream bucket was consumed */
344 apr_bucket_delete(b);
345 continue;
346 }
347 break;
348 }
349
350 if (str_len > 0) {
351 /* Do not block once some data has been consumed */
352 block = APR_NONBLOCK_READ;
353
354 /* Assure we don't overflow. */
355 consume = (str_len + actual > *len) ? *len - actual : str_len;
356
357 memcpy(c, str, consume);
358
359 c += consume;
360 actual += consume;
361
362 if (consume >= b->length) {
363 /* This physical bucket was consumed */
364 apr_bucket_delete(b);
365 }
366 else {
367 /* Only part of this physical bucket was consumed */
368 b->start += consume;
369 b->length -= consume;
370 }
371 }
372 else if (b->length == 0) {
373 apr_bucket_delete(b);
374 }
375
376 /* This could probably be actual == *len, but be safe from stray
377 * photons. */
378 if (actual >= *len) {
379 break;
380 }
381 }
382
383 *len = actual;
384 return status;
385}
386
387
388static ssize_t gnutls_transport_read(gnutls_transport_ptr_t ptr,
389 void *buffer, size_t len)
390{
391 gnutls_handle_t *ctxt = ptr;
392 apr_status_t rc;
393 apr_size_t in = len;
394 /* If Len = 0, we don't do anything. */
395 if (!len)
396 return 0;
397
398 if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
399
400 rc = ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
401 AP_MODE_READBYTES, ctxt->input_block, in);
402
403 /* Not a problem, there was simply no data ready yet.
404 */
405 if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
406 || (rc == APR_SUCCESS && APR_BRIGADE_EMPTY(ctxt->input_bb))) {
407 return 0;
408 }
409
410 if (rc != APR_SUCCESS) {
411 /* Unexpected errors discard the brigade */
412 apr_brigade_cleanup(ctxt->input_bb);
413 ctxt->input_bb = NULL;
414 return -1;
415 }
416 }
417
418// brigade_consume(ctxt->input_bb, ctxt->input_block, buffer, &len);
419
420
421 ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
422 AP_MODE_READBYTES, ctxt->input_block, len);
423
424 return len;
425}
426
427static ssize_t gnutls_transport_write(gnutls_transport_ptr_t ptr,
428 const void *buffer, size_t len)
429{
430 gnutls_handle_t *ctxt = ptr;
431
432// apr_bucket *bucket = apr_bucket_transient_create(in, inl,
433// outctx->bb->
434// bucket_alloc);
435
436 // outctx->length += inl;
437 //APR_BRIGADE_INSERT_TAIL(outctx->bb, bucket);
438 return 0;
439}
440
441static int gnutls_hook_pre_connection(conn_rec * c, void *csd) 113static int gnutls_hook_pre_connection(conn_rec * c, void *csd)
442{ 114{
443 gnutls_handle_t *ctxt; 115 gnutls_handle_t *ctxt;
444 gnutls_srvconf_rec *sc = 116 gnutls_srvconf_rec *sc =
445 (gnutls_srvconf_rec *) ap_get_module_config(c->base_server-> 117 (gnutls_srvconf_rec *) ap_get_module_config(c->base_server->
446 module_config, 118 module_config,
447 &gnutls_module); 119 &gnutls_module);
448 120
449 if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) { 121 if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) {
450 return DECLINED; 122 return DECLINED;
@@ -470,12 +142,14 @@ static int gnutls_hook_pre_connection(conn_rec * c, void *csd)
470 gnutls_certificate_server_set_request(ctxt->session, GNUTLS_CERT_IGNORE); 142 gnutls_certificate_server_set_request(ctxt->session, GNUTLS_CERT_IGNORE);
471 143
472// gnutls_dh_set_prime_bits(ctxt->session, DH_BITS); 144// gnutls_dh_set_prime_bits(ctxt->session, DH_BITS);
473 145
474 146
475 ap_set_module_config(c->conn_config, &gnutls_module, ctxt); 147 ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
476 148
477 gnutls_transport_set_pull_function(ctxt->session, gnutls_transport_read); 149 gnutls_transport_set_pull_function(ctxt->session,
478 gnutls_transport_set_push_function(ctxt->session, gnutls_transport_write); 150 mod_gnutls_transport_read);
151 gnutls_transport_set_push_function(ctxt->session,
152 mod_gnutls_transport_write);
479 gnutls_transport_set_ptr(ctxt->session, ctxt); 153 gnutls_transport_set_ptr(ctxt->session, ctxt);
480 ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c); 154 ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c);
481 ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c); 155 ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c);
@@ -484,24 +158,24 @@ static int gnutls_hook_pre_connection(conn_rec * c, void *csd)
484} 158}
485 159
486static const char *gnutls_set_cert_file(cmd_parms * parms, void *dummy, 160static const char *gnutls_set_cert_file(cmd_parms * parms, void *dummy,
487 const char *arg) 161 const char *arg)
488{ 162{
489 gnutls_srvconf_rec *sc = 163 gnutls_srvconf_rec *sc =
490 (gnutls_srvconf_rec *) ap_get_module_config(parms->server-> 164 (gnutls_srvconf_rec *) ap_get_module_config(parms->server->
491 module_config, 165 module_config,
492 &gnutls_module); 166 &gnutls_module);
493 sc->cert_file = apr_pstrdup(parms->pool, arg); 167 sc->cert_file = apr_pstrdup(parms->pool, arg);
494 return NULL; 168 return NULL;
495} 169}
496 170
497static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy, 171static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy,
498 const char *arg) 172 const char *arg)
499{ 173{
500 gnutls_srvconf_rec *sc = 174 gnutls_srvconf_rec *sc =
501 (gnutls_srvconf_rec *) ap_get_module_config(parms->server-> 175 (gnutls_srvconf_rec *) ap_get_module_config(parms->server->
502 module_config, 176 module_config,
503 &gnutls_module); 177 &gnutls_module);
504 sc->key_file = apr_pstrdup(parms->pool, arg); 178 sc->key_file = apr_pstrdup(parms->pool, arg);
505 return NULL; 179 return NULL;
506} 180}
507 181
@@ -510,8 +184,8 @@ static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy,
510{ 184{
511 gnutls_srvconf_rec *sc = 185 gnutls_srvconf_rec *sc =
512 (gnutls_srvconf_rec *) ap_get_module_config(parms->server-> 186 (gnutls_srvconf_rec *) ap_get_module_config(parms->server->
513 module_config, 187 module_config,
514 &gnutls_module); 188 &gnutls_module);
515 if (!strcasecmp(arg, "On")) { 189 if (!strcasecmp(arg, "On")) {
516 sc->enabled = GNUTLS_ENABLED_TRUE; 190 sc->enabled = GNUTLS_ENABLED_TRUE;
517 } 191 }
@@ -535,8 +209,8 @@ static const command_rec gnutls_cmds[] = {
535 RSRC_CONF, 209 RSRC_CONF,
536 "SSL Server Certificate file"), 210 "SSL Server Certificate file"),
537 AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled, 211 AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled,
538 NULL, RSRC_CONF, 212 NULL, RSRC_CONF,
539 "Whether this server has GnuTLS Enabled. Default: Off"), 213 "Whether this server has GnuTLS Enabled. Default: Off"),
540 214
541 {NULL} 215 {NULL}
542}; 216};
@@ -562,10 +236,12 @@ static void gnutls_hooks(apr_pool_t * p)
562 /* ap_register_output_filter ("UPGRADE_FILTER", 236 /* ap_register_output_filter ("UPGRADE_FILTER",
563 * ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5); 237 * ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5);
564 */ 238 */
565 ap_register_input_filter(GNUTLS_INPUT_FILTER_NAME, gnutls_filter_input, 239 ap_register_input_filter(GNUTLS_INPUT_FILTER_NAME,
566 NULL, AP_FTYPE_CONNECTION + 5); 240 mod_gnutls_filter_input, NULL,
567 ap_register_output_filter(GNUTLS_OUTPUT_FILTER_NAME, gnutls_filter_output, 241 AP_FTYPE_CONNECTION + 5);
568 NULL, AP_FTYPE_CONNECTION + 5); 242 ap_register_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
243 mod_gnutls_filter_output, NULL,
244 AP_FTYPE_CONNECTION + 5);
569} 245}
570 246
571static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s) 247static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s)
@@ -574,7 +250,6 @@ static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s)
574 gnutls_srvconf_rec *sc = apr_pcalloc(p, sizeof(*sc)); 250 gnutls_srvconf_rec *sc = apr_pcalloc(p, sizeof(*sc));
575 251
576 sc->enabled = GNUTLS_ENABLED_FALSE; 252 sc->enabled = GNUTLS_ENABLED_FALSE;
577 sc->non_https = 0;
578 253
579 gnutls_certificate_allocate_credentials(&sc->certs); 254 gnutls_certificate_allocate_credentials(&sc->certs);
580 gnutls_anon_allocate_server_credentials(&sc->anoncred); 255 gnutls_anon_allocate_server_credentials(&sc->anoncred);
@@ -586,7 +261,7 @@ static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s)
586 sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_128; 261 sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_128;
587 sc->ciphers[i++] = GNUTLS_CIPHER_3DES_CBC; 262 sc->ciphers[i++] = GNUTLS_CIPHER_3DES_CBC;
588 sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_40; 263 sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_40;
589 sc->ciphers[i] = 0; 264 sc->ciphers[i] = 0;
590 265
591 i = 0; 266 i = 0;
592 sc->key_exchange[i++] = GNUTLS_KX_RSA; 267 sc->key_exchange[i++] = GNUTLS_KX_RSA;