source: mod_gnutls/src/gnutls_hooks.c @ 41f7031

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since 41f7031 was 41f7031, checked in by Nokis Mavrogiannopoulos <nmav@…>, 12 years ago

some fixes in alternative name support

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