source: mod_gnutls/src/gnutls_io.c @ 73f6f12

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

Check handshake result before logging "TLS connection opened"

Without these checks the "TLS connection opened" message is logged even
after a failed handshake, which is confusing.

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