source: mod_gnutls/src/gnutls_io.c @ 47a909e

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

Safe integer type conversion in mgs_filter_input()

Read sizes should be too small for 32 or 64 bit integer length to
matter, but we have to make sure.

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