source: mod_gnutls/src/mod_gnutls.c @ e924ddd

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

client auth is sort of working.

  • Property mode set to 100644
File size: 33.5 KB
Line 
1/**
2 *  Copyright 2004-2005 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 "mod_gnutls.h"
19#include "http_vhost.h"
20
21extern server_rec *ap_server_conf;
22
23#if APR_HAS_THREADS
24GCRY_THREAD_OPTION_PTHREAD_IMPL;
25#endif
26
27#if MOD_GNUTLS_DEBUG
28static apr_file_t* debug_log_fp;
29#endif
30
31static apr_status_t mod_gnutls_cleanup_pre_config(void *data)
32{
33    gnutls_global_deinit();
34    return APR_SUCCESS;
35}
36
37#if MOD_GNUTLS_DEBUG
38static void gnutls_debug_log_all( int level, const char* str)
39{
40    apr_file_printf(debug_log_fp, "<%d> %s\n", level, str);
41}
42#endif
43
44static int mod_gnutls_hook_pre_config(apr_pool_t * pconf,
45                                      apr_pool_t * plog, apr_pool_t * ptemp)
46{
47
48#if APR_HAS_THREADS
49    /* TODO: Check MPM Type here */
50    gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
51#endif
52
53    gnutls_global_init();
54
55    apr_pool_cleanup_register(pconf, NULL, mod_gnutls_cleanup_pre_config,
56                              apr_pool_cleanup_null);
57
58#if MOD_GNUTLS_DEBUG
59    apr_file_open(&debug_log_fp, "/tmp/gnutls_debug",
60                  APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, pconf);
61
62    gnutls_global_set_log_level(9);
63    gnutls_global_set_log_function(gnutls_debug_log_all);
64#endif
65
66    return OK;
67}
68
69
70static gnutls_datum load_params(const char* file, server_rec* s, 
71                                apr_pool_t* pool) 
72{
73    gnutls_datum ret = { NULL, 0 };
74    apr_file_t* fp;
75    apr_finfo_t finfo;
76    apr_status_t rv;
77    apr_size_t br = 0;
78
79    rv = apr_file_open(&fp, file, APR_READ|APR_BINARY, APR_OS_DEFAULT, 
80                       pool);
81    if (rv != APR_SUCCESS) {
82        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, 
83                     "GnuTLS failed to load params file at: %s", file);
84        return ret;
85    }
86
87    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
88
89    if (rv != APR_SUCCESS) {
90        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, 
91                     "GnuTLS failed to stat params file at: %s", file);
92        return ret;
93    }
94
95    ret.data = apr_palloc(pool, finfo.size+1);
96    rv = apr_file_read_full(fp, ret.data, finfo.size, &br);
97
98    if (rv != APR_SUCCESS) {
99        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, 
100                     "GnuTLS failed to read params file at: %s", file);
101        return ret;
102    }
103    apr_file_close(fp);
104    ret.data[br] = '\0';
105    ret.size = br;
106
107    return ret;
108}
109
110static int mod_gnutls_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
111                                       apr_pool_t * ptemp,
112                                       server_rec * base_server)
113{
114    int rv;
115    int data_len;
116    server_rec *s;
117    gnutls_dh_params_t dh_params;
118    gnutls_rsa_params_t rsa_params;
119    mod_gnutls_srvconf_rec *sc;
120    mod_gnutls_srvconf_rec *sc_base;
121    void *data = NULL;
122    int first_run = 0;
123    const char *userdata_key = "mod_gnutls_init";
124         
125    apr_pool_userdata_get(&data, userdata_key, base_server->process->pool);
126    if (data == NULL) {
127        first_run = 1;
128        apr_pool_userdata_set((const void *)1, userdata_key, 
129                              apr_pool_cleanup_null, 
130                              base_server->process->pool);
131    }
132
133
134    {
135        gnutls_datum pdata;
136        apr_pool_t* tpool;
137        s = base_server;
138        sc_base = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config,
139                                                             &gnutls_module);
140
141        apr_pool_create(&tpool, p);
142
143        gnutls_dh_params_init(&dh_params);
144
145        pdata = load_params(sc_base->dh_params_file, s, tpool);
146
147        if (pdata.size != 0) {
148            rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata, 
149                                               GNUTLS_X509_FMT_PEM);
150            if (rv != 0) {
151                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, 
152                             "GnuTLS: Unable to load DH Params: (%d) %s",
153                             rv, gnutls_strerror(rv));
154                exit(rv);
155            }
156        }
157        else {
158            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, 
159                         "GnuTLS: Unable to load DH Params."
160                         " Shutting Down.");
161            exit(-1);
162        }
163        apr_pool_clear(tpool);
164
165        gnutls_rsa_params_init(&rsa_params);
166
167        pdata = load_params(sc_base->rsa_params_file, s, tpool);
168
169        if (pdata.size != 0) {
170            rv = gnutls_rsa_params_import_pkcs1(rsa_params, &pdata, 
171                                                GNUTLS_X509_FMT_PEM);
172            if (rv != 0) {
173                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, 
174                             "GnuTLS: Unable to load RSA Params: (%d) %s",
175                             rv, gnutls_strerror(rv));
176                exit(rv);
177            }
178        }
179        else {
180            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, 
181                         "GnuTLS: Unable to load RSA Params."
182                         " Shutting Down.");
183            exit(-1);
184        }
185
186        apr_pool_destroy(tpool);
187        rv = mod_gnutls_cache_post_config(p, s, sc_base);
188        if (rv != 0) {
189            ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, 
190                         "GnuTLS: Post Config for GnuTLSCache Failed."
191                         " Shutting Down.");
192            exit(-1);
193        }
194         
195        for (s = base_server; s; s = s->next) {
196            sc = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config,
197                                                                 &gnutls_module);
198            sc->cache_type = sc_base->cache_type;
199            sc->cache_config = sc_base->cache_config;
200
201            gnutls_certificate_set_rsa_export_params(sc->certs, 
202                                                     rsa_params);
203            gnutls_certificate_set_dh_params(sc->certs, dh_params);
204
205            if (sc->cert_x509 == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
206                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
207                             "[GnuTLS] - Host '%s:%d' is missing a "
208                             "Certificate File!",
209                         s->server_hostname, s->port);
210                exit(-1);
211            }
212           
213            if (sc->privkey_x509 == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
214                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
215                             "[GnuTLS] - Host '%s:%d' is missing a "
216                             "Private Key File!",
217                             s->server_hostname, s->port);
218                exit(-1);
219            }
220           
221            rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509, 
222                                               GNUTLS_OID_X520_COMMON_NAME, 0, 0,
223                                               NULL, &data_len);
224           
225            if (data_len < 1) {
226                sc->enabled = GNUTLS_ENABLED_FALSE;
227                sc->cert_cn = NULL;
228                continue;
229            }
230           
231            sc->cert_cn = apr_palloc(p, data_len);
232            rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509, 
233                                               GNUTLS_OID_X520_COMMON_NAME, 0, 0,
234                                               sc->cert_cn, &data_len);
235            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
236                         s,
237                         "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X sc: 0x%08X", sc->cert_cn, rv,
238                         gnutls_pk_algorithm_get_name(gnutls_x509_privkey_get_pk_algorithm(sc->privkey_x509)),
239                         (unsigned int)s, (unsigned int)sc);
240        }
241    }
242
243    ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION);
244
245    return OK;
246}
247
248static void mod_gnutls_hook_child_init(apr_pool_t *p, server_rec *s)
249{
250    apr_status_t rv = APR_SUCCESS;
251    mod_gnutls_srvconf_rec *sc = ap_get_module_config(s->module_config,
252                                                      &gnutls_module);
253
254    if (sc->cache_type != mod_gnutls_cache_none) {
255        rv = mod_gnutls_cache_child_init(p, s, sc);
256        if(rv != APR_SUCCESS) {
257            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
258                             "[GnuTLS] - Failed to run Cache Init");
259        }
260    }
261    else {
262        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
263                     "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache");
264    }
265}
266
267static const char *mod_gnutls_hook_http_scheme(const request_rec * r)
268{
269    mod_gnutls_srvconf_rec *sc =
270        (mod_gnutls_srvconf_rec *) ap_get_module_config(r->server->
271                                                        module_config,
272                                                        &gnutls_module);
273
274    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
275        return NULL;
276    }
277
278    return "https";
279}
280
281static apr_port_t mod_gnutls_hook_default_port(const request_rec * r)
282{
283    mod_gnutls_srvconf_rec *sc =
284        (mod_gnutls_srvconf_rec *) ap_get_module_config(r->server->
285                                                        module_config,
286                                                        &gnutls_module);
287
288    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
289        return 0;
290    }
291
292    return 443;
293}
294
295static void mod_gnutls_changed_servers(mod_gnutls_handle_t *ctxt)
296{
297    gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE, ctxt->sc->certs);
298    gnutls_certificate_server_set_request(ctxt->session, ctxt->sc->client_verify_mode);
299}
300
301#define MAX_HOST_LEN 255
302
303#if USING_2_1_RECENT
304typedef struct
305{
306    mod_gnutls_handle_t *ctxt;
307    gnutls_retr_st* ret;
308    const char* sni_name;
309} vhost_cb_rec;
310
311int vhost_cb (void* baton, conn_rec* conn, server_rec* s)
312{
313    mod_gnutls_srvconf_rec *tsc;
314    vhost_cb_rec* x = baton;
315
316    tsc = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config,
317                                                          &gnutls_module);
318   
319    if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
320        return 0;
321    }
322   
323    /* The CN can contain a * -- this will match those too. */
324    if (ap_strcasecmp_match(x->sni_name, tsc->cert_cn) == 0) {
325        /* found a match */
326        x->ret->cert.x509 = &tsc->cert_x509;
327        x->ret->key.x509 = tsc->privkey_x509;
328#if MOD_GNUTLS_DEBUG
329        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
330                     x->ctxt->c->base_server,
331                     "GnuTLS: Virtual Host CB: "
332                     "'%s' == '%s'", tsc->cert_cn, x->sni_name);
333#endif
334        /* Because we actually change the server used here, we need to reset
335         * things like ClientVerify.
336         */
337        x->ctxt->sc = tsc;
338        mod_gnutls_changed_servers(x->ctxt);
339        return 1;
340    }
341    return 0;
342}
343#endif
344
345static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st* ret) 
346{
347    int rv;
348    int sni_type;
349    int data_len = MAX_HOST_LEN;
350    char sni_name[MAX_HOST_LEN];
351    mod_gnutls_handle_t *ctxt;
352#if USING_2_1_RECENT
353    vhost_cb_rec cbx;
354#else
355    server_rec* s;
356    mod_gnutls_srvconf_rec *tsc;   
357#endif
358   
359    ctxt = gnutls_transport_get_ptr(session);
360   
361    sni_type = gnutls_certificate_type_get(session);
362    if (sni_type != GNUTLS_CRT_X509) {
363        /* In theory, we could support OpenPGP Certificates. Theory != code. */
364        ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
365                     ctxt->c->base_server,
366                     "GnuTLS: Only x509 Certificates are currently supported.");
367        return -1;
368    }
369
370    ret->type = GNUTLS_CRT_X509;
371    ret->ncerts = 1;
372    ret->deinit_all = 0;
373   
374    rv = gnutls_server_name_get(ctxt->session, sni_name, 
375                                &data_len, &sni_type, 0);
376
377    if (rv != 0) {
378        goto use_default_crt;
379    }
380
381    if (sni_type != GNUTLS_NAME_DNS) {
382        ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
383                     ctxt->c->base_server,
384                     "GnuTLS: Unknown type '%d' for SNI: "
385                     "'%s'", sni_type, sni_name);       
386        goto use_default_crt;
387    }
388   
389    /**
390     * Code in the Core already sets up the c->base_server as the base
391     * for this IP/Port combo.  Trust that the core did the 'right' thing.
392     */
393#if USING_2_1_RECENT
394    cbx.ctxt = ctxt;
395    cbx.ret = ret;
396    cbx.sni_name = sni_name;
397
398    rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
399    if (rv == 1) {
400        return 0;
401    }
402#else
403    for (s = ap_server_conf; s; s = s->next) {
404       
405        tsc = (mod_gnutls_srvconf_rec *) ap_get_module_config(s->module_config,
406                                                             &gnutls_module);
407        if (tsc->enabled != GNUTLS_ENABLED_TRUE) {
408            continue;
409        }
410#if MOD_GNUTLS_DEBUG
411        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
412                     ctxt->c->base_server,
413                     "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X s->n: 0x%08X  sc: 0x%08X", tsc->cert_cn, rv,
414                     gnutls_pk_algorithm_get_name(gnutls_x509_privkey_get_pk_algorithm(ctxt->sc->privkey_x509)),
415                     (unsigned int)s, (unsigned int)s->next, (unsigned int)tsc);
416#endif           
417        /* The CN can contain a * -- this will match those too. */
418        if (ap_strcasecmp_match(sni_name, tsc->cert_cn) == 0) {
419            /* found a match */
420            ret->cert.x509 = &tsc->cert_x509;
421            ret->key.x509 = tsc->privkey_x509;
422#if MOD_GNUTLS_DEBUG
423            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
424                         ctxt->c->base_server,
425                         "GnuTLS: Virtual Host: "
426                         "'%s' == '%s'", tsc->cert_cn, sni_name);
427#endif
428            ctxt->sc = tsc;
429            mod_gnutls_changed_servers(ctxt);
430            return 0;
431        }
432    }
433#endif
434   
435    /**
436     * If the client does not support the Server Name Indication, give the default
437     * certificate for this server.
438     */
439use_default_crt:
440    ret->cert.x509 = &ctxt->sc->cert_x509;
441    ret->key.x509 = ctxt->sc->privkey_x509;
442#if MOD_GNUTLS_DEBUG
443    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
444                 ctxt->c->base_server,
445                 "GnuTLS: Using Default Certificate.");
446#endif
447    return 0;
448}
449
450static mod_gnutls_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c)
451{
452    mod_gnutls_handle_t *ctxt;
453    mod_gnutls_srvconf_rec *sc =
454        (mod_gnutls_srvconf_rec *) ap_get_module_config(c->base_server->
455                                                        module_config,
456                                                        &gnutls_module);
457
458    ctxt = apr_pcalloc(pool, sizeof(*ctxt));
459    ctxt->c = c;
460    ctxt->sc = sc;
461    ctxt->status = 0;
462
463    ctxt->input_rc = APR_SUCCESS;
464    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
465    ctxt->input_cbuf.length = 0;
466
467    ctxt->output_rc = APR_SUCCESS;
468    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
469    ctxt->output_blen = 0;
470    ctxt->output_length = 0;
471
472    gnutls_init(&ctxt->session, GNUTLS_SERVER);
473
474    gnutls_protocol_set_priority(ctxt->session, sc->protocol);
475    gnutls_cipher_set_priority(ctxt->session, sc->ciphers);
476    gnutls_compression_set_priority(ctxt->session, sc->compression);
477    gnutls_kx_set_priority(ctxt->session, sc->key_exchange);
478    gnutls_mac_set_priority(ctxt->session, sc->macs);
479    gnutls_certificate_type_set_priority(ctxt->session, sc->cert_types);
480
481    mod_gnutls_cache_session_init(ctxt);
482
483    gnutls_certificate_server_set_retrieve_function(sc->certs, cert_retrieve_fn);
484
485    mod_gnutls_changed_servers(ctxt);
486    return ctxt;
487}
488
489static int mod_gnutls_hook_pre_connection(conn_rec * c, void *csd)
490{
491    mod_gnutls_handle_t *ctxt;
492    mod_gnutls_srvconf_rec *sc =
493        (mod_gnutls_srvconf_rec *) ap_get_module_config(c->base_server->
494                                                        module_config,
495                                                        &gnutls_module);
496
497    if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) {
498        return DECLINED;
499    }
500
501    ctxt = create_gnutls_handle(c->pool, c);
502
503    ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
504
505    gnutls_transport_set_pull_function(ctxt->session,
506                                       mod_gnutls_transport_read);
507    gnutls_transport_set_push_function(ctxt->session,
508                                       mod_gnutls_transport_write);
509    gnutls_transport_set_ptr(ctxt->session, ctxt);
510   
511    ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, 
512                                             NULL, c);
513    ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt,
514                                               NULL, c);
515
516    return OK;
517}
518
519static int mod_gnutls_hook_fixups(request_rec *r)
520{
521    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
522    char buf[GNUTLS_SESSION_ID_STRING_LEN];
523    const char* tmp;
524    int len;
525    mod_gnutls_handle_t *ctxt;
526    apr_table_t *env = r->subprocess_env;
527
528    ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module);
529
530    if(!ctxt) {
531        return DECLINED;
532    }
533
534    apr_table_setn(env, "HTTPS", "on");
535
536    apr_table_setn(env, "GNUTLS_VERSION_INTERFACE", MOD_GNUTLS_VERSION);
537    apr_table_setn(env, "GNUTLS_VERSION_LIBRARY", LIBGNUTLS_VERSION);
538
539    apr_table_setn(env, "SSL_PROTOCOL",
540                   gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session)));
541
542    apr_table_setn(env, "SSL_CIPHER",
543                   gnutls_cipher_get_name(gnutls_cipher_get(ctxt->session)));
544
545    apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
546
547    tmp = apr_psprintf(r->pool, "%d",
548              8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session)));
549
550    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
551
552    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
553
554    len = sizeof(sbuf);
555    gnutls_session_get_id(ctxt->session, sbuf, &len);
556    tmp = mod_gnutls_session_id2sz(sbuf, len, buf, sizeof(buf));
557    apr_table_setn(env, "SSL_SESSION_ID", tmp);
558   
559    return OK;
560}
561
562static int load_datum_from_file(apr_pool_t* pool, 
563                                const char* file,
564                                gnutls_datum_t* data)
565{
566    apr_file_t* fp;
567    apr_finfo_t finfo;
568    apr_status_t rv;
569    apr_size_t br = 0;
570   
571    rv = apr_file_open(&fp, file, APR_READ|APR_BINARY, APR_OS_DEFAULT, 
572                       pool);
573    if (rv != APR_SUCCESS) {
574        return rv;
575    }
576   
577    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
578   
579    if (rv != APR_SUCCESS) {
580        return rv;
581    }
582   
583    data->data = apr_palloc(pool, finfo.size+1);
584    rv = apr_file_read_full(fp, data->data, finfo.size, &br);
585   
586    if (rv != APR_SUCCESS) {
587        return rv;
588    }
589    apr_file_close(fp);
590   
591    data->data[br] = '\0';
592    data->size = br;
593   
594    return 0;
595}
596
597static const char *gnutls_set_cert_file(cmd_parms * parms, void *dummy,
598                                        const char *arg)
599{
600    int ret;
601    gnutls_datum_t data;
602    const char* file;
603    apr_pool_t* spool;
604    mod_gnutls_srvconf_rec *sc =
605        (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server->
606                                                        module_config,
607                                                        &gnutls_module);
608    apr_pool_create(&spool, parms->pool);
609   
610    file = ap_server_root_relative(spool, arg);
611
612    if (load_datum_from_file(spool, file, &data) != 0) {
613        return apr_psprintf(parms->pool, "GnuTLS: Error Reading "
614                            "Certificate '%s'", file);
615    }
616   
617    gnutls_x509_crt_init(&sc->cert_x509);
618    ret = gnutls_x509_crt_import(sc->cert_x509, &data, GNUTLS_X509_FMT_PEM);
619    if (ret != 0) {
620        return apr_psprintf(parms->pool, "GnuTLS: Failed to Import "
621                            "Certificate'%s': (%d) %s", file, ret, 
622                            gnutls_strerror(ret));
623    }
624   
625    apr_pool_destroy(spool);
626    return NULL;
627}
628
629static const char *gnutls_set_key_file(cmd_parms * parms, void *dummy,
630                                       const char *arg)
631{
632    int ret;
633    gnutls_datum_t data;
634    const char* file;
635    apr_pool_t* spool;
636    mod_gnutls_srvconf_rec *sc =
637        (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server->
638                                                        module_config,
639                                                        &gnutls_module);
640    apr_pool_create(&spool, parms->pool);
641   
642    file = ap_server_root_relative(spool, arg);
643   
644    if (load_datum_from_file(spool, file, &data) != 0) {
645        return apr_psprintf(parms->pool, "GnuTLS: Error Reading "
646                            "Private Key '%s'", file);
647    }
648   
649    gnutls_x509_privkey_init(&sc->privkey_x509);
650    ret = gnutls_x509_privkey_import(sc->privkey_x509, &data, GNUTLS_X509_FMT_PEM);
651    if (ret != 0) {
652        return apr_psprintf(parms->pool, "GnuTLS: Failed to Import "
653                            "Private Key '%s': (%d) %s", file, ret, 
654                            gnutls_strerror(ret));
655    }
656    apr_pool_destroy(spool);
657    return NULL;
658}
659
660static const char *gnutls_set_cache(cmd_parms * parms, void *dummy,
661                                       const char *type, const char* arg)
662{
663    const char* err;
664    mod_gnutls_srvconf_rec *sc = ap_get_module_config(parms->server->
665                                                        module_config,
666                                                        &gnutls_module);
667    if ((err = ap_check_cmd_context(parms, GLOBAL_ONLY))) {
668        return err;
669    }
670
671    if (strcasecmp("none", type) == 0) {
672        sc->cache_type = mod_gnutls_cache_none;
673    }
674    else if (strcasecmp("dbm", type) == 0) {
675        sc->cache_type = mod_gnutls_cache_dbm;
676    }
677#if HAVE_APR_MEMCACHE
678    else if (strcasecmp("memcache", type) == 0) {
679        sc->cache_type = mod_gnutls_cache_memcache;
680    }
681#endif
682    else {
683        return "Invalid Type for GnuTLSCache!";
684    }
685
686    if (sc->cache_type == mod_gnutls_cache_dbm) {
687        sc->cache_config = ap_server_root_relative(parms->pool, arg);
688    }
689    else {
690        sc->cache_config = apr_pstrdup(parms->pool, arg);
691    }
692
693    return NULL;
694}
695
696static const char *gnutls_set_cache_timeout(cmd_parms * parms, void *dummy,
697                                            const char *arg)
698{
699    int argint;
700    mod_gnutls_srvconf_rec *sc =
701    (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server->
702                                                    module_config,
703                                                    &gnutls_module);
704   
705    argint = atoi(arg);
706   
707    if (argint < 0) {
708        return "GnuTLSCacheTimeout: Invalid argument";
709    }
710    else if (argint == 0) {
711        sc->cache_timeout = 0;
712    }
713    else {
714        sc->cache_timeout = apr_time_from_sec(argint);
715    }
716   
717    return NULL;
718}
719
720
721static const char *gnutls_set_client_verify(cmd_parms * parms, void *dummy,
722                                            const char *arg)
723{
724    int mode;
725
726    if (strcasecmp("none", arg) == 0 || strcasecmp("ignore", arg) == 0) {
727        mode = GNUTLS_CERT_IGNORE;
728    }
729    else if (strcasecmp("optional", arg) == 0 || strcasecmp("request", arg) == 0) {
730        mode = GNUTLS_CERT_REQUEST;
731    }
732    else if (strcasecmp("require", arg) == 0) {
733        mode = GNUTLS_CERT_REQUIRE;
734    }
735    else {
736        return "GnuTLSClientVerify: Invalid argument";
737    }
738   
739    /* This was set from a directory context */
740    if (parms->path) {
741        mod_gnutls_dirconf_rec *dc = (mod_gnutls_dirconf_rec *)dummy;
742        dc->client_verify_mode = mode;
743    }
744    else {
745        mod_gnutls_srvconf_rec *sc =
746        (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server->
747                                                        module_config,
748                                                        &gnutls_module);       
749        sc->client_verify_mode = mode;
750    }
751
752    return NULL;
753}
754
755static const char *gnutls_set_client_ca_file(cmd_parms * parms, void *dummy,
756                                            const char *arg)
757{
758    int rv;
759    const char* file;
760    mod_gnutls_srvconf_rec *sc = 
761        (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server->
762                                                        module_config,
763                                                        &gnutls_module);       
764    file = ap_server_root_relative(parms->pool, arg);
765    rv = gnutls_certificate_set_x509_trust_file(sc->certs, 
766                                                file, GNUTLS_X509_FMT_PEM);
767   
768    if (rv < 0) {
769        return apr_psprintf(parms->pool, "GnuTLS: Failed to load "
770                            "Client CA File '%s': (%d) %s", file, rv, 
771                            gnutls_strerror(rv));   
772    }
773    return NULL;
774}
775
776
777static const char *gnutls_set_enabled(cmd_parms * parms, void *dummy,
778                                      const char *arg)
779{
780    mod_gnutls_srvconf_rec *sc =
781        (mod_gnutls_srvconf_rec *) ap_get_module_config(parms->server->
782                                                        module_config,
783                                                        &gnutls_module);
784    if (!strcasecmp(arg, "On")) {
785        sc->enabled = GNUTLS_ENABLED_TRUE;
786    }
787    else if (!strcasecmp(arg, "Off")) {
788        sc->enabled = GNUTLS_ENABLED_FALSE;
789    }
790    else {
791        return "GnuTLSEnable must be set to 'On' or 'Off'";
792    }
793
794    return NULL;
795}
796
797static const command_rec gnutls_cmds[] = {
798    AP_INIT_TAKE1("GnuTLSClientVerify", gnutls_set_client_verify,
799                  NULL,
800                  RSRC_CONF|OR_AUTHCFG,
801                  "Set Verification Requirements of the Client Certificate"),
802    AP_INIT_TAKE1("GnuTLSClientCAFile", gnutls_set_client_ca_file,
803                  NULL,
804                  RSRC_CONF,
805                  "Set the CA File for Client Certificates"),
806    AP_INIT_TAKE1("GnuTLSCertificateFile", gnutls_set_cert_file,
807                  NULL,
808                  RSRC_CONF,
809                  "SSL Server Key file"),
810    AP_INIT_TAKE1("GnuTLSKeyFile", gnutls_set_key_file,
811                  NULL,
812                  RSRC_CONF,
813                  "SSL Server Certificate file"),
814    AP_INIT_TAKE1("GnuTLSCacheTimeout", gnutls_set_cache_timeout,
815                  NULL,
816                  RSRC_CONF,
817                  "Cache Timeout"),
818    AP_INIT_TAKE2("GnuTLSCache", gnutls_set_cache,
819                  NULL,
820                  RSRC_CONF,
821                  "Cache Configuration"),
822    AP_INIT_TAKE1("GnuTLSEnable", gnutls_set_enabled,
823                  NULL, RSRC_CONF,
824                  "Whether this server has GnuTLS Enabled. Default: Off"),
825
826    {NULL}
827};
828
829int mod_gnutls_hook_authz(request_rec *r)
830{
831    int rv;
832    int status;
833    mod_gnutls_handle_t *ctxt;
834    mod_gnutls_dirconf_rec *dc = ap_get_module_config(r->per_dir_config,
835                                                      &gnutls_module);
836   
837    ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module);
838
839    if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
840        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 
841                      "GnuTLS: Ignoring Client Certificate!");
842        return DECLINED;
843    }
844
845    if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
846        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 
847                     "GnuTLS: Attempting to rehandshake with peer. %d %d",
848                      ctxt->sc->client_verify_mode, dc->client_verify_mode);
849       
850        gnutls_certificate_server_set_request(ctxt->session,
851                                              dc->client_verify_mode);
852   
853        if (mod_gnutls_rehandshake(ctxt) != 0) {
854            return HTTP_FORBIDDEN;
855        }
856    }
857    else if (ctxt->sc->client_verify_mode == GNUTLS_CERT_IGNORE) {
858        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
859                      "GnuTLS: Peer is set to IGNORE");
860        return DECLINED;
861    }
862   
863    rv = gnutls_certificate_verify_peers2(ctxt->session, &status);
864
865    if (rv < 0) {
866        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
867                     "GnuTLS: Failed to Verify Peer: (%d) %s", 
868                     rv, gnutls_strerror(rv));
869        return HTTP_FORBIDDEN;
870    }
871   
872    if (status < 0) {
873        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
874                     "GnuTLS: Peer Status is invalid."); 
875        return HTTP_FORBIDDEN;
876    }
877   
878    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
879        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
880                     "GnuTLS: Could not find Signer for Peer Certificate"); 
881    }
882   
883    if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
884        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
885                     "GnuTLS: Could not find CA for Peer Certificate"); 
886    }
887   
888    if (status & GNUTLS_CERT_INVALID) {
889        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
890                     "GnuTLS: Peer Certificate is invalid."); 
891        return HTTP_FORBIDDEN;
892    }
893    else if (status & GNUTLS_CERT_REVOKED) {
894        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
895                     "GnuTLS: Peer Certificate is revoked."); 
896        return HTTP_FORBIDDEN;
897    }
898   
899    /* TODO: OpenPGP Certificates */
900    if (gnutls_certificate_type_get(ctxt->session) != GNUTLS_CRT_X509) {
901        ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, 
902                     "GnuTLS: Only x509 is supported for client certificates");         
903        return HTTP_FORBIDDEN;
904    }
905    /* TODO: Further Verification. */
906    ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, 
907                 "GnuTLS: Verified Peer.");             
908    return OK;
909}
910
911static void gnutls_hooks(apr_pool_t * p)
912{
913    ap_hook_pre_connection(mod_gnutls_hook_pre_connection, NULL, NULL,
914                           APR_HOOK_MIDDLE);
915    ap_hook_post_config(mod_gnutls_hook_post_config, NULL, NULL,
916                        APR_HOOK_MIDDLE);
917    ap_hook_child_init(mod_gnutls_hook_child_init, NULL, NULL,
918                        APR_HOOK_MIDDLE);
919#if USING_2_1_RECENT
920    ap_hook_http_scheme(mod_gnutls_hook_http_scheme, NULL, NULL,
921                        APR_HOOK_MIDDLE);
922#else
923    ap_hook_http_method(mod_gnutls_hook_http_scheme, NULL, NULL,
924                        APR_HOOK_MIDDLE);
925#endif
926    ap_hook_default_port(mod_gnutls_hook_default_port, NULL, NULL,
927                         APR_HOOK_MIDDLE);
928    ap_hook_pre_config(mod_gnutls_hook_pre_config, NULL, NULL,
929                       APR_HOOK_MIDDLE);
930   
931    ap_hook_access_checker(mod_gnutls_hook_authz, NULL, NULL, APR_HOOK_REALLY_FIRST);
932
933    ap_hook_fixups(mod_gnutls_hook_fixups, NULL, NULL, APR_HOOK_REALLY_FIRST);
934
935    /* TODO: HTTP Upgrade Filter */
936    /* ap_register_output_filter ("UPGRADE_FILTER",
937     *          ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5);
938     */
939    ap_register_input_filter(GNUTLS_INPUT_FILTER_NAME,
940                             mod_gnutls_filter_input, NULL,
941                             AP_FTYPE_CONNECTION + 5);
942    ap_register_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
943                              mod_gnutls_filter_output, NULL,
944                              AP_FTYPE_CONNECTION + 5);
945}
946
947static void *gnutls_config_server_create(apr_pool_t * p, server_rec * s)
948{
949    int i;
950    mod_gnutls_srvconf_rec *sc = apr_pcalloc(p, sizeof(*sc));
951
952    sc->enabled = GNUTLS_ENABLED_FALSE;
953
954    gnutls_certificate_allocate_credentials(&sc->certs);
955    sc->privkey_x509 = NULL;
956    sc->cert_x509 = NULL;
957    sc->cache_timeout = apr_time_from_sec(300);
958    sc->cache_type = mod_gnutls_cache_dbm;
959    sc->cache_config = ap_server_root_relative(p, "conf/gnutls_cache");
960
961    /* TODO: Make this Configurable. But it isn't configurable in mod_ssl? */
962    sc->dh_params_file = ap_server_root_relative(p, "conf/dhfile");
963    sc->rsa_params_file = ap_server_root_relative(p, "conf/rsafile");
964
965    /* Finish SSL Client Certificate Support */
966    sc->client_verify_mode = GNUTLS_CERT_IGNORE;
967
968    /* TODO: Make this Configurable ! */
969    /* mod_ssl uses a flex based parser for this part.. sigh */
970    i = 0;
971    sc->ciphers[i++] = GNUTLS_CIPHER_AES_256_CBC;
972    sc->ciphers[i++] = GNUTLS_CIPHER_AES_128_CBC;
973    sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_128;
974    sc->ciphers[i++] = GNUTLS_CIPHER_3DES_CBC;
975    sc->ciphers[i++] = GNUTLS_CIPHER_ARCFOUR_40;
976    sc->ciphers[i] = 0;
977
978    i = 0;
979    sc->key_exchange[i++] = GNUTLS_KX_RSA;
980    sc->key_exchange[i++] = GNUTLS_KX_RSA_EXPORT;
981    sc->key_exchange[i++] = GNUTLS_KX_DHE_DSS;
982    sc->key_exchange[i++] = GNUTLS_KX_DHE_RSA;
983    sc->key_exchange[i++] = GNUTLS_KX_ANON_DH;
984    sc->key_exchange[i++] = GNUTLS_KX_SRP;
985    sc->key_exchange[i++] = GNUTLS_KX_SRP_RSA;
986    sc->key_exchange[i++] = GNUTLS_KX_SRP_DSS;
987    sc->key_exchange[i] = 0;
988
989    i = 0;
990    sc->macs[i++] = GNUTLS_MAC_SHA;
991    sc->macs[i++] = GNUTLS_MAC_MD5;
992    sc->macs[i++] = GNUTLS_MAC_RMD160;
993    sc->macs[i] = 0;
994
995    i = 0;
996    sc->protocol[i++] = GNUTLS_TLS1_1;
997    sc->protocol[i++] = GNUTLS_TLS1;
998    sc->protocol[i++] = GNUTLS_SSL3;
999    sc->protocol[i] = 0;
1000
1001    i = 0;
1002    sc->compression[i++] = GNUTLS_COMP_NULL;
1003    sc->compression[i++] = GNUTLS_COMP_ZLIB;
1004    sc->compression[i++] = GNUTLS_COMP_LZO;
1005    sc->compression[i] = 0;
1006
1007    i = 0;
1008    sc->cert_types[i++] = GNUTLS_CRT_X509;
1009    sc->cert_types[i] = 0;
1010 
1011    return sc;
1012}
1013
1014void *gnutls_config_dir_create(apr_pool_t *p, char *dir)
1015{
1016    mod_gnutls_dirconf_rec *dc = apr_palloc(p, sizeof(*dc));
1017
1018    dc->client_verify_mode = -1;
1019   
1020    return dc;
1021}
1022
1023module AP_MODULE_DECLARE_DATA gnutls_module = {
1024    STANDARD20_MODULE_STUFF,
1025    gnutls_config_dir_create,
1026    NULL,
1027    gnutls_config_server_create,
1028    NULL,
1029/*    gnutls_config_server_merge, */
1030    gnutls_cmds,
1031    gnutls_hooks
1032};
Note: See TracBrowser for help on using the repository browser.