source: mod_gnutls/src/mod_gnutls.c @ 316bd8c

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