source: mod_gnutls/src/gnutls_hooks.c @ 7bebb42

debian/masterdebian/stretch-backportsjessie-backportsmsvaproxy-ticketupstream
Last change on this file since 7bebb42 was 7bebb42, checked in by Nokis Mavrogiannopoulos <nmav@…>, 13 years ago

upgraded to 0.4.0

  • Property mode set to 100644
File size: 25.3 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2007 Nikos Mavrogiannopoulos
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 */
18
19#include "mod_gnutls.h"
20#include "http_vhost.h"
21#include "ap_mpm.h"
22
23#if !USING_2_1_RECENT
24extern server_rec *ap_server_conf;
25#endif
26
27#if APR_HAS_THREADS
28GCRY_THREAD_OPTION_PTHREAD_IMPL;
29#endif
30
31#if MOD_GNUTLS_DEBUG
32static apr_file_t *debug_log_fp;
33#endif
34
35static int mpm_is_threaded;
36
37static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt);
38/* use side==0 for server and side==1 for client */
39static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert,
40                                     int side, int export_certificates_enabled);
41
42static apr_status_t mgs_cleanup_pre_config(void *data)
43{
44    gnutls_global_deinit();
45    return APR_SUCCESS;
46}
47
48#if MOD_GNUTLS_DEBUG
49static void gnutls_debug_log_all(int level, const char *str)
50{
51    apr_file_printf(debug_log_fp, "<%d> %s\n", level, str);
52}
53#endif
54
55int
56mgs_hook_pre_config(apr_pool_t * pconf,
57                    apr_pool_t * plog, apr_pool_t * ptemp)
58{
59
60#if APR_HAS_THREADS
61    ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_is_threaded);
62    if (mpm_is_threaded) {
63        gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
64    }
65#else
66    mpm_is_threaded = 0;
67#endif
68
69    gnutls_global_init();
70
71    apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config,
72                              apr_pool_cleanup_null);
73
74#if MOD_GNUTLS_DEBUG
75    apr_file_open(&debug_log_fp, "/tmp/gnutls_debug",
76                  APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
77                  pconf);
78
79    gnutls_global_set_log_level(9);
80    gnutls_global_set_log_function(gnutls_debug_log_all);
81#endif
82
83    return OK;
84}
85
86
87static gnutls_datum
88load_params(const char *file, server_rec * s, apr_pool_t * pool)
89{
90    gnutls_datum ret = { NULL, 0 };
91    apr_file_t *fp;
92    apr_finfo_t finfo;
93    apr_status_t rv;
94    apr_size_t br = 0;
95
96    rv = apr_file_open(&fp, file, APR_READ | APR_BINARY, APR_OS_DEFAULT,
97                       pool);
98    if (rv != APR_SUCCESS) {
99        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
100                     "GnuTLS failed to load params file at: %s. Will use internal params.", file);
101        return ret;
102    }
103
104    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
105
106    if (rv != APR_SUCCESS) {
107        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
108                     "GnuTLS failed to stat params file at: %s", file);
109        return ret;
110    }
111
112    ret.data = apr_palloc(pool, finfo.size + 1);
113    rv = apr_file_read_full(fp, ret.data, finfo.size, &br);
114
115    if (rv != APR_SUCCESS) {
116        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
117                     "GnuTLS failed to read params file at: %s", file);
118        return ret;
119    }
120    apr_file_close(fp);
121    ret.data[br] = '\0';
122    ret.size = br;
123
124    return ret;
125}
126
127/* We don't support openpgp certificates, yet */
128const static int cert_type_prio[2] = { GNUTLS_CRT_X509, 0 };
129
130static int mgs_select_virtual_server_cb( gnutls_session_t session)
131{
132    mgs_handle_t *ctxt;
133    mgs_srvconf_rec *tsc;
134    int ret;
135
136    ctxt = gnutls_transport_get_ptr(session);
137
138    /* find the virtual server */
139    tsc = mgs_find_sni_server(session);
140
141    if (tsc != NULL)
142        ctxt->sc = tsc;
143
144    gnutls_certificate_server_set_request(session,
145       ctxt->sc->client_verify_mode);
146
147    /* set the new server credentials
148     */
149
150    gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
151      ctxt->sc->certs);
152
153    gnutls_credentials_set(session, GNUTLS_CRD_ANON,
154      ctxt->sc->anon_creds);
155
156    if ( ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
157       gnutls_credentials_set(session, GNUTLS_CRD_SRP,
158         ctxt->sc->srp_creds);
159    }
160
161    /* update the priorities - to avoid negotiating a ciphersuite that is not
162     * enabled on this virtual server. Note that here we ignore the version
163     * negotiation.
164     */
165    ret = gnutls_priority_set( session, ctxt->sc->priorities);
166    gnutls_certificate_type_set_priority( session, cert_type_prio);
167   
168   
169    /* actually it shouldn't fail since we have checked at startup */
170    if (ret < 0) return ret;
171
172    /* allow separate caches per virtual host. Actually allowing the same is not
173     * a good idea, especially if they have different security requirements.
174     */
175    mgs_cache_session_init(ctxt);
176
177    return 0;
178}
179
180static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st * ret)
181{
182    mgs_handle_t *ctxt;
183
184    ctxt = gnutls_transport_get_ptr(session);
185
186    ret->type = GNUTLS_CRT_X509;
187    ret->ncerts = 1;
188    ret->deinit_all = 0;
189
190    ret->cert.x509 = &ctxt->sc->cert_x509;
191    ret->key.x509 = ctxt->sc->privkey_x509;
192    return 0;
193}
194
195const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n"
196"MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n"
197"gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n"
198"KRipli8Lk7hV+XmT7Jde6qgNdArb9P90c1nQQdXDPqcdKB5EaxR3O8qXtDoj+4AW\n"
199"dr0gekNsZIHx0rkHhxdGGludMuaI+HdIVEUjtSSw1X1ep3onddLs+gMs+9v1L7N4\n"
200"YWAnkATleuavh05zA85TKZzMBBx7wwjYKlaY86jQw4JxrjX46dv7tpS1yAPYn3rk\n"
201"Nd4jbVJfVHWbZeNy/NaO8g+nER+eSv9zAgEC\n"
202"-----END DH PARAMETERS-----\n";
203
204int
205mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
206                     apr_pool_t * ptemp, server_rec * base_server)
207{
208    int rv;
209    size_t data_len;
210    server_rec *s;
211    gnutls_dh_params_t dh_params;
212    gnutls_rsa_params_t rsa_params;
213    mgs_srvconf_rec *sc;
214    mgs_srvconf_rec *sc_base;
215    void *data = NULL;
216    int first_run = 0;
217    const char *userdata_key = "mgs_init";
218
219    apr_pool_userdata_get(&data, userdata_key, base_server->process->pool);
220    if (data == NULL) {
221        first_run = 1;
222        apr_pool_userdata_set((const void *) 1, userdata_key,
223                              apr_pool_cleanup_null,
224                              base_server->process->pool);
225    }
226
227
228    {
229        gnutls_datum pdata;
230        apr_pool_t *tpool;
231        s = base_server;
232        sc_base =
233            (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
234                                                     &gnutls_module);
235
236        apr_pool_create(&tpool, p);
237
238        gnutls_dh_params_init(&dh_params);
239
240        pdata = load_params(sc_base->dh_params_file, s, tpool);
241
242        if (pdata.size != 0) {
243            rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata,
244                                               GNUTLS_X509_FMT_PEM);
245            if (rv != 0) {
246                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
247                             "GnuTLS: Unable to load DH Params: (%d) %s",
248                             rv, gnutls_strerror(rv));
249                exit(rv);
250            }
251        } else {
252            /* If the file does not exist use internal parameters
253             */
254            pdata.data = (void*)static_dh_params;
255            pdata.size = sizeof( static_dh_params);
256            rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata,
257                                               GNUTLS_X509_FMT_PEM);
258
259            if (rv < 0) {
260              ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
261                         "GnuTLS: Unable to load internal DH Params."
262                         " Shutting down.");
263              exit(-1);
264            }
265        }
266        apr_pool_clear(tpool);
267
268        rsa_params = NULL;
269       
270        pdata = load_params(sc_base->rsa_params_file, s, tpool);
271
272        if (pdata.size != 0) {
273            gnutls_rsa_params_init(&rsa_params);
274            rv = gnutls_rsa_params_import_pkcs1(rsa_params, &pdata,
275                                                GNUTLS_X509_FMT_PEM);
276            if (rv != 0) {
277                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
278                             "GnuTLS: Unable to load RSA Params: (%d) %s",
279                             rv, gnutls_strerror(rv));
280                exit(rv);
281            }
282        } 
283        /* not an error but RSA-EXPORT ciphersuites are not available
284         */
285
286        apr_pool_destroy(tpool);
287        rv = mgs_cache_post_config(p, s, sc_base);
288        if (rv != 0) {
289            ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
290                         "GnuTLS: Post Config for GnuTLSCache Failed."
291                         " Shutting Down.");
292            exit(-1);
293        }
294
295        for (s = base_server; s; s = s->next) {
296            sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
297                                                          &gnutls_module);
298            sc->cache_type = sc_base->cache_type;
299            sc->cache_config = sc_base->cache_config;
300
301            if (rsa_params != NULL)
302              gnutls_certificate_set_rsa_export_params(sc->certs,
303                                                     rsa_params);
304            gnutls_certificate_set_dh_params(sc->certs, dh_params);
305
306            gnutls_anon_set_server_dh_params( sc->anon_creds, dh_params);
307           
308            gnutls_certificate_server_set_retrieve_function(sc->certs,
309                                                    cert_retrieve_fn);
310
311            if ( sc->srp_tpasswd_conf_file != NULL && sc->srp_tpasswd_file != NULL) {
312                gnutls_srp_set_server_credentials_file( sc->srp_creds, 
313                    sc->srp_tpasswd_file, sc->srp_tpasswd_conf_file);
314            }
315           
316            if (sc->cert_x509 == NULL
317                && sc->enabled == GNUTLS_ENABLED_TRUE) {
318                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
319                             "[GnuTLS] - Host '%s:%d' is missing a "
320                             "Certificate File!", s->server_hostname,
321                             s->port);
322                exit(-1);
323            }
324
325            if (sc->privkey_x509 == NULL
326                && sc->enabled == GNUTLS_ENABLED_TRUE) {
327                ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
328                             "[GnuTLS] - Host '%s:%d' is missing a "
329                             "Private Key File!",
330                             s->server_hostname, s->port);
331                exit(-1);
332            }
333
334            if (sc->enabled == GNUTLS_ENABLED_TRUE) {
335            rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509,
336                                               GNUTLS_OID_X520_COMMON_NAME,
337                                               0, 0, NULL, &data_len);
338
339            if (data_len < 1) {
340                sc->enabled = GNUTLS_ENABLED_FALSE;
341                sc->cert_cn = NULL;
342                continue;
343            }
344
345            sc->cert_cn = apr_palloc(p, data_len);
346            rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509,
347                                               GNUTLS_OID_X520_COMMON_NAME,
348                                               0, 0, sc->cert_cn,
349                                               &data_len);
350            }
351        }
352    }
353
354    ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION);
355
356    return OK;
357}
358
359void mgs_hook_child_init(apr_pool_t * p, server_rec * s)
360{
361    apr_status_t rv = APR_SUCCESS;
362    mgs_srvconf_rec *sc = ap_get_module_config(s->module_config,
363                                               &gnutls_module);
364
365    if (sc->cache_type != mgs_cache_none) {
366        rv = mgs_cache_child_init(p, s, sc);
367        if (rv != APR_SUCCESS) {
368            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
369                         "[GnuTLS] - Failed to run Cache Init");
370        }
371    } else {
372        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
373                     "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache");
374    }
375}
376
377const char *mgs_hook_http_scheme(const request_rec * r)
378{
379    mgs_srvconf_rec *sc =
380        (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config,
381                                                 &gnutls_module);
382
383    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
384        return NULL;
385    }
386
387    return "https";
388}
389
390apr_port_t mgs_hook_default_port(const request_rec * r)
391{
392    mgs_srvconf_rec *sc =
393        (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config,
394                                                 &gnutls_module);
395
396    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
397        return 0;
398    }
399
400    return 443;
401}
402
403#define MAX_HOST_LEN 255
404
405#if USING_2_1_RECENT
406typedef struct {
407    mgs_handle_t *ctxt;
408    mgs_srvconf_rec *sc;
409    const char *sni_name;
410} vhost_cb_rec;
411
412static int vhost_cb(void *baton, conn_rec * conn, server_rec * s)
413{
414    mgs_srvconf_rec *tsc;
415    vhost_cb_rec *x = baton;
416
417    tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
418                                                   &gnutls_module);
419
420    if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
421        return 0;
422    }
423
424    /* The CN can contain a * -- this will match those too. */
425    if (ap_strcasecmp_match(x->sni_name, tsc->cert_cn) == 0) {
426        /* found a match */
427#if MOD_GNUTLS_DEBUG
428        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
429                     x->ctxt->c->base_server,
430                     "GnuTLS: Virtual Host CB: "
431                     "'%s' == '%s'", tsc->cert_cn, x->sni_name);
432#endif
433        /* Because we actually change the server used here, we need to reset
434         * things like ClientVerify.
435         */
436        x->sc = tsc;
437        /* Shit. Crap. Dammit. We *really* should rehandshake here, as our
438         * certificate structure *should* change when the server changes.
439         * acccckkkkkk.
440         */
441        return 1;
442    }
443    return 0;
444}
445#endif
446
447mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
448{
449    int rv;
450    unsigned int sni_type;
451    size_t data_len = MAX_HOST_LEN;
452    char sni_name[MAX_HOST_LEN];
453    mgs_handle_t *ctxt;
454#if USING_2_1_RECENT
455    vhost_cb_rec cbx;
456#else
457    server_rec *s;
458    mgs_srvconf_rec *tsc;
459#endif
460
461    ctxt = gnutls_transport_get_ptr(session);
462
463    sni_type = gnutls_certificate_type_get(session);
464    if (sni_type != GNUTLS_CRT_X509) {
465        /* In theory, we could support OpenPGP Certificates. Theory != code. */
466        ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
467                     ctxt->c->base_server,
468                     "GnuTLS: Only x509 Certificates are currently supported.");
469        return NULL;
470    }
471
472    rv = gnutls_server_name_get(ctxt->session, sni_name,
473                                &data_len, &sni_type, 0);
474
475    if (rv != 0) {
476        return NULL;
477    }
478
479    if (sni_type != GNUTLS_NAME_DNS) {
480        ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
481                     ctxt->c->base_server,
482                     "GnuTLS: Unknown type '%d' for SNI: "
483                     "'%s'", sni_type, sni_name);
484        return NULL;
485    }
486
487    /**
488     * Code in the Core already sets up the c->base_server as the base
489     * for this IP/Port combo.  Trust that the core did the 'right' thing.
490     */
491#if USING_2_1_RECENT
492    cbx.ctxt = ctxt;
493    cbx.sc = NULL;
494    cbx.sni_name = sni_name;
495
496    rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
497    if (rv == 1) {
498        return cbx.sc;
499    }
500#else
501    for (s = ap_server_conf; s; s = s->next) {
502
503        tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
504                                                       &gnutls_module);
505        if (tsc->enabled != GNUTLS_ENABLED_TRUE) {
506            continue;
507        }
508#if MOD_GNUTLS_DEBUG
509        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
510                     ctxt->c->base_server,
511                     "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X s->n: 0x%08X  sc: 0x%08X",
512                     tsc->cert_cn, rv,
513                     gnutls_pk_algorithm_get_name
514                     (gnutls_x509_privkey_get_pk_algorithm
515                      (ctxt->sc->privkey_x509)), (unsigned int) s,
516                     (unsigned int) s->next, (unsigned int) tsc);
517#endif
518        /* The CN can contain a * -- this will match those too. */
519        if (ap_strcasecmp_match(sni_name, tsc->cert_cn) == 0) {
520#if MOD_GNUTLS_DEBUG
521            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
522                         ctxt->c->base_server,
523                         "GnuTLS: Virtual Host: "
524                         "'%s' == '%s'", tsc->cert_cn, sni_name);
525#endif
526            return tsc;
527        }
528    }
529#endif
530    return NULL;
531}
532
533
534static const int protocol_priority[] = {
535  GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
536         
537
538static mgs_handle_t *create_gnutls_handle(apr_pool_t * pool, conn_rec * c)
539{
540    mgs_handle_t *ctxt;
541    mgs_srvconf_rec *sc =
542        (mgs_srvconf_rec *) ap_get_module_config(c->base_server->
543                                                 module_config,
544                                                 &gnutls_module);
545
546    ctxt = apr_pcalloc(pool, sizeof(*ctxt));
547    ctxt->c = c;
548    ctxt->sc = sc;
549    ctxt->status = 0;
550
551    ctxt->input_rc = APR_SUCCESS;
552    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
553    ctxt->input_cbuf.length = 0;
554
555    ctxt->output_rc = APR_SUCCESS;
556    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
557    ctxt->output_blen = 0;
558    ctxt->output_length = 0;
559
560    gnutls_init(&ctxt->session, GNUTLS_SERVER);
561   
562    /* This is not very good as it trades security for compatibility,
563     * but it is the only way to be ultra-portable.
564     */
565    gnutls_session_enable_compatibility_mode( ctxt->session);
566   
567    /* because we don't set any default priorities here (we set later at
568     * the user hello callback) we need to at least set this in order for
569     * gnutls to be able to read packets.
570     */
571    gnutls_protocol_set_priority( ctxt->session, protocol_priority);
572
573    gnutls_handshake_set_post_client_hello_function( ctxt->session, mgs_select_virtual_server_cb);
574
575    return ctxt;
576}
577
578int mgs_hook_pre_connection(conn_rec * c, void *csd)
579{
580    mgs_handle_t *ctxt;
581    mgs_srvconf_rec *sc =
582        (mgs_srvconf_rec *) ap_get_module_config(c->base_server->
583                                                 module_config,
584                                                 &gnutls_module);
585
586    if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) {
587        return DECLINED;
588    }
589
590    ctxt = create_gnutls_handle(c->pool, c);
591
592    ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
593
594    gnutls_transport_set_pull_function(ctxt->session, mgs_transport_read);
595    gnutls_transport_set_push_function(ctxt->session, mgs_transport_write);
596    gnutls_transport_set_ptr(ctxt->session, ctxt);
597
598    ctxt->input_filter =
599        ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c);
600    ctxt->output_filter =
601        ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c);
602
603    return OK;
604}
605
606int mgs_hook_fixups(request_rec * r)
607{
608    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
609    char buf[AP_IOBUFSIZE];
610    const char *tmp;
611    size_t len;
612    mgs_handle_t *ctxt;
613    int rv = OK;
614
615    apr_table_t *env = r->subprocess_env;
616
617    ctxt =
618        ap_get_module_config(r->connection->conn_config, &gnutls_module);
619
620    if (!ctxt) {
621        return DECLINED;
622    }
623
624    apr_table_setn(env, "HTTPS", "on");
625
626    apr_table_setn(env, "SSL_VERSION_LIBRARY", "GnuTLS/"LIBGNUTLS_VERSION);
627    apr_table_setn(env, "SSL_VERSION_INTERFACE", "mod_gnutls/"MOD_GNUTLS_VERSION);
628
629    apr_table_setn(env, "SSL_PROTOCOL",
630                   gnutls_protocol_get_name(gnutls_protocol_get_version
631                                            (ctxt->session)));
632
633    /* should have been called SSL_CIPHERSUITE instead */                                         
634    apr_table_setn(env, "SSL_CIPHER",
635          gnutls_cipher_suite_get_name(
636            gnutls_kx_get(ctxt->session), gnutls_cipher_get(ctxt->session),
637                    gnutls_mac_get(ctxt->session)));
638
639    apr_table_setn(env, "SSL_COMPRESS_METHOD",
640                   gnutls_compression_get_name(gnutls_compression_get
641                                          (ctxt->session)));
642
643    apr_table_setn(env, "SSL_SRP_USER",
644                   gnutls_srp_server_get_username( ctxt->session));
645
646    if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
647        apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
648
649    unsigned int key_size =
650        8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
651    tmp = apr_psprintf(r->pool, "%u", key_size);
652
653    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
654
655    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
656
657    apr_table_setn(env, "SSL_CIPHER_EXPORT",
658                   (key_size <= 40) ? "true" : "false");
659
660    len = sizeof(sbuf);
661    gnutls_session_get_id(ctxt->session, sbuf, &len);
662    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
663    apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
664
665    mgs_add_common_cert_vars(r, ctxt->sc->cert_x509, 0, ctxt->sc->export_certificates_enabled);
666
667    return rv;
668}
669
670int mgs_hook_authz(request_rec * r)
671{
672    int rv;
673    mgs_handle_t *ctxt;
674    mgs_dirconf_rec *dc = ap_get_module_config(r->per_dir_config,
675                                               &gnutls_module);
676
677    ctxt =
678        ap_get_module_config(r->connection->conn_config, &gnutls_module);
679
680    if (!ctxt) {
681        return DECLINED;
682    }
683
684    if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
685        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
686                      "GnuTLS: Directory set to Ignore Client Certificate!");
687    } else {
688        if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
689            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
690                          "GnuTLS: Attempting to rehandshake with peer. %d %d",
691                          ctxt->sc->client_verify_mode,
692                          dc->client_verify_mode);
693
694            gnutls_certificate_server_set_request(ctxt->session,
695                                                  dc->client_verify_mode);
696
697            if (mgs_rehandshake(ctxt) != 0) {
698                return HTTP_FORBIDDEN;
699            }
700        } else if (ctxt->sc->client_verify_mode == GNUTLS_CERT_IGNORE) {
701#if MOD_GNUTLS_DEBUG
702            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
703                          "GnuTLS: Peer is set to IGNORE");
704#endif
705        } else {
706            rv = mgs_cert_verify(r, ctxt);
707            if (rv != DECLINED) {
708                return rv;
709            }
710        }
711    }
712
713    return DECLINED;
714}
715
716/* variables that are not sent by default:
717 *
718 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
719 * SSL_SERVER_CERT      string  PEM-encoded client certificate
720 */
721
722/* side is either 0 for SERVER or 1 for CLIENT
723 */
724#define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT")
725static void
726mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side, int export_certificates_enabled)
727{
728    unsigned char sbuf[64];     /* buffer to hold serials */
729    char buf[AP_IOBUFSIZE];
730    const char *tmp;
731    size_t len;
732    int alg;
733
734    apr_table_t *env = r->subprocess_env;
735
736    if (export_certificates_enabled != 0) {
737      char cert_buf[10*1024];
738      len = sizeof(cert_buf);
739
740      if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0)
741        apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL),
742                   apr_pstrmemdup(r->pool, cert_buf, len));
743     
744    }
745
746    len = sizeof(buf);
747    gnutls_x509_crt_get_dn(cert, buf, &len);
748    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_S_DN", NULL),
749                   apr_pstrmemdup(r->pool, buf, len));
750
751    len = sizeof(buf);
752    gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
753    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_I_DN", NULL),
754                   apr_pstrmemdup(r->pool, buf, len));
755
756    len = sizeof(sbuf);
757    gnutls_x509_crt_get_serial(cert, sbuf, &len);
758    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
759    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_M_SERIAL", NULL),
760                   apr_pstrdup(r->pool, tmp));
761
762    tmp =
763        mgs_time2sz(gnutls_x509_crt_get_expiration_time
764                    (cert), buf, sizeof(buf));
765    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL),
766                   apr_pstrdup(r->pool, tmp));
767
768    tmp =
769        mgs_time2sz(gnutls_x509_crt_get_activation_time
770                    (cert), buf, sizeof(buf));
771    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL),
772                   apr_pstrdup(r->pool, tmp));
773
774    alg = gnutls_x509_crt_get_signature_algorithm( cert);
775    if (alg >= 0) {
776      apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_SIG", NULL),
777          gnutls_sign_algorithm_get_name( alg));
778    }
779
780    alg = gnutls_x509_crt_get_pk_algorithm( cert, NULL);
781    if (alg >= 0) {
782      apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY", NULL),
783          gnutls_pk_algorithm_get_name( alg));
784    }
785
786
787}
788
789
790static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt)
791{
792    const gnutls_datum_t *cert_list;
793    unsigned int cert_list_size, status, expired;
794    int rv, ret;
795    gnutls_x509_crt_t cert;
796    apr_time_t activation_time, expiration_time, cur_time;
797
798    cert_list =
799        gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
800
801    if (cert_list == NULL || cert_list_size == 0) {
802        /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
803         */
804        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
805            return OK;
806
807        /* no certificate provided by the client, but one was required. */
808        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
809                      "GnuTLS: Failed to Verify Peer: "
810                      "Client did not submit a certificate");
811        return HTTP_FORBIDDEN;
812    }
813
814    if (cert_list_size > 1) {
815        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
816                      "GnuTLS: Failed to Verify Peer: "
817                      "Chained Client Certificates are not supported.");
818        return HTTP_FORBIDDEN;
819    }
820
821    gnutls_x509_crt_init(&cert);
822    rv = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
823    if (rv < 0) {
824        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
825                      "GnuTLS: Failed to Verify Peer: "
826                      "Failed to import peer certificates.");
827        ret = HTTP_FORBIDDEN;
828        goto exit;
829    }
830
831    apr_time_ansi_put(&expiration_time,
832                      gnutls_x509_crt_get_expiration_time(cert));
833    apr_time_ansi_put(&activation_time,
834                      gnutls_x509_crt_get_activation_time(cert));
835
836    rv = gnutls_x509_crt_verify(cert, ctxt->sc->ca_list,
837                                ctxt->sc->ca_list_size, 0, &status);
838
839    if (rv < 0) {
840        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
841                      "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
842                      rv, gnutls_strerror(rv));
843        ret = HTTP_FORBIDDEN;
844        goto exit;
845    }
846
847    expired = 0;
848    cur_time = apr_time_now();
849    if (activation_time > cur_time) {
850        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
851                      "GnuTLS: Failed to Verify Peer: "
852                      "Peer Certificate is not yet activated.");
853        expired = 1;
854    }
855
856    if (expiration_time < cur_time) {
857        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
858                      "GnuTLS: Failed to Verify Peer: "
859                      "Peer Certificate is expired.");
860        expired = 1;
861    }
862
863    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
864        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
865                      "GnuTLS: Could not find Signer for Peer Certificate");
866    }
867
868    if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
869        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
870                      "GnuTLS: Peer's Certificate signer is not a CA");
871    }
872
873    if (status & GNUTLS_CERT_INVALID) {
874        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
875                      "GnuTLS: Peer Certificate is invalid.");
876    } else if (status & GNUTLS_CERT_REVOKED) {
877        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
878                      "GnuTLS: Peer Certificate is revoked.");
879    }
880
881    /* TODO: Further Verification. */
882    /* Revocation is X.509 non workable paradigm, I really doubt implementation
883     * is worth doing --nmav
884     */
885/// ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size);
886
887//    mgs_hook_fixups(r);
888//    rv = mgs_authz_lua(r);
889
890    mgs_add_common_cert_vars(r, cert, 1, ctxt->sc->export_certificates_enabled);
891
892    {
893      /* days remaining */
894      unsigned long remain = (apr_time_sec(expiration_time) - apr_time_sec(cur_time))/86400;
895      apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN", 
896          apr_psprintf(r->pool, "%lu", remain));
897    }
898
899    if (status == 0 && expired == 0) {
900        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY", "SUCCESS");
901        ret = OK;
902    } else {
903        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY", "FAILED");
904        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
905            ret = OK;
906        else
907            ret = HTTP_FORBIDDEN;
908    }
909
910  exit:
911    gnutls_x509_crt_deinit(cert);
912    return ret;
913
914
915}
Note: See TracBrowser for help on using the repository browser.