source: mod_gnutls/src/gnutls_io.c @ 401a0de

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 401a0de was 401a0de, checked in by Thomas Klute <thomas2.klute@…>, 4 years ago

Close TLS session on EOF in input filter

If the input filter receives an EOF, the connection should be closed
and it resources released. The code for this can be shared with the
session shutdown code in the output filter, so it is moved to a
separate function.

  • Property mode set to 100644
File size: 26.6 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *  Copyright 2015 Thomas Klute
6 *
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 *
19 */
20
21#include "mod_gnutls.h"
22
23#ifdef APLOG_USE_MODULE
24APLOG_USE_MODULE(gnutls);
25#endif
26
27/**
28 * Describe how the GnuTLS Filter system works here
29 *  - Basicly the same as what mod_ssl does with OpenSSL.
30 *
31 */
32
33#define HTTP_ON_HTTPS_PORT \
34    "GET /" CRLF
35
36#define HTTP_ON_HTTPS_PORT_BUCKET(alloc) \
37    apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT, \
38                               sizeof(HTTP_ON_HTTPS_PORT) - 1, \
39                               alloc)
40
41#define IS_PROXY_STR(c) \
42    ((c->is_proxy == GNUTLS_ENABLED_TRUE) ? "proxy " : "")
43
44/**
45 * Convert APR_EINTR or APR_EAGAIN to the match raw error code. Needed
46 * to pass the status on to GnuTLS from the pull function.
47 */
48#define EAI_APR_TO_RAW(s) (APR_STATUS_IS_EAGAIN(s) ? EAGAIN : EINTR)
49
50
51
52static apr_status_t gnutls_io_filter_error(ap_filter_t * f,
53        apr_bucket_brigade * bb,
54        apr_status_t status) {
55    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
56    apr_bucket *bucket;
57
58    switch (status) {
59    case HTTP_BAD_REQUEST:
60        /* log the situation */
61        ap_log_error(APLOG_MARK, APLOG_INFO, 0,
62                     f->c->base_server,
63                     "GnuTLS handshake failed: HTTP spoken on HTTPS port; "
64                     "trying to send HTML error page");
65        mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
66            ap_get_module_config(f->c->base_server->module_config,
67                                 &gnutls_module);
68        ctxt->status = -1;
69        sc->non_ssl_request = 1;
70
71        /* fake the request line */
72        bucket = HTTP_ON_HTTPS_PORT_BUCKET(f->c->bucket_alloc);
73        break;
74
75    default:
76        return status;
77    }
78
79    APR_BRIGADE_INSERT_TAIL(bb, bucket);
80    bucket = apr_bucket_eos_create(f->c->bucket_alloc);
81    APR_BRIGADE_INSERT_TAIL(bb, bucket);
82
83    return APR_SUCCESS;
84}
85
86static int char_buffer_read(mgs_char_buffer_t * buffer, char *in, int inl) {
87    if (!buffer->length) {
88        return 0;
89    }
90
91    if (buffer->length > inl) {
92        /* we have have enough to fill the caller's buffer */
93        memmove(in, buffer->value, inl);
94        buffer->value += inl;
95        buffer->length -= inl;
96    } else {
97        /* swallow remainder of the buffer */
98        memmove(in, buffer->value, buffer->length);
99        inl = buffer->length;
100        buffer->value = NULL;
101        buffer->length = 0;
102    }
103
104    return inl;
105}
106
107static int char_buffer_write(mgs_char_buffer_t * buffer, char *in, int inl) {
108    buffer->value = in;
109    buffer->length = inl;
110    return inl;
111}
112
113/**
114 * From mod_ssl / ssl_engine_io.c
115 * This function will read from a brigade and discard the read buckets as it
116 * proceeds.  It will read at most *len bytes.
117 */
118static apr_status_t brigade_consume(apr_bucket_brigade * bb,
119        apr_read_type_e block,
120        char *c, apr_size_t * len) {
121    apr_size_t actual = 0;
122    apr_status_t status = APR_SUCCESS;
123
124    while (!APR_BRIGADE_EMPTY(bb)) {
125        apr_bucket *b = APR_BRIGADE_FIRST(bb);
126        const char *str;
127        apr_size_t str_len;
128        apr_size_t consume;
129
130        /* Justin points out this is an http-ism that might
131         * not fit if brigade_consume is added to APR.  Perhaps
132         * apr_bucket_read(eos_bucket) should return APR_EOF?
133         * Then this becomes mainline instead of a one-off.
134         */
135        if (APR_BUCKET_IS_EOS(b)) {
136            status = APR_EOF;
137            break;
138        }
139
140        /* The reason I'm not offering brigade_consume yet
141         * across to apr-util is that the following call
142         * illustrates how borked that API really is.  For
143         * this sort of case (caller provided buffer) it
144         * would be much more trivial for apr_bucket_consume
145         * to do all the work that follows, based on the
146         * particular characteristics of the bucket we are
147         * consuming here.
148         */
149        status = apr_bucket_read(b, &str, &str_len, block);
150
151        if (status != APR_SUCCESS) {
152            if (APR_STATUS_IS_EOF(status)) {
153                /* This stream bucket was consumed */
154                apr_bucket_delete(b);
155                continue;
156            }
157            break;
158        }
159
160        if (str_len > 0) {
161            /* Do not block once some data has been consumed */
162            block = APR_NONBLOCK_READ;
163
164            /* Assure we don't overflow. */
165            consume =
166                    (str_len + actual >
167                    *len) ? *len - actual : str_len;
168
169            memcpy(c, str, consume);
170
171            c += consume;
172            actual += consume;
173
174            if (consume >= b->length) {
175                /* This physical bucket was consumed */
176                apr_bucket_delete(b);
177            } else {
178                /* Only part of this physical bucket was consumed */
179                b->start += consume;
180                b->length -= consume;
181            }
182        } else if (b->length == 0) {
183            apr_bucket_delete(b);
184        }
185
186        /* This could probably be actual == *len, but be safe from stray
187         * photons. */
188        if (actual >= *len) {
189            break;
190        }
191    }
192
193    *len = actual;
194    return status;
195}
196
197static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
198        char *buf, apr_size_t * len)
199{
200    apr_size_t wanted = *len;
201    apr_size_t bytes = 0;
202    int rc;
203
204    *len = 0;
205
206    /* If we have something leftover from last time, try that first. */
207    if ((bytes = char_buffer_read(&ctxt->input_cbuf, buf, wanted))) {
208        *len = bytes;
209        if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
210            /* We want to rollback this read. */
211            if (ctxt->input_cbuf.length > 0) {
212                ctxt->input_cbuf.value -= bytes;
213                ctxt->input_cbuf.length += bytes;
214            } else {
215                char_buffer_write(&ctxt->input_cbuf, buf,
216                        (int) bytes);
217            }
218            return APR_SUCCESS;
219        }
220        /* This could probably be *len == wanted, but be safe from stray
221         * photons.
222         */
223        if (*len >= wanted) {
224            return APR_SUCCESS;
225        }
226        if (ctxt->input_mode == AP_MODE_GETLINE) {
227            if (memchr(buf, APR_ASCII_LF, *len)) {
228                return APR_SUCCESS;
229            }
230        } else {
231            /* Down to a nonblock pattern as we have some data already
232             */
233            ctxt->input_block = APR_NONBLOCK_READ;
234        }
235    }
236
237    if (ctxt->session == NULL) {
238        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
239                      "%s: GnuTLS session is NULL!", __func__);
240        return APR_EGENERAL;
241    }
242
243    while (1)
244    {
245        rc = gnutls_record_recv(ctxt->session, buf + bytes, wanted - bytes);
246
247        if (rc == GNUTLS_E_INTERRUPTED)
248            ctxt->input_rc = APR_EINTR;
249        else if (rc == GNUTLS_E_AGAIN)
250            ctxt->input_rc = APR_EAGAIN;
251
252        if (rc > 0) {
253            *len += rc;
254            if (ctxt->input_mode == AP_MODE_SPECULATIVE) {
255                /* We want to rollback this read. */
256                char_buffer_write(&ctxt->input_cbuf, buf,
257                        rc);
258            }
259            return ctxt->input_rc;
260        } else if (rc == 0) {
261            /* If EAGAIN, we will loop given a blocking read,
262             * otherwise consider ourselves at EOF.
263             */
264            if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
265                    || APR_STATUS_IS_EINTR(ctxt->input_rc)) {
266                /* Already read something, return APR_SUCCESS instead.
267                 * On win32 in particular, but perhaps on other kernels,
268                 * a blocking call isn't 'always' blocking.
269                 */
270                if (*len > 0) {
271                    ctxt->input_rc = APR_SUCCESS;
272                    break;
273                }
274                if (ctxt->input_block == APR_NONBLOCK_READ) {
275                    break;
276                }
277            } else {
278                if (*len > 0) {
279                    ctxt->input_rc = APR_SUCCESS;
280                } else {
281                    ctxt->input_rc = APR_EOF;
282                }
283                break;
284            }
285        } else { /* (rc < 0) */
286
287            if (rc == GNUTLS_E_REHANDSHAKE) {
288                /* A client has asked for a new Hankshake. Currently, we don't do it */
289                ap_log_cerror(APLOG_MARK, APLOG_INFO,
290                        ctxt->input_rc,
291                        ctxt->c,
292                        "GnuTLS: Error reading data. Client Requested a New Handshake."
293                        " (%d) '%s'", rc,
294                        gnutls_strerror(rc));
295            } else if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
296                rc = gnutls_alert_get(ctxt->session);
297                ap_log_cerror(APLOG_MARK, APLOG_INFO,
298                        ctxt->input_rc,
299                        ctxt->c,
300                        "GnuTLS: Warning Alert From Client: "
301                        " (%d) '%s'", rc,
302                        gnutls_alert_get_name(rc));
303            } else if (rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
304                rc = gnutls_alert_get(ctxt->session);
305                ap_log_cerror(APLOG_MARK, APLOG_INFO,
306                        ctxt->input_rc,
307                        ctxt->c,
308                        "GnuTLS: Fatal Alert From Client: "
309                        "(%d) '%s'", rc,
310                        gnutls_alert_get_name(rc));
311                ctxt->input_rc = APR_EGENERAL;
312                break;
313            } else {
314                /* Some Other Error. Report it. Die. */
315                if (gnutls_error_is_fatal(rc)) {
316                    ap_log_cerror(APLOG_MARK,
317                            APLOG_INFO,
318                            ctxt->input_rc,
319                            ctxt->c,
320                            "GnuTLS: Error reading data. (%d) '%s'",
321                            rc,
322                            gnutls_strerror(rc));
323                } else if (*len > 0) {
324                    ctxt->input_rc = APR_SUCCESS;
325                    break;
326                }
327            }
328
329            if (ctxt->input_rc == APR_SUCCESS) {
330                ap_log_cerror(APLOG_MARK, APLOG_INFO, ctxt->input_rc, ctxt->c,
331                              "%s: GnuTLS error: %s (%d)",
332                              __func__, gnutls_strerror(rc), rc);
333                ctxt->input_rc = APR_EGENERAL;
334            }
335            break;
336        }
337    }
338    return ctxt->input_rc;
339}
340
341static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt,
342        char *buf, apr_size_t * len) {
343    const char *pos = NULL;
344    apr_status_t status;
345    apr_size_t tmplen = *len, buflen = *len, offset = 0;
346
347    *len = 0;
348
349    while (tmplen > 0) {
350        status = gnutls_io_input_read(ctxt, buf + offset, &tmplen);
351
352        if (status != APR_SUCCESS) {
353            return status;
354        }
355
356        *len += tmplen;
357
358        if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
359            break;
360        }
361
362        offset += tmplen;
363        tmplen = buflen - offset;
364    }
365
366    if (pos) {
367        char *value;
368        int length;
369        apr_size_t bytes = pos - buf;
370
371        bytes += 1;
372        value = buf + bytes;
373        length = *len - bytes;
374
375        char_buffer_write(&ctxt->input_cbuf, value, length);
376
377        *len = bytes;
378    }
379
380    return APR_SUCCESS;
381}
382
383#define HANDSHAKE_MAX_TRIES 1024
384
385static int gnutls_do_handshake(mgs_handle_t * ctxt) {
386    int ret;
387    int errcode;
388    int maxtries = HANDSHAKE_MAX_TRIES;
389
390    if (ctxt->status != 0 || ctxt->session == NULL) {
391        return -1;
392    }
393
394tryagain:
395    do {
396        ret = gnutls_handshake(ctxt->session);
397        maxtries--;
398    } while ((ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)
399            && maxtries > 0);
400
401    if (maxtries < 1) {
402        ctxt->status = -1;
403#if USING_2_1_RECENT
404        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
405                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
406#else
407        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
408                ctxt->c->base_server,
409                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
410#endif
411        if (ctxt->session) {
412            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
413                    gnutls_error_to_alert
414                    (GNUTLS_E_INTERNAL_ERROR, NULL));
415            gnutls_deinit(ctxt->session);
416        }
417        ctxt->session = NULL;
418        return -1;
419    }
420
421    if (ret < 0) {
422        if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED
423                || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
424            errcode = gnutls_alert_get(ctxt->session);
425            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
426                    ctxt->c->base_server,
427                    "GnuTLS: Handshake Alert (%d) '%s'.",
428                    errcode,
429                    gnutls_alert_get_name(errcode));
430        }
431
432        if (!gnutls_error_is_fatal(ret)) {
433            ap_log_error(APLOG_MARK, APLOG_INFO, 0,
434                    ctxt->c->base_server,
435                    "GnuTLS: Non-Fatal Handshake Error: (%d) '%s'",
436                    ret, gnutls_strerror(ret));
437            goto tryagain;
438        }
439#if USING_2_1_RECENT
440        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
441                "GnuTLS: Handshake Failed (%d) '%s'", ret,
442                gnutls_strerror(ret));
443#else
444        ap_log_error(APLOG_MARK, APLOG_INFO, 0,
445                ctxt->c->base_server,
446                "GnuTLS: Handshake Failed (%d) '%s'", ret,
447                gnutls_strerror(ret));
448#endif
449        ctxt->status = -1;
450        if (ctxt->session) {
451            gnutls_alert_send(ctxt->session, GNUTLS_AL_FATAL,
452                    gnutls_error_to_alert(ret,
453                    NULL));
454            gnutls_deinit(ctxt->session);
455        }
456        ctxt->session = NULL;
457        return ret;
458    } else {
459        /* all done with the handshake */
460        ctxt->status = 1;
461        /* If the session was resumed, we did not set the correct
462         * server_rec in ctxt->sc.  Go Find it. (ick!)
463         */
464        if (gnutls_session_is_resumed(ctxt->session)) {
465            mgs_srvconf_rec *sc;
466            sc = mgs_find_sni_server(ctxt->session);
467            if (sc) {
468                ctxt->sc = sc;
469            }
470        }
471        return GNUTLS_E_SUCCESS;
472    }
473}
474
475int mgs_rehandshake(mgs_handle_t * ctxt) {
476    int rv;
477
478    if (ctxt->session == NULL)
479        return -1;
480
481    rv = gnutls_rehandshake(ctxt->session);
482
483    if (rv != 0) {
484        /* the client did not want to rehandshake. goodbye */
485        ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
486                ctxt->c->base_server,
487                "GnuTLS: Client Refused Rehandshake request.");
488        return -1;
489    }
490
491    ctxt->status = 0;
492
493    rv = gnutls_do_handshake(ctxt);
494
495    return rv;
496}
497
498
499
500/**
501 * Close the TLS session associated with the given connection
502 * structure and free its resources
503 */
504static int mgs_bye(mgs_handle_t* ctxt)
505{
506    int ret = GNUTLS_E_SUCCESS;
507    /* End Of Connection */
508    if (ctxt->session != NULL)
509    {
510        /* Try A Clean Shutdown */
511        do {
512            ret = gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
513        } while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
514        if (ret != GNUTLS_E_SUCCESS)
515            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
516                          "%s: Error while closing TLS %sconnection: "
517                          "'%s' (%d)",
518                          __func__, IS_PROXY_STR(ctxt),
519                          gnutls_strerror(ret), (int) ret);
520        else
521            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
522                          "%s: TLS %sconnection closed.",
523                          __func__, IS_PROXY_STR(ctxt));
524        /* De-Initialize Session */
525        gnutls_deinit(ctxt->session);
526        ctxt->session = NULL;
527    }
528    return ret;
529}
530
531
532
533apr_status_t mgs_filter_input(ap_filter_t * f,
534        apr_bucket_brigade * bb,
535        ap_input_mode_t mode,
536        apr_read_type_e block, apr_off_t readbytes)
537{
538    apr_status_t status = APR_SUCCESS;
539    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
540    apr_size_t len = sizeof (ctxt->input_buffer);
541
542    if (f->c->aborted) {
543        apr_bucket *bucket =
544                apr_bucket_eos_create(f->c->bucket_alloc);
545        APR_BRIGADE_INSERT_TAIL(bb, bucket);
546        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
547                      "%s: %sconnection aborted",
548                      __func__, IS_PROXY_STR(ctxt));
549        return APR_ECONNABORTED;
550    }
551
552    if (ctxt->status == 0) {
553        int ret = gnutls_do_handshake(ctxt);
554        if (ret == GNUTLS_E_SUCCESS)
555            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
556                          "%s: TLS %sconnection opened.",
557                          __func__, IS_PROXY_STR(ctxt));
558    }
559
560    if (ctxt->status < 0) {
561        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
562                      "%s %s: ap_get_brigade", __func__, IS_PROXY_STR(ctxt));
563        return ap_get_brigade(f->next, bb, mode, block, readbytes);
564    }
565
566    /* XXX: we don't currently support anything other than these modes. */
567    if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE &&
568            mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) {
569        return APR_ENOTIMPL;
570    }
571
572    ctxt->input_mode = mode;
573    ctxt->input_block = block;
574
575    if (ctxt->input_mode == AP_MODE_READBYTES ||
576            ctxt->input_mode == AP_MODE_SPECULATIVE) {
577        if (readbytes < 0) {
578            /* you're asking us to speculatively read a negative number of bytes! */
579            return APR_ENOTIMPL;
580        }
581        /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
582        if ((apr_size_t) readbytes < len) {
583            len = (apr_size_t) readbytes;
584        }
585        status =
586                gnutls_io_input_read(ctxt, ctxt->input_buffer, &len);
587    } else if (ctxt->input_mode == AP_MODE_GETLINE) {
588        status =
589                gnutls_io_input_getline(ctxt, ctxt->input_buffer,
590                &len);
591    } else {
592        /* We have no idea what you are talking about, so return an error. */
593        return APR_ENOTIMPL;
594    }
595
596    if (status != APR_SUCCESS)
597    {
598        /* no data for nonblocking read, return APR_EAGAIN */
599        if ((block == APR_NONBLOCK_READ) && (status == APR_EINTR))
600            return APR_EAGAIN;
601
602        /* Close TLS session and free resources on EOF,
603         * gnutls_io_filter_error will add an EOS bucket */
604        if (status == APR_EOF)
605            mgs_bye(ctxt);
606
607        return gnutls_io_filter_error(f, bb, status);
608    }
609
610    /* Create a transient bucket out of the decrypted data. */
611    if (len > 0) {
612        apr_bucket *bucket =
613                apr_bucket_transient_create(ctxt->input_buffer, len,
614                f->c->bucket_alloc);
615        APR_BRIGADE_INSERT_TAIL(bb, bucket);
616    }
617
618    return status;
619}
620
621static ssize_t write_flush(mgs_handle_t * ctxt) {
622    apr_bucket *e;
623
624    if (!(ctxt->output_blen || ctxt->output_length)) {
625        ctxt->output_rc = APR_SUCCESS;
626        return 1;
627    }
628
629    if (ctxt->output_blen) {
630        e = apr_bucket_transient_create(ctxt->output_buffer,
631                ctxt->output_blen,
632                ctxt->output_bb->
633                bucket_alloc);
634        /* we filled this buffer first so add it to the
635         *               * head of the brigade
636         *                               */
637        APR_BRIGADE_INSERT_HEAD(ctxt->output_bb, e);
638        ctxt->output_blen = 0;
639    }
640
641    ctxt->output_length = 0;
642    e = apr_bucket_flush_create(ctxt->output_bb->bucket_alloc);
643    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, e);
644
645    ctxt->output_rc = ap_pass_brigade(ctxt->output_filter->next,
646            ctxt->output_bb);
647    /* clear the brigade to be ready for next time */
648    apr_brigade_cleanup(ctxt->output_bb);
649
650    return (ctxt->output_rc == APR_SUCCESS) ? 1 : -1;
651}
652
653apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket_brigade * bb) {
654    int ret;
655    mgs_handle_t *ctxt = (mgs_handle_t *) f->ctx;
656    apr_status_t status = APR_SUCCESS;
657    apr_read_type_e rblock = APR_NONBLOCK_READ;
658
659    if (f->c->aborted) {
660        apr_brigade_cleanup(bb);
661        return APR_ECONNABORTED;
662    }
663
664    if (ctxt->status == 0) {
665        ret = gnutls_do_handshake(ctxt);
666        if (ret == GNUTLS_E_SUCCESS)
667            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
668                          "%s: TLS %sconnection opened.",
669                          __func__, IS_PROXY_STR(ctxt));
670    }
671
672    if (ctxt->status < 0) {
673        return ap_pass_brigade(f->next, bb);
674    }
675
676    while (!APR_BRIGADE_EMPTY(bb)) {
677        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
678
679        if (APR_BUCKET_IS_EOS(bucket)) {
680            return ap_pass_brigade(f->next, bb);
681        } else if (APR_BUCKET_IS_FLUSH(bucket)) {
682            /* Try Flush */
683            if (write_flush(ctxt) < 0) {
684                /* Flush Error */
685                return ctxt->output_rc;
686            }
687            /* cleanup! */
688            apr_bucket_delete(bucket);
689        } else if (AP_BUCKET_IS_EOC(bucket)) {
690            /* End Of Connection, close TLS session and free
691             * resources */
692            mgs_bye(ctxt);
693            /* cleanup! */
694            apr_bucket_delete(bucket);
695            /* Pass next brigade! */
696            return ap_pass_brigade(f->next, bb);
697        } else {
698            /* filter output */
699            const char *data;
700            apr_size_t len;
701
702            status = apr_bucket_read(bucket, &data, &len, rblock);
703
704            if (APR_STATUS_IS_EAGAIN(status)) {
705                /* No data available so Flush! */
706                if (write_flush(ctxt) < 0) {
707                    return ctxt->output_rc;
708                }
709                /* Try again with a blocking read. */
710                rblock = APR_BLOCK_READ;
711                continue;
712            }
713
714            rblock = APR_NONBLOCK_READ;
715
716            if (!APR_STATUS_IS_EOF(status)
717                    && (status != APR_SUCCESS)) {
718                return status;
719            }
720
721            if (len > 0) {
722
723                if (ctxt->session == NULL) {
724                    ret = GNUTLS_E_INVALID_REQUEST;
725                } else {
726                    do {
727                        ret =
728                                gnutls_record_send
729                                (ctxt->session, data,
730                                len);
731                    } while (ret == GNUTLS_E_INTERRUPTED
732                            || ret == GNUTLS_E_AGAIN);
733                }
734
735                if (ret < 0) {
736                    /* error sending output */
737                    ap_log_error(APLOG_MARK,
738                            APLOG_INFO,
739                            ctxt->output_rc,
740                            ctxt->c->base_server,
741                            "GnuTLS: Error writing data."
742                            " (%d) '%s'",
743                            (int) ret,
744                            gnutls_strerror(ret));
745                    if (ctxt->output_rc == APR_SUCCESS) {
746                        ctxt->output_rc =
747                                APR_EGENERAL;
748                        return ctxt->output_rc;
749                    }
750                } else if ((apr_size_t)(ret) != len) {
751                    /* we know the above cast is OK because len > 0 and ret >= 0 */
752                    /* Not able to send the entire bucket,
753                       split it and send it again. */
754                    apr_bucket_split(bucket, ret);
755                }
756            }
757
758            apr_bucket_delete(bucket);
759        }
760    }
761
762    return status;
763}
764
765/**
766 * Pull function for GnuTLS
767 */
768ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
769                           void *buffer, size_t len)
770{
771    mgs_handle_t *ctxt = ptr;
772    apr_status_t rc;
773    apr_size_t in = len;
774    apr_read_type_e block = ctxt->input_block;
775
776    ctxt->input_rc = APR_SUCCESS;
777
778    /* If Len = 0, we don't do anything. */
779    if (!len || buffer == NULL) {
780        return 0;
781    }
782    if (!ctxt->input_bb) {
783        ctxt->input_rc = APR_EOF;
784        return -1;
785    }
786
787    if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
788
789        rc = ap_get_brigade(ctxt->input_filter->next,
790                ctxt->input_bb, AP_MODE_READBYTES,
791                ctxt->input_block, in);
792
793        /* Not a problem, there was simply no data ready yet.
794         */
795        if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
796            || (rc == APR_SUCCESS
797                && APR_BRIGADE_EMPTY(ctxt->input_bb)))
798        {
799            if (APR_STATUS_IS_EOF(ctxt->input_rc))
800            {
801                return 0;
802            }
803            else
804            {
805                if (ctxt->session)
806                    gnutls_transport_set_errno(ctxt->session,
807                                               EAI_APR_TO_RAW(ctxt->input_rc));
808                return -1;
809            }
810        }
811
812        if (rc != APR_SUCCESS) {
813            /* Unexpected errors discard the brigade */
814            apr_brigade_cleanup(ctxt->input_bb);
815            ctxt->input_bb = NULL;
816            return -1;
817        }
818    }
819
820    ctxt->input_rc =
821            brigade_consume(ctxt->input_bb, block, buffer, &len);
822
823    if (ctxt->input_rc == APR_SUCCESS) {
824        return (ssize_t) len;
825    }
826
827    if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
828        || APR_STATUS_IS_EINTR(ctxt->input_rc))
829    {
830        if (len == 0)
831        {
832            if (ctxt->session)
833                gnutls_transport_set_errno(ctxt->session,
834                                           EAI_APR_TO_RAW(ctxt->input_rc));
835            return -1;
836        }
837
838        return (ssize_t) len;
839    }
840
841    /* Unexpected errors and APR_EOF clean out the brigade.
842     * Subsequent calls will return APR_EOF.
843     */
844    apr_brigade_cleanup(ctxt->input_bb);
845    ctxt->input_bb = NULL;
846
847    if (APR_STATUS_IS_EOF(ctxt->input_rc) && len) {
848        /* Provide the results of this read pass,
849         * without resetting the BIO retry_read flag
850         */
851        return (ssize_t) len;
852    }
853
854    return -1;
855}
856
857ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr,
858        const void *buffer, size_t len) {
859    mgs_handle_t *ctxt = ptr;
860
861    /* pass along the encrypted data
862     * need to flush since we're using SSL's malloc-ed buffer
863     * which will be overwritten once we leave here
864     */
865    apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
866            ctxt->output_bb->
867            bucket_alloc);
868    ctxt->output_length += len;
869    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
870
871    if (write_flush(ctxt) < 0) {
872        return -1;
873    }
874    return len;
875}
Note: See TracBrowser for help on using the repository browser.