source: mod_gnutls/src/gnutls_io.c @ eda8686

debian/masterdebian/stretch-backportsjessie-backportsproxy-ticketupstream
Last change on this file since eda8686 was 19f2719, checked in by Thomas Klute <thomas2.klute@…>, 4 years ago

Update comments and formating in push/pull functions

  • Property mode set to 100644
File size: 28.0 KB
RevLine 
[fcb122d]1/**
2 *  Copyright 2004-2005 Paul Querna
[e183628]3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
[e391197]5 *  Copyright 2015 Thomas Klute
[7e2b223]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
[55dc3f0]23#ifdef APLOG_USE_MODULE
24APLOG_USE_MODULE(gnutls);
25#endif
26
[7e2b223]27/**
[671b64f]28 * Describe how the GnuTLS Filter system works here
[dae0aec]29 *  - Basicly the same as what mod_ssl does with OpenSSL.
30 *
[7e2b223]31 */
32
[dae0aec]33#define HTTP_ON_HTTPS_PORT \
34    "GET /" CRLF
[7e2b223]35
[dae0aec]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)
[7e2b223]40
[265eafc]41#define IS_PROXY_STR(c) \
42    ((c->is_proxy == GNUTLS_ENABLED_TRUE) ? "proxy " : "")
43
[02a6a18]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
[dae0aec]52static apr_status_t gnutls_io_filter_error(ap_filter_t * f,
[e183628]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) {
[4fefa39]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;
[e183628]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;
[dae0aec]84}
[7e2b223]85
[e183628]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;
[dae0aec]105}
106
[e183628]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;
[7e2b223]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,
[e183628]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        }
[7e2b223]185
[e183628]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}
[7e2b223]196
[c301152]197static apr_status_t gnutls_io_input_read(mgs_handle_t * ctxt,
[398d1a0]198        char *buf, apr_size_t * len)
199{
[e183628]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) {
[398d1a0]238        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
239                      "%s: GnuTLS session is NULL!", __func__);
[e183628]240        return APR_EGENERAL;
241    }
242
[f5a36ee]243    while (1)
244    {
245        rc = gnutls_record_recv(ctxt->session, buf + bytes, wanted - bytes);
[e183628]246
[f5a36ee]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;
[e183628]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 */
[398d1a0]289                ap_log_cerror(APLOG_MARK, APLOG_INFO,
[e183628]290                        ctxt->input_rc,
[398d1a0]291                        ctxt->c,
[e183628]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);
[398d1a0]297                ap_log_cerror(APLOG_MARK, APLOG_INFO,
[e183628]298                        ctxt->input_rc,
[398d1a0]299                        ctxt->c,
[e183628]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);
[398d1a0]305                ap_log_cerror(APLOG_MARK, APLOG_INFO,
[e183628]306                        ctxt->input_rc,
[398d1a0]307                        ctxt->c,
[e183628]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)) {
[398d1a0]316                    ap_log_cerror(APLOG_MARK,
[e183628]317                            APLOG_INFO,
318                            ctxt->input_rc,
[398d1a0]319                            ctxt->c,
[e183628]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) {
[4261999]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);
[e183628]333                ctxt->input_rc = APR_EGENERAL;
334            }
335            break;
336        }
337    }
338    return ctxt->input_rc;
[dae0aec]339}
340
[c301152]341static apr_status_t gnutls_io_input_getline(mgs_handle_t * ctxt,
[e183628]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;
[dae0aec]346
[e183628]347    *len = 0;
[dae0aec]348
[e183628]349    while (tmplen > 0) {
350        status = gnutls_io_input_read(ctxt, buf + offset, &tmplen);
[dae0aec]351
[e183628]352        if (status != APR_SUCCESS) {
353            return status;
354        }
[dae0aec]355
[e183628]356        *len += tmplen;
[dae0aec]357
[e183628]358        if ((pos = memchr(buf, APR_ASCII_LF, *len))) {
359            break;
360        }
[dae0aec]361
[e183628]362        offset += tmplen;
363        tmplen = buflen - offset;
364    }
[dae0aec]365
[e183628]366    if (pos) {
367        char *value;
368        int length;
369        apr_size_t bytes = pos - buf;
[dae0aec]370
[e183628]371        bytes += 1;
372        value = buf + bytes;
373        length = *len - bytes;
[dae0aec]374
[e183628]375        char_buffer_write(&ctxt->input_cbuf, value, length);
[dae0aec]376
[e183628]377        *len = bytes;
378    }
[dae0aec]379
[e183628]380    return APR_SUCCESS;
[dae0aec]381}
382
[0106b25]383#define HANDSHAKE_MAX_TRIES 1024
[e183628]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;
[8e33f2d]403#if USING_2_1_RECENT
[e183628]404        ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
405                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
[8e33f2d]406#else
[e183628]407        ap_log_error(APLOG_MARK, APLOG_ERR, 0,
408                ctxt->c->base_server,
409                "GnuTLS: Handshake Failed. Hit Maximum Attempts");
[8e33f2d]410#endif
[e183628]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,
[beb14d9]427                    "GnuTLS: Handshake Alert (%d) '%s'.",
[e183628]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        }
[316bd8c]439#if USING_2_1_RECENT
[e183628]440        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
441                "GnuTLS: Handshake Failed (%d) '%s'", ret,
442                gnutls_strerror(ret));
[316bd8c]443#else
[e183628]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));
[316bd8c]448#endif
[e183628]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;
[671b64f]461        /* If the session was resumed, we did not set the correct
[e183628]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        }
[73f6f12]471        return GNUTLS_E_SUCCESS;
[e183628]472    }
[31645b2]473}
474
[e183628]475int mgs_rehandshake(mgs_handle_t * ctxt) {
476    int rv;
[e02dd8c]477
[e183628]478    if (ctxt->session == NULL)
479        return -1;
[e02dd8c]480
[e183628]481    rv = gnutls_rehandshake(ctxt->session);
[e02dd8c]482
[e183628]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    }
[e02dd8c]490
[e183628]491    ctxt->status = 0;
[e02dd8c]492
[e183628]493    rv = gnutls_do_handshake(ctxt);
[e02dd8c]494
[e183628]495    return rv;
[dae0aec]496}
497
[401a0de]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)
[2ceb836]515            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, ctxt->c,
[401a0de]516                          "%s: Error while closing TLS %sconnection: "
517                          "'%s' (%d)",
518                          __func__, IS_PROXY_STR(ctxt),
519                          gnutls_strerror(ret), (int) ret);
520        else
[2ceb836]521            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
[401a0de]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
[e02dd8c]533apr_status_t mgs_filter_input(ap_filter_t * f,
[e183628]534        apr_bucket_brigade * bb,
535        ap_input_mode_t mode,
[265eafc]536        apr_read_type_e block, apr_off_t readbytes)
537{
[e183628]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);
[265eafc]546        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
547                      "%s: %sconnection aborted",
548                      __func__, IS_PROXY_STR(ctxt));
[e183628]549        return APR_ECONNABORTED;
550    }
551
552    if (ctxt->status == 0) {
[73f6f12]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));
[e183628]558    }
559
560    if (ctxt->status < 0) {
[265eafc]561        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
562                      "%s %s: ap_get_brigade", __func__, IS_PROXY_STR(ctxt));
[e183628]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) {
[fd82e59]577        if (readbytes < 0) {
578            /* you're asking us to speculatively read a negative number of bytes! */
579            return APR_ENOTIMPL;
580        }
[e183628]581        /* Err. This is bad. readbytes *can* be a 64bit int! len.. is NOT */
[fd82e59]582        if ((apr_size_t) readbytes < len) {
[e183628]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
[f5a36ee]596    if (status != APR_SUCCESS)
597    {
598        /* no data for nonblocking read, return APR_EAGAIN */
[73b0bf0]599        if ((block == APR_NONBLOCK_READ) && APR_STATUS_IS_EINTR(status))
[f5a36ee]600            return APR_EAGAIN;
601
[401a0de]602        /* Close TLS session and free resources on EOF,
603         * gnutls_io_filter_error will add an EOS bucket */
[73b0bf0]604        if (APR_STATUS_IS_EOF(status))
[401a0de]605            mgs_bye(ctxt);
606
[e183628]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;
[dae0aec]619}
620
[e183628]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;
[485d28e]651}
652
[e183628]653apr_status_t mgs_filter_output(ap_filter_t * f, apr_bucket_brigade * bb) {
[fd82e59]654    int ret;
[e183628]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;
[671b64f]658
[e183628]659    if (f->c->aborted) {
660        apr_brigade_cleanup(bb);
661        return APR_ECONNABORTED;
662    }
663
664    if (ctxt->status == 0) {
[73f6f12]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));
[e183628]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);
[671b64f]681        } else if (APR_BUCKET_IS_FLUSH(bucket)) {
[e183628]682            /* Try Flush */
683            if (write_flush(ctxt) < 0) {
684                /* Flush Error */
685                return ctxt->output_rc;
686            }
687            /* cleanup! */
[671b64f]688            apr_bucket_delete(bucket);
[e183628]689        } else if (AP_BUCKET_IS_EOC(bucket)) {
[401a0de]690            /* End Of Connection, close TLS session and free
691             * resources */
692            mgs_bye(ctxt);
[9a9bc1e]693            /* cleanup! */
[671b64f]694            apr_bucket_delete(bucket);
[e183628]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
[9a9bc1e]702            status = apr_bucket_read(bucket, &data, &len, rblock);
[e183628]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;
[b4a875b]725                } else {
[e183628]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                    }
[fd82e59]750                } else if ((apr_size_t)(ret) != len) {
751                    /* we know the above cast is OK because len > 0 and ret >= 0 */
[671b64f]752                    /* Not able to send the entire bucket,
[e183628]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;
[dae0aec]763}
764
[02a6a18]765/**
766 * Pull function for GnuTLS
[a9fa300]767 *
768 * Generic errnos used for gnutls_transport_set_errno:
769 * EIO: Unknown I/O error
770 * ECONNABORTED: Input BB does not exist (NULL)
771 *
772 * The reason we are not using APR_TO_OS_ERROR to map apr_status_t to
773 * errnos is this warning in the APR documentation: "If the statcode
774 * was not created by apr_get_os_error or APR_FROM_OS_ERROR, the
775 * results are undefined." We cannot know if this applies to any error
776 * we might encounter.
[02a6a18]777 */
[c301152]778ssize_t mgs_transport_read(gnutls_transport_ptr_t ptr,
[02a6a18]779                           void *buffer, size_t len)
780{
[e183628]781    mgs_handle_t *ctxt = ptr;
782    apr_status_t rc;
783    apr_size_t in = len;
784    apr_read_type_e block = ctxt->input_block;
785
786    ctxt->input_rc = APR_SUCCESS;
787
788    /* If Len = 0, we don't do anything. */
[19f2719]789    if (!len || buffer == NULL)
790    {
[e183628]791        return 0;
792    }
[19f2719]793    /* Input bucket brigade is missing, EOF */
794    if (!ctxt->input_bb)
795    {
[e183628]796        ctxt->input_rc = APR_EOF;
[a9fa300]797        gnutls_transport_set_errno(ctxt->session, ECONNABORTED);
[e183628]798        return -1;
799    }
800
[19f2719]801    if (APR_BRIGADE_EMPTY(ctxt->input_bb))
802    {
[e183628]803        rc = ap_get_brigade(ctxt->input_filter->next,
[19f2719]804                            ctxt->input_bb, AP_MODE_READBYTES,
805                            ctxt->input_block, in);
[e183628]806
807        /* Not a problem, there was simply no data ready yet.
808         */
809        if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
[02a6a18]810            || (rc == APR_SUCCESS
811                && APR_BRIGADE_EMPTY(ctxt->input_bb)))
812        {
813            if (APR_STATUS_IS_EOF(ctxt->input_rc))
814            {
[e183628]815                return 0;
[02a6a18]816            }
817            else
818            {
[6868585]819                gnutls_transport_set_errno(ctxt->session,
820                                           EAI_APR_TO_RAW(ctxt->input_rc));
[e183628]821                return -1;
822            }
823        }
[e02dd8c]824
[19f2719]825        if (rc != APR_SUCCESS)
826        {
[e183628]827            /* Unexpected errors discard the brigade */
828            apr_brigade_cleanup(ctxt->input_bb);
829            ctxt->input_bb = NULL;
[a9fa300]830            gnutls_transport_set_errno(ctxt->session, EIO);
[e183628]831            return -1;
832        }
833    }
834
[19f2719]835    ctxt->input_rc = brigade_consume(ctxt->input_bb, block, buffer, &len);
[e183628]836
[19f2719]837    if (ctxt->input_rc == APR_SUCCESS)
838    {
[e183628]839        return (ssize_t) len;
840    }
841
842    if (APR_STATUS_IS_EAGAIN(ctxt->input_rc)
[02a6a18]843        || APR_STATUS_IS_EINTR(ctxt->input_rc))
844    {
845        if (len == 0)
846        {
[6868585]847            gnutls_transport_set_errno(ctxt->session,
848                                       EAI_APR_TO_RAW(ctxt->input_rc));
[e183628]849            return -1;
[60cf11c]850        }
[e183628]851
852        return (ssize_t) len;
853    }
854
855    /* Unexpected errors and APR_EOF clean out the brigade.
[19f2719]856     * Subsequent calls will return APR_EOF. */
[e183628]857    apr_brigade_cleanup(ctxt->input_bb);
858    ctxt->input_bb = NULL;
859
[19f2719]860    if (APR_STATUS_IS_EOF(ctxt->input_rc) && len)
861    {
862        /* Some data has been received before EOF, return it. */
[e183628]863        return (ssize_t) len;
864    }
865
[a9fa300]866    gnutls_transport_set_errno(ctxt->session, EIO);
[e183628]867    return -1;
[dae0aec]868}
869
[a9fa300]870/**
871 * Push function for GnuTLS
872 *
873 * In case of unexpected errors gnutls_transport_set_errno is called
874 * with EIO.  The reason we are not using APR_TO_OS_ERROR to map
875 * apr_status_t to errnos is this warning in the APR documentation:
876 * "If the statcode was not created by apr_get_os_error or
877 * APR_FROM_OS_ERROR, the results are undefined." We cannot know if
878 * this applies to any error we might encounter.
879 */
[c301152]880ssize_t mgs_transport_write(gnutls_transport_ptr_t ptr,
[19f2719]881                            const void *buffer, size_t len)
882{
[e183628]883    mgs_handle_t *ctxt = ptr;
884
885    /* pass along the encrypted data
886     * need to flush since we're using SSL's malloc-ed buffer
887     * which will be overwritten once we leave here
888     */
889    apr_bucket *bucket = apr_bucket_transient_create(buffer, len,
890            ctxt->output_bb->
891            bucket_alloc);
892    ctxt->output_length += len;
893    APR_BRIGADE_INSERT_TAIL(ctxt->output_bb, bucket);
894
[be41ee4]895    if (write_flush(ctxt) < 0)
896    {
897        /* We encountered an error. APR_EINTR or APR_EAGAIN can be
898         * handled, treat everything else as a generic I/O error. */
899        int err = EIO;
900        if (APR_STATUS_IS_EAGAIN(ctxt->output_rc)
901            || APR_STATUS_IS_EINTR(ctxt->output_rc))
902            err = EAI_APR_TO_RAW(ctxt->output_rc);
903
904        gnutls_transport_set_errno(ctxt->session, err);
[e183628]905        return -1;
906    }
907    return len;
[7e2b223]908}
Note: See TracBrowser for help on using the repository browser.