source: mod_gnutls/src/mod_gnutls.c @ 6a8a839

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

updated

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