summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
-rw-r--r--include/mod_gnutls.h19
-rw-r--r--src/gnutls_io.c647
-rw-r--r--src/mod_gnutls.c46
3 files changed, 561 insertions, 151 deletions
diff --git a/include/mod_gnutls.h b/include/mod_gnutls.h
index b1abd52..3ea9b58 100644
--- a/include/mod_gnutls.h
+++ b/include/mod_gnutls.h
@@ -69,13 +69,32 @@ typedef struct
69 int compression[16]; 69 int compression[16];
70} mod_gnutls_srvconf_rec; 70} mod_gnutls_srvconf_rec;
71 71
72typedef struct {
73 int length;
74 char *value;
75} mod_gnutls_char_buffer_t;
76
72typedef struct 77typedef struct
73{ 78{
74 mod_gnutls_srvconf_rec *sc; 79 mod_gnutls_srvconf_rec *sc;
80 conn_rec* c;
75 gnutls_session_t session; 81 gnutls_session_t session;
82
83 apr_status_t input_rc;
76 ap_filter_t *input_filter; 84 ap_filter_t *input_filter;
77 apr_bucket_brigade *input_bb; 85 apr_bucket_brigade *input_bb;
78 apr_read_type_e input_block; 86 apr_read_type_e input_block;
87 ap_input_mode_t input_mode;
88 mod_gnutls_char_buffer_t input_cbuf;
89 char input_buffer[AP_IOBUFSIZE];
90
91 apr_status_t output_rc;
92 ap_filter_t *output_filter;
93 apr_bucket_brigade *output_bb;
94 char output_buffer[AP_IOBUFSIZE];
95 apr_size_t output_blen;
96 apr_size_t output_length;
97
79 int status; 98 int status;
80 int non_https; 99 int non_https;
81} mod_gnutls_handle_t; 100} mod_gnutls_handle_t;
diff --git a/src/gnutls_io.c b/src/gnutls_io.c
index 1cb14a3..659effa 100644
--- a/src/gnutls_io.c
+++ b/src/gnutls_io.c
@@ -19,152 +19,80 @@
19 19
20/** 20/**
21 * Describe how the GnuTLS Filter system works here 21 * Describe how the GnuTLS Filter system works here
22 * - It is basicly the same as what mod_ssl uses in that respect. 22 * - Basicly the same as what mod_ssl does with OpenSSL.
23 *
23 */ 24 */
24 25
25apr_status_t mod_gnutls_filter_input(ap_filter_t * f, 26#define HTTP_ON_HTTPS_PORT \
26 apr_bucket_brigade * bb, 27 "GET /" CRLF
27 ap_input_mode_t mode,
28 apr_read_type_e block,
29 apr_off_t readbytes)
30{
31 apr_bucket *b;
32 apr_status_t status = APR_SUCCESS;
33 mod_gnutls_handle_t *ctxt = (mod_gnutls_handle_t *) f->ctx;
34
35 if (f->c->aborted) {
36 apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
37 APR_BRIGADE_INSERT_TAIL(bb, bucket);
38 return APR_ECONNABORTED;
39 }
40
41#if 0
42 for (b = APR_BRIGADE_FIRST(bb);
43 b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
44 if (APR_BUCKET_IS_EOS(b)) {
45 /* end of connection */
46 }
47 else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
48 == APR_SUCCESS) {
49 /* more data */
50 }
51 }
52#endif
53 return status;
54}
55 28
56#define GNUTLS_HANDSHAKE_ATTEMPTS 10 29#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
30 apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT, \
31 sizeof(HTTP_ON_HTTPS_PORT) - 1, \
32 alloc)
57 33
58apr_status_t mod_gnutls_filter_output(ap_filter_t * f, 34static apr_status_t gnutls_io_filter_error(ap_filter_t * f,
59 apr_bucket_brigade * bb) 35 apr_bucket_brigade * bb,
36 apr_status_t status)
60{ 37{
61 int ret, i;
62 const char *buf = 0;
63 apr_size_t bytes = 0;
64 mod_gnutls_handle_t *ctxt = (mod_gnutls_handle_t *) f->ctx; 38 mod_gnutls_handle_t *ctxt = (mod_gnutls_handle_t *) f->ctx;
65 apr_status_t status = APR_SUCCESS; 39 apr_bucket *bucket;
66 apr_read_type_e rblock = APR_NONBLOCK_READ;
67 40
68 if (f->c->aborted) { 41 switch (status) {
69 apr_brigade_cleanup(bb); 42 case HTTP_BAD_REQUEST:
70 return APR_ECONNABORTED; 43 /* log the situation */
71 } 44 ap_log_error(APLOG_MARK, APLOG_INFO, 0,
45 f->c->base_server,
46 "GnuTLS handshake failed: HTTP spoken on HTTPS port; "
47 "trying to send HTML error page");
72 48
73 if (ctxt->status == 0) { 49 ctxt->status = -1;
74 for (i = GNUTLS_HANDSHAKE_ATTEMPTS; i > 0; i--) {
75 ret = gnutls_handshake(ctxt->session);
76
77 if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
78 continue;
79 }
80
81 if (ret < 0) {
82 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
83 || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
84 ret = gnutls_alert_get(ctxt->session);
85 ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server,
86 "GnuTLS: Hanshake Alert (%d) '%s'.\n", ret,
87 gnutls_alert_get_name(ret));
88 }
89 50
90 if (gnutls_error_is_fatal(ret) != 0) { 51 /* fake the request line */
91 gnutls_deinit(ctxt->session); 52 bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
92 ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server, 53 break;
93 "GnuTLS: Handshake Failed (%d) '%s'", ret,
94 gnutls_strerror(ret));
95 ctxt->status = -1;
96 break;
97 }
98 }
99 else {
100 ctxt->status = 1;
101 break; /* all done with the handshake */
102 }
103 }
104 }
105 54
106 if (ctxt->status < 0) { 55 default:
107 return ap_pass_brigade(f->next, bb); 56 return status;
108 } 57 }
109 58
110 while (!APR_BRIGADE_EMPTY(bb)) { 59 APR_BRIGADE_INSERT_TAIL(bb, bucket);
111 apr_bucket *bucket = APR_BRIGADE_FIRST(bb); 60 bucket = apr_bucket_eos_create(f->c->bucket_alloc);
112 if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) { 61 APR_BRIGADE_INSERT_TAIL(bb, bucket);
113 /** TODO: GnuTLS doesn't have a special flush method? **/
114 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
115 return status;
116 }
117 break;
118 }
119 else if (AP_BUCKET_IS_EOC(bucket)) {
120 gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
121
122 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
123 return status;
124 }
125 break;
126 }
127 else {
128 /* filter output */
129 const char *data;
130 apr_size_t len;
131
132 status = apr_bucket_read(bucket, &data, &len, rblock);
133 62
134 if (APR_STATUS_IS_EAGAIN(status)) { 63 return APR_SUCCESS;
135 rblock = APR_BLOCK_READ; 64}
136 continue; /* and try again with a blocking read. */
137 }
138
139 rblock = APR_NONBLOCK_READ;
140
141 if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
142 break;
143 }
144
145 ret = gnutls_record_send(ctxt->session, data, len);
146 if (ret < 0) {
147 /* error sending output */
148 }
149 else if ((apr_size_t) ret != len) {
150 /* not all of the data was sent. */
151 /* mod_ssl basicly errors out here.. this doesn't seem right? */
152 }
153 else {
154 /* send complete */
155
156 }
157
158 apr_bucket_delete(bucket);
159 65
160 if (status != APR_SUCCESS) { 66static int char_buffer_read(mod_gnutls_char_buffer_t * buffer, char *in,
161 break; 67 int inl)
162 } 68{
69 if (!buffer->length) {
70 return 0;
71 }
163 72
164 } 73 if (buffer->length > inl) {
74 /* we have have enough to fill the caller's buffer */
75 memcpy(in, buffer->value, inl);
76 buffer->value += inl;
77 buffer->length -= inl;
78 }
79 else {
80 /* swallow remainder of the buffer */
81 memcpy(in, buffer->value, buffer->length);
82 inl = buffer->length;
83 buffer->value = NULL;
84 buffer->length = 0;
165 } 85 }
166 86
167 return status; 87 return inl;
88}
89
90static int char_buffer_write(mod_gnutls_char_buffer_t * buffer, char *in,
91 int inl)
92{
93 buffer->value = in;
94 buffer->length = inl;
95 return inl;
168} 96}
169 97
170/** 98/**
@@ -253,16 +181,383 @@ static apr_status_t brigade_consume(apr_bucket_brigade * bb,
253} 181}
254 182
255 183
184static apr_status_t gnutls_io_input_read(mod_gnutls_handle_t * ctxt,
185 char *buf, apr_size_t * len)
186{
187 apr_size_t wanted = *len;
188 apr_size_t bytes = 0;
189 int rc;
190
191 *len = 0;
192
193 /* If we have something leftover from last time, try that first. */
194 if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) {
195 *len = bytes;
196 if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
197 /* We want to rollback this read. */
198 if (ctxt->input_cbuf.length > 0) {
199 ctxt->input_cbuf.value -= bytes;
200 ctxt->input_cbuf.length += bytes;
201 }
202 else {
203 char_buffer_write(&ctxt->input_cbuf, buf, (int) bytes);
204 }
205 return APR_SUCCESS;
206 }
207 /* This could probably be *len == wanted, but be safe from stray
208 * photons.
209 */
210 if (*len >= wanted) {
211 return APR_SUCCESS;
212 }
213 if (ctxt->input_mode == AP_MODE_GETLINE) {
214 if (memchr(buf, APR_ASCII_LF, *len)) {
215 return APR_SUCCESS;
216 }
217 }
218 else {
219 /* Down to a nonblock pattern as we have some data already
220 */
221 ctxt->input_block = APR_NONBLOCK_READ;
222 }
223 }
224
225 while (1) {
226
227 if (ctxt->status < 0) {
228 /* Ensure a non-zero error code is returned */
229 if (ctxt->input_rc == APR_SUCCESS) {
230 ctxt->input_rc = APR_EGENERAL;
231 }
232 break;
233 }
234
235 rc = gnutls_record_recv(ctxt->session, buf + bytes, wanted - bytes);
236
237 if (rc > 0) {
238 *len += rc;
239 if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
240 /* We want to rollback this read. */
241 char_buffer_write(&ctxt->input_cbuf, buf, rc);
242 }
243 return ctxt->input_rc;
244 }
245 else if (rc == 0) {
246 /* If EAGAIN, we will loop given a blocking read,
247 * otherwise consider ourselves at EOF.
248 */
249 if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
250 || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
251 /* Already read something, return APR_SUCCESS instead.
252 * On win32 in particular, but perhaps on other kernels,
253 * a blocking call isn't 'always' blocking.
254 */
255 if (*len > 0) {
256 ctxt->input_rc = APR_SUCCESS;
257 break;
258 }
259 if (ctxt->input_block == APR_NONBLOCK_READ) {
260 break;
261 }
262 }
263 else {
264 if (*len > 0) {
265 ctxt->input_rc = APR_SUCCESS;
266 }
267 else {
268 ctxt->input_rc = APR_EOF;
269 }
270 break;
271 }
272 }
273 else { /* (rc < 0) */
274
275 if (rc == GNUTLS_E_REHANDSHAKE) {
276 /* A client has asked for a new Hankshake. Currently, we don't do it */
277 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc,
278 ctxt->c->base_server,
279 "GnuTLS: Error reading data. Client Requested a New Handshake."
280 " (%d) '%s'", rc, gnutls_strerror(rc));
281 }
282 else {
283 /* Some Other Error. Report it. Die. */
284 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->input_rc,
285 ctxt->c->base_server,
286 "GnuTLS: Error reading data. (%d) '%s'", rc,
287 gnutls_strerror(rc));
288 }
289
290 if (ctxt->input_rc == APR_SUCCESS) {
291 ctxt->input_rc = APR_EGENERAL;
292 }
293 break;
294 }
295 }
296 return ctxt->input_rc;
297}
298
299static apr_status_t gnutls_io_input_getline(mod_gnutls_handle_t * ctxt,
300 char *buf, apr_size_t * len)
301{
302 const char *pos = NULL;
303 apr_status_t status;
304 apr_size_t tmplen = *len, buflen = *len, offset = 0;
305
306 *len = 0;
307
308 while (tmplen > 0) {
309 status = gnutls_io_input_read(ctxt, buf + offset, &tmplen);
310
311 if (status != APR_SUCCESS) {
312 return status;
313 }
314
315 *len += tmplen;
316
317 if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
318 break;
319 }
320
321 offset += tmplen;
322 tmplen = buflen - offset;
323 }
324
325 if (pos) {
326 char *value;
327 int length;
328 apr_size_t bytes = pos - buf;
329
330 bytes += 1;
331 value = buf + bytes;
332 length = *len - bytes;
333
334 char_buffer_write(&ctxt->input_cbuf, value, length);
335
336 *len = bytes;
337 }
338
339 return APR_SUCCESS;
340}
341
342
343#define GNUTLS_HANDSHAKE_ATTEMPTS 10
344
345static void gnutls_do_handshake(mod_gnutls_handle_t * ctxt)
346{
347 int i, ret;
348
349 if (ctxt->status != 0)
350 return;
351
352 for (i = GNUTLS_HANDSHAKE_ATTEMPTS; i > 0; i--) {
353 ret = gnutls_handshake(ctxt->session);
354 if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) {
355 continue;
356 }
357
358 if (ret < 0) {
359 if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
360 || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
361 ret = gnutls_alert_get(ctxt->session);
362 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server,
363 "GnuTLS: Hanshake Alert (%d) '%s'.\n", ret,
364 gnutls_alert_get_name(ret));
365 }
366
367 if (gnutls_error_is_fatal(ret) != 0) {
368 gnutls_deinit(ctxt->session);
369 ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctxt->c->base_server,
370 "GnuTLS: Handshake Failed (%d) '%s'", ret,
371 gnutls_strerror(ret));
372 ctxt->status = -1;
373 return;
374 }
375 }
376 else {
377 ctxt->status = 1;
378 return; /* all done with the handshake */
379 }
380 }
381 ctxt->status = -1;
382 return;
383}
384
385
386apr_status_t mod_gnutls_filter_input(ap_filter_t * f,
387 apr_bucket_brigade * bb,
388 ap_input_mode_t mode,
389 apr_read_type_e block,
390 apr_off_t readbytes)
391{
392 apr_status_t status = APR_SUCCESS;
393 mod_gnutls_handle_t *ctxt = (mod_gnutls_handle_t *) f->ctx;
394 apr_size_t len = sizeof(ctxt->input_buffer);
395
396 if (f->c->aborted) {
397 apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
398 APR_BRIGADE_INSERT_TAIL(bb, bucket);
399 return APR_ECONNABORTED;
400 }
401
402 if (ctxt->status == 0) {
403 gnutls_do_handshake(ctxt);
404 }
405
406 if (ctxt->status < 0) {
407 return ap_get_brigade(f->next, bb, mode, block, readbytes);
408 }
409
410 /* XXX: we don't currently support anything other than these modes. */
411 if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
412 mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
413 return APR_ENOTIMPL;
414 }
415
416 ctxt->input_mode = mode;
417 ctxt->input_block = block;
418
419 if (ctxt->input_mode == AP_MODE_READBYTES ||
420 ctxt->input_mode == AP_MODE_SPECULATIVE) {
421 /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
422 if (readbytes < len) {
423 len = (apr_size_t) readbytes;
424 }
425 status = gnutls_io_input_read(ctxt, ctxt->input_buffer, &len);
426 }
427 else if (ctxt->input_mode == AP_MODE_GETLINE) {
428 status = gnutls_io_input_getline(ctxt, ctxt->input_buffer, &len);
429 }
430 else {
431 /* We have no idea what you are talking about, so return an error. */
432 return APR_ENOTIMPL;
433 }
434
435 if (status != APR_SUCCESS) {
436 return gnutls_io_filter_error(f, bb, status);
437 }
438
439 /* Create a transient bucket out of the decrypted data. */
440 if (len > 0) {
441 apr_bucket *bucket =
442 apr_bucket_transient_create(ctxt->input_buffer, len,
443 f->c->bucket_alloc);
444 APR_BRIGADE_INSERT_TAIL(bb, bucket);
445 }
446
447 return status;
448}
449
450apr_status_t mod_gnutls_filter_output(ap_filter_t * f,
451 apr_bucket_brigade * bb)
452{
453 int ret;
454 mod_gnutls_handle_t *ctxt = (mod_gnutls_handle_t *) f->ctx;
455 apr_status_t status = APR_SUCCESS;
456 apr_read_type_e rblock = APR_NONBLOCK_READ;
457
458 if (f->c->aborted) {
459 apr_brigade_cleanup(bb);
460 return APR_ECONNABORTED;
461 }
462
463 if (ctxt->status == 0) {
464 gnutls_do_handshake(ctxt);
465 }
466
467 if (ctxt->status < 0) {
468 return ap_pass_brigade(f->next, bb);
469 }
470
471 while (!APR_BRIGADE_EMPTY(bb)) {
472 apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
473 if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
474 /** TODO: GnuTLS doesn't have a special flush method? **/
475 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
476 return status;
477 }
478 break;
479 }
480 else if (AP_BUCKET_IS_EOC(bucket)) {
481 gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
482
483 if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
484 return status;
485 }
486 break;
487 }
488 else {
489 /* filter output */
490 const char *data;
491 apr_size_t len;
492
493 status = apr_bucket_read(bucket, &data, &len, rblock);
494
495 if (APR_STATUS_IS_EAGAIN(status)) {
496 rblock = APR_BLOCK_READ;
497 continue; /* and try again with a blocking read. */
498 }
499
500 rblock = APR_NONBLOCK_READ;
501
502 if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
503 break;
504 }
505
506 ret = gnutls_record_send(ctxt->session, data, len);
507
508 if (ret < 0) {
509 /* error sending output */
510 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->output_rc,
511 ctxt->c->base_server,
512 "GnuTLS: Error writing data."
513 " (%d) '%s'", ret, gnutls_strerror(ret));
514 if (ctxt->output_rc == APR_SUCCESS) {
515 ctxt->output_rc = APR_EGENERAL;
516 }
517 }
518 else if ((apr_size_t) ret != len) {
519 /* not all of the data was sent. */
520 /* mod_ssl basicly errors out here.. this doesn't seem right? */
521 ap_log_error(APLOG_MARK, APLOG_INFO, ctxt->output_rc,
522 ctxt->c->base_server,
523 "GnuTLS: failed to write %" APR_SSIZE_T_FMT
524 " of %" APR_SIZE_T_FMT " bytes.",
525 len - (apr_size_t) ret, len);
526 if (ctxt->output_rc == APR_SUCCESS) {
527 ctxt->output_rc = APR_EGENERAL;
528 }
529 }
530
531 apr_bucket_delete(bucket);
532
533 if (ctxt->output_rc != APR_SUCCESS) {
534 break;
535 }
536 }
537 }
538
539 return status;
540}
541
256ssize_t mod_gnutls_transport_read(gnutls_transport_ptr_t ptr, 542ssize_t mod_gnutls_transport_read(gnutls_transport_ptr_t ptr,
257 void *buffer, size_t len) 543 void *buffer, size_t len)
258{ 544{
259 mod_gnutls_handle_t *ctxt = ptr; 545 mod_gnutls_handle_t *ctxt = ptr;
260 apr_status_t rc; 546 apr_status_t rc;
261 apr_size_t in = len; 547 apr_size_t in = len;
548 apr_read_type_e block = ctxt->input_block;
549
550 ctxt->input_rc = APR_SUCCESS;
551
262 /* If Len = 0, we don't do anything. */ 552 /* If Len = 0, we don't do anything. */
263 if (!len) 553 if (!len)
264 return 0; 554 return 0;
265 555
556 if (!ctxt->input_bb) {
557 ctxt->input_rc = APR_EOF;
558 return -1;
559 }
560
266 if (APR_BRIGADE_EMPTY(ctxt->input_bb)) { 561 if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
267 562
268 rc = ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb, 563 rc = ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
@@ -283,13 +578,64 @@ ssize_t mod_gnutls_transport_read(gnutls_transport_ptr_t ptr,
283 } 578 }
284 } 579 }
285 580
286// brigade_consume(ctxt->input_bb, ctxt->input_block, buffer, &len); 581 ctxt->input_rc = brigade_consume(ctxt->input_bb, block, buffer, &len);
287 582
583 if (ctxt->input_rc == APR_SUCCESS) {
584 return (ssize_t) len;
585 }
288 586
289 ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb, 587 if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
290 AP_MODE_READBYTES, ctxt->input_block, len); 588 || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
589 return (ssize_t) len;
590 }
291 591
292 return len; 592 /* Unexpected errors and APR_EOF clean out the brigade.
593 * Subsequent calls will return APR_EOF.
594 */
595 apr_brigade_cleanup(ctxt->input_bb);
596 ctxt->input_bb = NULL;
597
598 if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) {
599 /* Provide the results of this read pass,
600 * without resetting the BIO retry_read flag
601 */
602 return (ssize_t) len;
603 }
604
605 return -1;
606}
607
608
609static ssize_t write_flush(mod_gnutls_handle_t * ctxt)
610{
611 apr_bucket *e;
612
613 if (!(ctxt->output_blen || ctxt->output_length)) {
614 ctxt->output_rc = APR_SUCCESS;
615 return 1;
616 }
617
618 if (ctxt->output_blen) {
619 e = apr_bucket_transient_create(ctxt->output_buffer,
620 ctxt->output_blen,
621 ctxt->output_bb->bucket_alloc);
622 /* we filled this buffer first so add it to the
623 * head of the brigade
624 */
625 APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e);
626 ctxt->output_blen = 0;
627 }
628
629 ctxt->output_length = 0;
630 e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc);
631 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
632
633 ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next,
634 ctxt->output_bb);
635 /* create new brigade ready for next time through */
636 ctxt->output_bb =
637 apr_brigade_create(ctxt->c->pool, ctxt->c->bucket_alloc);
638 return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1;
293} 639}
294 640
295ssize_t mod_gnutls_transport_write(gnutls_transport_ptr_t ptr, 641ssize_t mod_gnutls_transport_write(gnutls_transport_ptr_t ptr,
@@ -297,11 +643,34 @@ ssize_t mod_gnutls_transport_write(gnutls_transport_ptr_t ptr,
297{ 643{
298 mod_gnutls_handle_t *ctxt = ptr; 644 mod_gnutls_handle_t *ctxt = ptr;
299 645
300// apr_bucket *bucket = apr_bucket_transient_create(in, inl, 646 if (!ctxt->output_length
301// outctx->bb-> 647 && (len + ctxt->output_blen < sizeof(ctxt->output_buffer))) {
302// bucket_alloc); 648 /* the first two SSL_writes (of 1024 and 261 bytes)
649 * need to be in the same packet (vec[0].iov_base)
650 */
651 /* XXX: could use apr_brigade_write() to make code look cleaner
652 * but this way we avoid the malloc(APR_BUCKET_BUFF_SIZE)
653 * and free() of it later
654 */
655 memcpy(&ctxt->output_buffer[ctxt->output_blen], buffer, len);
656 ctxt->output_blen += len;
657 }
658 else {
659 /* pass along the encrypted data
660 * need to flush since we're using SSL's malloc-ed buffer
661 * which will be overwritten once we leave here
662 */
663 apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
664 ctxt->output_bb->
665 bucket_alloc);
666
667 ctxt->output_length += len;
668 APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
303 669
304 // outctx->length += inl; 670 if (write_flush(ctxt) < 0) {
305 //APR_BRIGADE_INSERT_TAIL(outctx->bb, bucket); 671 return -1;
306 return 0; 672 }
673 }
674
675 return len;
307} 676}
diff --git a/src/mod_gnutls.c b/src/mod_gnutls.c
index c34da5a..d4f1f16 100644
--- a/src/mod_gnutls.c
+++ b/src/mod_gnutls.c
@@ -57,8 +57,8 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
57 57
58 58
59 /* TODO: Should we regenerate these after X requests / X time ? */ 59 /* TODO: Should we regenerate these after X requests / X time ? */
60// gnutls_dh_params_init(&dh_params); 60 gnutls_dh_params_init(&dh_params);
61// gnutls_dh_params_generate2(dh_params, DH_BITS); 61 gnutls_dh_params_generate2(dh_params, DH_BITS);
62// gnutls_rsa_params_init(&rsa_params); 62// gnutls_rsa_params_init(&rsa_params);
63// gnutls_rsa_params_generate2(rsa_params, RSA_BITS); 63// gnutls_rsa_params_generate2(rsa_params, RSA_BITS);
64 64
@@ -70,7 +70,7 @@ static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
70 sc->key_file, 70 sc->key_file,
71 GNUTLS_X509_FMT_PEM); 71 GNUTLS_X509_FMT_PEM);
72// gnutls_certificate_set_rsa_export_params(sc->certs, rsa_params); 72// gnutls_certificate_set_rsa_export_params(sc->certs, rsa_params);
73// gnutls_certificate_set_dh_params(sc->certs, dh_params); 73 gnutls_certificate_set_dh_params(sc->certs, dh_params);
74 } 74 }
75 else if (sc->enabled == GNUTLS_ENABLED_TRUE) { 75 else if (sc->enabled == GNUTLS_ENABLED_TRUE) {
76 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, 76 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
@@ -112,7 +112,7 @@ static apr_port_t mod_gnutls_hook_default_port(const request_rec * r)
112 return 443; 112 return 443;
113} 113}
114 114
115static int mod_gnutls_hook_pre_connection(conn_rec * c, void *csd) 115static mod_gnutls_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c)
116{ 116{
117 mod_gnutls_handle_t *ctxt; 117 mod_gnutls_handle_t *ctxt;
118 mod_gnutls_srvconf_rec *sc = 118 mod_gnutls_srvconf_rec *sc =
@@ -120,14 +120,20 @@ static int mod_gnutls_hook_pre_connection(conn_rec * c, void *csd)
120 module_config, 120 module_config,
121 &gnutls_module); 121 &gnutls_module);
122 122
123 if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) { 123 ctxt = apr_pcalloc(pool, sizeof(*ctxt));
124 return DECLINED; 124 ctxt->c = c;
125 }
126
127 ctxt = apr_pcalloc(c->pool, sizeof(*ctxt));
128
129 ctxt->sc = sc; 125 ctxt->sc = sc;
130 ctxt->status = 0; 126 ctxt->status = 0;
127
128 ctxt->input_rc = APR_SUCCESS;
129 ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
130 ctxt->input_cbuf.length = 0;
131
132 ctxt->output_rc = APR_SUCCESS;
133 ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
134 ctxt->output_blen = 0;
135 ctxt->output_length = 0;
136
131 gnutls_init(&ctxt->session, GNUTLS_SERVER); 137 gnutls_init(&ctxt->session, GNUTLS_SERVER);
132 138
133 gnutls_cipher_set_priority(ctxt->session, sc->ciphers); 139 gnutls_cipher_set_priority(ctxt->session, sc->ciphers);
@@ -145,6 +151,22 @@ static int mod_gnutls_hook_pre_connection(conn_rec * c, void *csd)
145 151
146// gnutls_dh_set_prime_bits(ctxt->session, DH_BITS); 152// gnutls_dh_set_prime_bits(ctxt->session, DH_BITS);
147 153
154 return ctxt;
155}
156
157static int mod_gnutls_hook_pre_connection(conn_rec * c, void *csd)
158{
159 mod_gnutls_handle_t *ctxt;
160 mod_gnutls_srvconf_rec *sc =
161 (mod_gnutls_srvconf_rec *) ap_get_module_config(c->base_server->
162 module_config,
163 &gnutls_module);
164
165 if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) {
166 return DECLINED;
167 }
168
169 ctxt = create_gnutls_handle(c->pool, c);
148 170
149 ap_set_module_config(c->conn_config, &gnutls_module, ctxt); 171 ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
150 172
@@ -153,8 +175,8 @@ static int mod_gnutls_hook_pre_connection(conn_rec * c, void *csd)
153 gnutls_transport_set_push_function(ctxt->session, 175 gnutls_transport_set_push_function(ctxt->session,
154 mod_gnutls_transport_write); 176 mod_gnutls_transport_write);
155 gnutls_transport_set_ptr(ctxt->session, ctxt); 177 gnutls_transport_set_ptr(ctxt->session, ctxt);
156 ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c); 178 ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c);
157 ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c); 179 ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c);
158 180
159 return OK; 181 return OK;
160} 182}