source: mod_gnutls/src/mod_gnutls.c @ aa99b13

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since aa99b13 was aa99b13, checked in by Paul Querna <chip@…>, 15 years ago

commit before i move everything around

  • Property mode set to 100644
File size: 19.2 KB
Line 
1/* ====================================================================
2 *  Copyright 2004 Paul Querna
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 *
16 */
17
18#include "httpd.h"
19#include "http_config.h"
20#include "http_protocol.h"
21#include "http_connection.h"
22#include "http_core.h"
23#include "http_log.h"
24#include "apr_buckets.h"
25#include "apr_strings.h"
26#include "apr_tables.h"
27
28#include <gcrypt.h>
29#include <gnutls/gnutls.h>
30
31#if APR_HAS_THREADS
32GCRY_THREAD_OPTION_PTHREAD_IMPL;
33#endif
34
35module AP_MODULE_DECLARE_DATA gnutls_module;
36
37#ifdef GNUTLS_AS_FILTER
38#define GNUTLS_OUTPUT_FILTER_NAME "GnuTLS Output Filter"
39#define GNUTLS_INPUT_FILTER_NAME "GnuTLS Input Filter"
40#endif
41
42#define GNUTLS_ENABLED_FALSE 0
43#define GNUTLS_ENABLED_TRUE  1
44
45
46typedef struct {
47    gnutls_certificate_credentials_t certs;
48    gnutls_anon_server_credentials_t anoncred;
49    char *key_file;
50    char *cert_file;
51    int enabled;
52    int ciphers[16];
53    int key_exchange[16];
54    int macs[16];
55    int protocol[16];
56    int compression[16];
57} gnutls_srvconf_rec;
58
59typedef struct gnutls_handle_t gnutls_handle_t;
60struct gnutls_handle_t
61{
62    gnutls_srvconf_rec *sc;
63    gnutls_session_t session;
64    ap_filter_t *input_filter;
65    apr_bucket_brigade *input_bb;
66    apr_read_type_e input_block;
67    int status;
68    int non_https;
69};
70
71static apr_status_t gnutls_filter_input(ap_filter_t * f,
72                                        apr_bucket_brigade * bb,
73                                        ap_input_mode_t mode,
74                                        apr_read_type_e block,
75                                        apr_off_t readbytes)
76{
77    apr_status_t status = APR_SUCCESS;
78    gnutls_handle_t *ctxt = (gnutls_handle_t *) f->ctx;
79
80    if (f->c->aborted) {
81        apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc);
82        APR_BRIGADE_INSERT_TAIL(bb, bucket);
83        return APR_ECONNABORTED;
84    }
85
86
87    for (b = APR_BRIGADE_FIRST(bb);
88         b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
89        if (APR_BUCKET_IS_EOS(b)) {
90            /* end of connection */
91        }
92        else if (apr_bucket_read(b, &buf, &bytes, APR_BLOCK_READ)
93                 == APR_SUCCESS) {
94            /* more data */
95        }
96    }
97
98    return status;
99}
100
101#define HANDSHAKE_ATTEMPTS 10
102
103static apr_status_t gnutls_filter_output(ap_filter_t * f,
104                                         apr_bucket_brigade * bb)
105{
106    int ret, i;
107    const char *buf = 0;
108    apr_size_t bytes = 0;
109    gnutls_handle_t *ctxt = (gnutls_handle_t *) f->ctx;
110    apr_status_t status = APR_SUCCESS;
111    apr_read_type_e rblock = APR_NONBLOCK_READ;
112
113    if (f->c->aborted) {
114        apr_brigade_cleanup(bb);
115        return APR_ECONNABORTED;
116    }
117
118    if(ctxt->status == 0) {
119        for (i=HANDSHAKE_ATTEMPTS; i>0; i--){
120            ret = gnutls_handshake(ctxt->session);
121
122            if(ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN){
123                continue;
124            }
125
126            if (ret < 0) {
127                if (ret == GNUTLS_E_WARNING_ALERT_RECEIVED || ret == GNUTLS_E_FATAL_ALERT_RECEIVED) {
128                    ret = gnutls_alert_get(ctxt->session);
129                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server,
130                        "GnuTLS: Hanshake Alert (%d) '%s'.\n", ret, gnutls_alert_get_name(ret));
131                }
132
133                if (gnutls_error_is_fatal(ret) != 0) {
134                    gnutls_deinit(ctxt->session);
135                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, f->c->base_server,
136                         "GnuTLS: Handshake Failed (%d) '%s'",ret, gnutls_strerror(ret));
137                    ctxt->status = -1;
138                    break;
139                }
140            }
141            else {
142                ctxt->status = 1;
143                break; /* all done with the handshake */
144            }
145        } 
146    }
147
148    if (ctxt->status < 0) {
149        return ap_pass_brigade(f->next, bb);
150    }
151
152    while (!APR_BRIGADE_EMPTY(bb)) {
153        apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
154        if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
155            /** TODO: GnuTLS doesn't have a special flush method? **/
156            if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
157                return status;
158            }
159            break;
160        }
161        else if(AP_BUCKET_IS_EOC(bucket)) {
162            gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
163
164            if ((status = ap_pass_brigade(f->next, bb)) != APR_SUCCESS) {
165                return status;
166            }
167            break;
168        }
169        else {
170            /* filter output */
171            const char *data;
172            apr_size_t len;
173
174            status = apr_bucket_read(bucket, &data, &len, rblock);
175
176            if (APR_STATUS_IS_EAGAIN(status)) {
177                 rblock = APR_BLOCK_READ;
178                 continue; /* and try again with a blocking read. */
179            }
180
181            rblock = APR_NONBLOCK_READ;
182
183            if (!APR_STATUS_IS_EOF(status) && (status != APR_SUCCESS)) {
184                break;
185            }
186
187            ret = gnutls_record_send(ctxt->session, data, len);
188            status = ssl_filter_write(f, data, len);
189            if(ret < 0) {
190                /* error sending output */
191            }
192            else if ((apr_size_t)res != len) {
193                /* not all of the data was sent. */
194                /* mod_ssl basicly errors out here.. this doesn't seem right? */
195            }
196            else {
197                /* send complete */
198               
199            }
200
201            apr_bucket_delete(bucket);
202
203            if (status != APR_SUCCESS) {
204                break;
205            }
206
207        }
208    }
209
210    return status;
211}
212
213static apr_status_t gnutls_cleanup_pre_config(void *data)
214{
215    gnutls_global_deinit();
216    return APR_SUCCESS;
217}
218
219static int gnutls_hook_pre_config(apr_pool_t * pconf,
220                                  apr_pool_t * plog, apr_pool_t * ptemp)
221{
222
223#if APR_HAS_THREADS
224    gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
225#endif
226
227    gnutls_global_init();
228
229    apr_pool_cleanup_register(pconf, NULL, gnutls_cleanup_pre_config,
230                              apr_pool_cleanup_null);
231
232    return OK;
233}
234
235#define DH_BITS 1024
236#define RSA_BITS 512
237
238static int gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
239                                   apr_pool_t * ptemp,
240                                   server_rec * base_server)
241{
242    gnutls_srvconf_rec *sc;
243    server_rec *s;
244    gnutls_dh_params_t dh_params;
245    gnutls_rsa_params_t rsa_params;
246
247
248    /* TODO: Should we regenerate these after X requests / X time ? */
249//    gnutls_dh_params_init(&dh_params);
250//    gnutls_dh_params_generate2(dh_params, DH_BITS);
251//    gnutls_rsa_params_init(&rsa_params);
252//    gnutls_rsa_params_generate2(rsa_params, RSA_BITS);
253
254    for (s = base_server; s; s = s->next) {
255        sc = (gnutls_srvconf_rec *) ap_get_module_config(s->module_config,
256                                                       &gnutls_module);
257        if (sc->cert_file != NULL && sc->key_file != NULL) {
258            gnutls_certificate_set_x509_key_file(sc->certs, sc->cert_file,
259                                                 sc->key_file,
260                                                 GNUTLS_X509_FMT_PEM);
261//          gnutls_certificate_set_rsa_export_params(sc->certs, rsa_params);
262//          gnutls_certificate_set_dh_params(sc->certs, dh_params);
263        }
264        else if(sc->enabled == GNUTLS_ENABLED_TRUE ){
265            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
266                         "[GnuTLS] - Host '%s:%d' is missing a Cert and Key File!",
267                         s->server_hostname, s->port);
268        }
269    }
270
271
272    ap_add_version_component(p, "GnuTLS/" LIBGNUTLS_VERSION);
273    return OK;
274}
275
276static const char *gnutls_hook_http_method(const request_rec * r)
277{
278    gnutls_srvconf_rec *sc =
279        (gnutls_srvconf_rec *) ap_get_module_config(r->server->module_config,
280                                                  &gnutls_module);
281
282    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
283        return NULL;
284    }
285
286    return "https";
287}
288
289static apr_port_t gnutls_hook_default_port(const request_rec * r)
290{
291    gnutls_srvconf_rec *sc =
292        (gnutls_srvconf_rec *) ap_get_module_config(r->server->module_config,
293                                                  &gnutls_module);
294
295    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
296        return 0;
297    }
298
299    return 443;
300}
301
302/**
303 * From mod_ssl / ssl_engine_io.c
304 * This function will read from a brigade and discard the read buckets as it
305 * proceeds.  It will read at most *len bytes.
306 */
307static apr_status_t brigade_consume(apr_bucket_brigade * bb,
308                                    apr_read_type_e block,
309                                    char *c, apr_size_t * len)
310{
311    apr_size_t actual = 0;
312    apr_status_t status = APR_SUCCESS;
313
314    while (!APR_BRIGADE_EMPTY(bb)) {
315        apr_bucket *b = APR_BRIGADE_FIRST(bb);
316        const char *str;
317        apr_size_t str_len;
318        apr_size_t consume;
319
320        /* Justin points out this is an http-ism that might
321         * not fit if brigade_consume is added to APR.  Perhaps
322         * apr_bucket_read(eos_bucket) should return APR_EOF?
323         * Then this becomes mainline instead of a one-off.
324         */
325        if (APR_BUCKET_IS_EOS(b)) {
326            status = APR_EOF;
327            break;
328        }
329
330        /* The reason I'm not offering brigade_consume yet
331         * across to apr-util is that the following call
332         * illustrates how borked that API really is.  For
333         * this sort of case (caller provided buffer) it
334         * would be much more trivial for apr_bucket_consume
335         * to do all the work that follows, based on the
336         * particular characteristics of the bucket we are
337         * consuming here.
338         */
339        status = apr_bucket_read(b, &str, &str_len, block);
340
341        if (status != APR_SUCCESS) {
342            if (APR_STATUS_IS_EOF(status)) {
343                /* This stream bucket was consumed */
344                apr_bucket_delete(b);
345                continue;
346            }
347            break;
348        }
349
350        if (str_len > 0) {
351            /* Do not block once some data has been consumed */
352            block = APR_NONBLOCK_READ;
353
354            /* Assure we don't overflow. */
355            consume = (str_len + actual > *len) ? *len - actual : str_len;
356
357            memcpy(c, str, consume);
358
359            c += consume;
360            actual += consume;
361
362            if (consume >= b->length) {
363                /* This physical bucket was consumed */
364                apr_bucket_delete(b);
365            }
366            else {
367                /* Only part of this physical bucket was consumed */
368                b->start += consume;
369                b->length -= consume;
370            }
371        }
372        else if (b->length == 0) {
373            apr_bucket_delete(b);
374        }
375
376        /* This could probably be actual == *len, but be safe from stray
377         * photons. */
378        if (actual >= *len) {
379            break;
380        }
381    }
382
383    *len = actual;
384    return status;
385}
386
387
388static ssize_t gnutls_transport_read(gnutls_transport_ptr_t ptr,
389                                     void *buffer, size_t len)
390{
391    gnutls_handle_t *ctxt = ptr;
392    apr_status_t rc;
393    apr_size_t in = len;
394    /* If Len = 0, we don't do anything. */
395    if (!len)
396        return 0;
397
398    if (APR_BRIGADE_EMPTY(ctxt->input_bb)) {
399
400        rc = ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
401                            AP_MODE_READBYTES, ctxt->input_block, in);
402
403        /* Not a problem, there was simply no data ready yet.
404         */
405        if (APR_STATUS_IS_EAGAIN(rc) || APR_STATUS_IS_EINTR(rc)
406            || (rc == APR_SUCCESS && APR_BRIGADE_EMPTY(ctxt->input_bb))) {
407            return 0;
408        }
409
410        if (rc != APR_SUCCESS) {
411            /* Unexpected errors discard the brigade */
412            apr_brigade_cleanup(ctxt->input_bb);
413            ctxt->input_bb = NULL;
414            return -1;
415        }
416    }
417
418//    brigade_consume(ctxt->input_bb, ctxt->input_block, buffer, &len);
419
420
421    ap_get_brigade(ctxt->input_filter->next, ctxt->input_bb,
422                   AP_MODE_READBYTES, ctxt->input_block, len);
423
424    return len;
425}
426
427static ssize_t gnutls_transport_write(gnutls_transport_ptr_t ptr,
428                                      const void *buffer, size_t len)
429{
430    gnutls_handle_t *ctxt = ptr;
431
432//    apr_bucket *bucket = apr_bucket_transient_create(in, inl,
433//                                                     outctx->bb->
434//                                                     bucket_alloc);
435
436    //  outctx->length += inl;
437    //APR_BRIGADE_INSERT_TAIL(outctx->bb, bucket);
438    return 0;
439}
440
441static int gnutls_hook_pre_connection(conn_rec * c, void *csd)
442{
443    gnutls_handle_t *ctxt;
444    gnutls_srvconf_rec *sc =
445        (gnutls_srvconf_rec *) ap_get_module_config(c->base_server->
446                                                  module_config,
447                                                  &gnutls_module);
448
449    if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) {
450        return DECLINED;
451    }
452
453    ctxt = apr_pcalloc(c->pool, sizeof(*ctxt));
454
455    ctxt->sc = sc;
456    ctxt->status = 0;
457    gnutls_init(&ctxt->session, GNUTLS_SERVER);
458
459    gnutls_cipher_set_priority(ctxt->session, sc->ciphers);
460    gnutls_compression_set_priority(ctxt->session, sc->compression);
461    gnutls_kx_set_priority(ctxt->session, sc->key_exchange);
462    gnutls_protocol_set_priority(ctxt->session, sc->protocol);
463    gnutls_mac_set_priority(ctxt->session, sc->macs);
464
465    gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE, sc->certs);
466//  if(anon) {
467//    gnutls_credentials_set(ctxt->session, GNUTLS_CRD_ANON, sc->anoncred);
468//  }
469
470    gnutls_certificate_server_set_request(ctxt->session, GNUTLS_CERT_IGNORE);
471
472//    gnutls_dh_set_prime_bits(ctxt->session, DH_BITS);
473 
474
475    ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
476
477    gnutls_transport_set_pull_function(ctxt->session, gnutls_transport_read);
478    gnutls_transport_set_push_function(ctxt->session, gnutls_transport_write);
479    gnutls_transport_set_ptr(ctxt->session, ctxt);
480    ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c);
481    ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c);
482
483    return OK;
484}
485
486static const char *gnutls_set_cert_file(cmd_parms * parms, void *dummy,
487                                      const char *arg)
488{
489    gnutls_srvconf_rec *sc =
490        (gnutls_srvconf_rec *) ap_get_module_config(parms->server->
491                                                  module_config,
492                                                  &gnutls_module);
493    sc->cert_file = apr_pstrdup(parms->pool, arg);
494    return NULL;
495}
496
497static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy,
498                                      const char *arg)
499{
500    gnutls_srvconf_rec *sc =
501        (gnutls_srvconf_rec *) ap_get_module_config(parms->server->
502                                                  module_config,
503                                                  &gnutls_module);
504    sc->key_file =  apr_pstrdup(parms->pool, arg);
505    return NULL;
506}
507
508static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy,
509                                      const char *arg)
510{
511    gnutls_srvconf_rec *sc =
512        (gnutls_srvconf_rec *) ap_get_module_config(parms->server->
513                                                  module_config,
514                                                  &gnutls_module);
515    if (!strcasecmp(arg, "On")) {
516        sc->enabled = GNUTLS_ENABLED_TRUE;
517    }
518    else if (!strcasecmp(arg, "Off")) {
519        sc->enabled = GNUTLS_ENABLED_FALSE;
520    }
521    else {
522        return "GnuTLSEnable must be set to 'On' or 'Off'";
523    }
524
525    return NULL;
526}
527
528static const command_rec gnutls_cmds[] = {
529    AP_INIT_TAKE1("GnuTLSCertificateFile", gnutls_set_cert_file,
530                  NULL,
531                  RSRC_CONF,
532                  "SSL Server Key file"),
533    AP_INIT_TAKE1("GnuTLSKeyFile", gnutls_set_key_file,
534                  NULL,
535                  RSRC_CONF,
536                  "SSL Server Certificate file"),
537    AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled,
538                 NULL, RSRC_CONF,
539                 "Whether this server has GnuTLS Enabled. Default: Off"),
540
541    {NULL}
542};
543
544/* TODO: CACertificateFile & Client Authentication
545 *    AP_INIT_TAKE1("GnuTLSCACertificateFile", ap_set_server_string_slot,
546 *                 (void *) APR_OFFSETOF(gnutls_srvconf_rec, key_file), NULL,
547 *                 RSRC_CONF,
548 *                 "CA"),
549 */
550
551static void gnutls_hooks(apr_pool_t * p)
552{
553    ap_hook_pre_connection(gnutls_hook_pre_connection, NULL, NULL,
554                           APR_HOOK_MIDDLE);
555    ap_hook_post_config(gnutls_hook_post_config, NULL, NULL, APR_HOOK_MIDDLE);
556    ap_hook_http_method(gnutls_hook_http_method, NULL, NULL, APR_HOOK_MIDDLE);
557    ap_hook_default_port(gnutls_hook_default_port, NULL, NULL,
558                         APR_HOOK_MIDDLE);
559    ap_hook_pre_config(gnutls_hook_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
560
561    /* TODO: HTTP Upgrade Filter */
562    /* ap_register_output_filter ("UPGRADE_FILTER",
563     *          ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5);
564     */
565    ap_register_input_filter(GNUTLS_INPUT_FILTER_NAME, gnutls_filter_input,
566                             NULL, AP_FTYPE_CONNECTION + 5);
567    ap_register_output_filter(GNUTLS_OUTPUT_FILTER_NAME, gnutls_filter_output,
568                              NULL, AP_FTYPE_CONNECTION + 5);
569}
570
571static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s)
572{
573    int i;
574    gnutls_srvconf_rec *sc = apr_pcalloc(p, sizeof(*sc));
575
576    sc->enabled = GNUTLS_ENABLED_FALSE;
577    sc->non_https = 0;
578
579    gnutls_certificate_allocate_credentials(&sc->certs);
580    gnutls_anon_allocate_server_credentials(&sc->anoncred);
581    sc->key_file = NULL;
582    sc->cert_file = NULL;
583
584    i = 0;
585    sc->ciphers[i++] = GNUTLS_CIPHER_RIJNDAEL_128_CBC;
586    sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_128;
587    sc->ciphers[i++] = GNUTLS_CIPHER_3DES_CBC;
588    sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_40;
589    sc->ciphers[i]   = 0;
590
591    i = 0;
592    sc->key_exchange[i++] = GNUTLS_KX_RSA;
593    sc->key_exchange[i++] = GNUTLS_KX_RSA_EXPORT;
594    sc->key_exchange[i++] = GNUTLS_KX_DHE_RSA;
595    sc->key_exchange[i++] = GNUTLS_KX_DHE_DSS;
596    sc->key_exchange[i] = 0;
597
598    i = 0;
599    sc->macs[i++] = GNUTLS_MAC_MD5;
600    sc->macs[i++] = GNUTLS_MAC_SHA;
601    sc->macs[i++] = GNUTLS_MAC_RMD160;
602    sc->macs[i] = 0;
603
604    i = 0;
605    sc->protocol[i++] = GNUTLS_TLS1_1;
606    sc->protocol[i++] = GNUTLS_TLS1;
607    sc->protocol[i++] = GNUTLS_SSL3;
608    sc->protocol[i] = 0;
609
610    i = 0;
611    sc->compression[i++] = GNUTLS_COMP_NULL;
612    sc->compression[i++] = GNUTLS_COMP_ZLIB;
613    sc->compression[i++] = GNUTLS_COMP_LZO;
614    sc->compression[i] = 0;
615
616    return sc;
617}
618
619
620
621module AP_MODULE_DECLARE_DATA gnutls_module = {
622    STANDARD20_MODULE_STUFF,
623    NULL,
624    NULL,
625    gnutls_config_server_create,
626    NULL,
627/*    gnutls_config_server_merge, */
628    gnutls_cmds,
629    gnutls_hooks
630};
Note: See TracBrowser for help on using the repository browser.