source: mod_gnutls/src/gnutls_hooks.c @ 2aaf4f5

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 2aaf4f5 was 2aaf4f5, checked in by Daniel Kahn Gillmor <dkg@…>, 6 years ago

implement GnuTLSExportCertificates control over max exported cert size

This patchset implements the proposed modification to
GnuTLSExportCertificates, allowing server administrators to choose the
maximum size of the exported certs.

Some advantages:

  • avoids large buffers on the stack
  • more configurable for server admins who expect to use larger certs
  • better visibilty for users when a too-large-cert is encountered

This also increases the default maximum exported size from 10KiB to
16KiB.

  • Property mode set to 100644
File size: 53.7 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *
6 *  Licensed under the Apache License, Version 2.0 (the "License");
7 *  you may not use this file except in compliance with the License.
8 *  You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 *  Unless required by applicable law or agreed to in writing, software
13 *  distributed under the License is distributed on an "AS IS" BASIS,
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 *
18 */
19
20#include "mod_gnutls.h"
21#include "http_vhost.h"
22#include "ap_mpm.h"
23#include "mod_status.h"
24
25#ifdef ENABLE_MSVA
26#include <msv/msv.h>
27#endif
28
29#ifdef APLOG_USE_MODULE
30APLOG_USE_MODULE(gnutls);
31#endif
32
33#if !USING_2_1_RECENT
34extern server_rec *ap_server_conf;
35#endif
36
37#if MOD_GNUTLS_DEBUG
38static apr_file_t *debug_log_fp;
39#endif
40
41static gnutls_datum_t session_ticket_key = {NULL, 0};
42
43static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt);
44/* use side==0 for server and side==1 for client */
45static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, int export_cert_size);
46static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, int export_cert_size);
47static const char* mgs_x509_construct_uid(request_rec * pool, gnutls_x509_crt_t cert);
48static int mgs_status_hook(request_rec *r, int flags);
49
50/* Pool Cleanup Function */
51apr_status_t mgs_cleanup_pre_config(void *data) {
52        /* Free all session data */
53    gnutls_free(session_ticket_key.data);
54    session_ticket_key.data = NULL;
55    session_ticket_key.size = 0;
56        /* Deinitialize GnuTLS Library */
57    gnutls_global_deinit();
58    return APR_SUCCESS;
59}
60
61/* Logging Function for Maintainers */
62#if MOD_GNUTLS_DEBUG
63static void gnutls_debug_log_all(int level, const char *str) {
64    apr_file_printf(debug_log_fp, "<%d> %s", level, str);
65}
66#define _gnutls_log apr_file_printf
67#else
68#define _gnutls_log(...)
69#endif
70
71static const char* mgs_readable_cvm(mgs_client_verification_method_e m) {
72    switch(m) {
73    case mgs_cvm_unset:
74        return "unset";
75    case mgs_cvm_cartel:
76        return "cartel";
77    case mgs_cvm_msva:
78        return "msva";
79    }
80    return "unknown";
81}
82
83/* Pre-Configuration HOOK: Runs First */
84int mgs_hook_pre_config(apr_pool_t * pconf, apr_pool_t * plog, apr_pool_t * ptemp) {
85
86/* Maintainer Logging */
87#if MOD_GNUTLS_DEBUG
88    apr_file_open(&debug_log_fp, "/tmp/gnutls_debug", APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pconf);
89    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
90    gnutls_global_set_log_level(9);
91    gnutls_global_set_log_function(gnutls_debug_log_all);
92    _gnutls_log(debug_log_fp, "gnutls: %s\n", gnutls_check_version(NULL));
93#endif
94
95    int ret;
96
97        /* Check for required GnuTLS Library Version */
98    if (gnutls_check_version(LIBGNUTLS_VERSION) == NULL) {
99                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_check_version() failed. Required: "
100                                        "gnutls-%s Found: gnutls-%s", LIBGNUTLS_VERSION, gnutls_check_version(NULL));
101        return DONE;
102    }
103
104        /* Initialize GnuTLS Library */
105    ret = gnutls_global_init();
106    if (ret < 0) {
107                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_global_init: %s", gnutls_strerror(ret));
108                return DONE;
109    }
110
111        /* Generate a Session Key */
112    ret = gnutls_session_ticket_key_generate(&session_ticket_key);
113    if (ret < 0) {
114                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_session_ticket_key_generate: %s", gnutls_strerror(ret));
115                return DONE;
116    }
117
118    AP_OPTIONAL_HOOK(status_hook, mgs_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
119
120        /* Register a pool clean-up function */
121    apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config, apr_pool_cleanup_null);
122
123    return OK;
124}
125
126static int mgs_select_virtual_server_cb(gnutls_session_t session) {
127
128    mgs_handle_t *ctxt = NULL;
129    mgs_srvconf_rec *tsc = NULL;
130    int ret = 0;
131
132    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
133
134    ctxt = gnutls_transport_get_ptr(session);
135
136    /* find the virtual server */
137    tsc = mgs_find_sni_server(session);
138
139    if (tsc != NULL) {
140        // Found a TLS vhost based on the SNI from the client; use it instead.
141        ctxt->sc = tsc;
142        }
143
144    gnutls_certificate_server_set_request(session, ctxt->sc->client_verify_mode);
145
146    /* Set Anon credentials */
147    gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctxt->sc->certs);
148        /* Set x509 credentials */
149    gnutls_credentials_set(session, GNUTLS_CRD_ANON, ctxt->sc->anon_creds);
150
151#ifdef ENABLE_SRP
152        /* Set SRP credentials */
153    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
154        gnutls_credentials_set(session, GNUTLS_CRD_SRP, ctxt->sc->srp_creds);
155    }
156#endif
157
158    /* update the priorities - to avoid negotiating a ciphersuite that is not
159     * enabled on this virtual server. Note that here we ignore the version
160     * negotiation.
161     */
162
163    ret = gnutls_priority_set(session, ctxt->sc->priorities);
164    /* actually it shouldn't fail since we have checked at startup */
165    return ret;
166
167}
168
169static int cert_retrieve_fn(gnutls_session_t session,
170                                                        const gnutls_datum_t * req_ca_rdn, int nreqs,
171                                                        const gnutls_pk_algorithm_t * pk_algos, int pk_algos_length,
172                                                        gnutls_retr2_st *ret) {
173
174
175        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
176
177        mgs_handle_t *ctxt;
178
179    if (session == NULL) {
180                // ERROR INVALID SESSION
181                ret->ncerts = 0;
182                ret->deinit_all = 1;
183        return -1;
184        }
185    ctxt = gnutls_transport_get_ptr(session);
186
187    if (gnutls_certificate_type_get(session) == GNUTLS_CRT_X509) {
188                // X509 CERTIFICATE
189                ret->cert_type = GNUTLS_CRT_X509;
190                ret->key_type = GNUTLS_PRIVKEY_X509;
191        ret->ncerts = ctxt->sc->certs_x509_chain_num;
192        ret->deinit_all = 0;
193        ret->cert.x509 = ctxt->sc->certs_x509_chain;
194        ret->key.x509 = ctxt->sc->privkey_x509;
195        return 0;
196    } else if (gnutls_certificate_type_get(session) == GNUTLS_CRT_OPENPGP) {
197                // OPENPGP CERTIFICATE
198                ret->cert_type = GNUTLS_CRT_OPENPGP;
199                ret->key_type = GNUTLS_PRIVKEY_OPENPGP;
200        ret->ncerts = 1;
201        ret->deinit_all = 0;
202        ret->cert.pgp = ctxt->sc->cert_pgp;
203        ret->key.pgp = ctxt->sc->privkey_pgp;
204        return 0;
205    } else {
206                // UNKNOWN CERTIFICATE
207                ret->ncerts = 0;
208                ret->deinit_all = 1;
209            return -1;
210        }
211}
212
213/* 2048-bit group parameters from SRP specification */
214const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n"
215        "MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n"
216        "gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n"
217        "KRipli8Lk7hV+XmT7Jde6qgNdArb9P90c1nQQdXDPqcdKB5EaxR3O8qXtDoj+4AW\n"
218        "dr0gekNsZIHx0rkHhxdGGludMuaI+HdIVEUjtSSw1X1ep3onddLs+gMs+9v1L7N4\n"
219        "YWAnkATleuavh05zA85TKZzMBBx7wwjYKlaY86jQw4JxrjX46dv7tpS1yAPYn3rk\n"
220        "Nd4jbVJfVHWbZeNy/NaO8g+nER+eSv9zAgEC\n"
221        "-----END DH PARAMETERS-----\n";
222
223/* Read the common name or the alternative name of the certificate.
224 * We only support a single name per certificate.
225 *
226 * Returns negative on error.
227 */
228static int read_crt_cn(server_rec * s, apr_pool_t * p, gnutls_x509_crt_t cert, char **cert_cn) {
229
230    int rv = 0, i;
231    size_t data_len;
232
233
234    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
235    *cert_cn = NULL;
236
237    data_len = 0;
238    rv = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, NULL, &data_len);
239
240    if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
241        *cert_cn = apr_palloc(p, data_len);
242        rv = gnutls_x509_crt_get_dn_by_oid(cert,
243                GNUTLS_OID_X520_COMMON_NAME,
244                0, 0, *cert_cn,
245                &data_len);
246    } else { /* No CN return subject alternative name */
247        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
248                "No common name found in certificate for '%s:%d'. Looking for subject alternative name...",
249                s->server_hostname, s->port);
250        rv = 0;
251        /* read subject alternative name */
252        for (i = 0; !(rv < 0); i++) {
253            data_len = 0;
254            rv = gnutls_x509_crt_get_subject_alt_name(cert, i,
255                    NULL,
256                    &data_len,
257                    NULL);
258
259            if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER
260                    && data_len > 1) {
261                /* FIXME: not very efficient. What if we have several alt names
262                 * before DNSName?
263                 */
264                *cert_cn = apr_palloc(p, data_len + 1);
265
266                rv = gnutls_x509_crt_get_subject_alt_name
267                        (cert, i, *cert_cn, &data_len, NULL);
268                (*cert_cn)[data_len] = 0;
269
270                if (rv == GNUTLS_SAN_DNSNAME)
271                    break;
272            }
273        }
274    }
275
276    return rv;
277}
278
279static int read_pgpcrt_cn(server_rec * s, apr_pool_t * p,
280        gnutls_openpgp_crt_t cert, char **cert_cn) {
281    int rv = 0;
282    size_t data_len;
283
284
285    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
286    *cert_cn = NULL;
287
288    data_len = 0;
289    rv = gnutls_openpgp_crt_get_name(cert, 0, NULL, &data_len);
290
291    if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
292        *cert_cn = apr_palloc(p, data_len);
293        rv = gnutls_openpgp_crt_get_name(cert, 0, *cert_cn,
294                &data_len);
295    } else { /* No CN return subject alternative name */
296        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
297                "No name found in PGP certificate for '%s:%d'.",
298                s->server_hostname, s->port);
299    }
300
301    return rv;
302}
303
304int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, server_rec * base_server) {
305
306    int rv;
307    server_rec *s;
308    gnutls_dh_params_t dh_params = NULL;
309    mgs_srvconf_rec *sc;
310    mgs_srvconf_rec *sc_base;
311    void *data = NULL;
312    const char *userdata_key = "mgs_init";
313
314    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
315
316    apr_pool_userdata_get(&data, userdata_key, base_server->process->pool);
317    if (data == NULL) {
318        apr_pool_userdata_set((const void *) 1, userdata_key, apr_pool_cleanup_null, base_server->process->pool);
319    }
320
321
322    s = base_server;
323    sc_base = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
324
325    gnutls_dh_params_init(&dh_params);
326
327    if (sc_base->dh_params == NULL) {
328        gnutls_datum_t pdata = {
329            (void *) static_dh_params,
330            sizeof(static_dh_params)
331        };
332        rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata, GNUTLS_X509_FMT_PEM);
333        /* Generate DH Params
334        int dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
335                GNUTLS_SEC_PARAM_NORMAL);
336        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
337            "GnuTLS: Generating DH Params of %i bits.  "
338            "To avoid this use GnuTLSDHFile to specify DH Params for this host",
339            dh_bits);
340#if MOD_GNUTLS_DEBUG
341            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
342                    "GnuTLS: Generated DH Params of %i bits",dh_bits);
343#endif
344        rv = gnutls_dh_params_generate2 (dh_params,dh_bits);
345        */
346        if (rv < 0) {
347            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
348                    "GnuTLS: Unable to generate or load DH Params: (%d) %s",
349                    rv, gnutls_strerror(rv));
350            exit(rv);
351        }
352    } else {
353        dh_params = sc_base->dh_params;
354    }
355
356    rv = mgs_cache_post_config(p, s, sc_base);
357    if (rv != 0) {
358        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
359                "GnuTLS: Post Config for GnuTLSCache Failed."
360                " Shutting Down.");
361        exit(-1);
362    }
363
364    for (s = base_server; s; s = s->next) {
365        sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
366        sc->cache_type = sc_base->cache_type;
367        sc->cache_config = sc_base->cache_config;
368        sc->cache_timeout = sc_base->cache_timeout;
369
370        /* defaults for unset values: */
371        if (sc->enabled == GNUTLS_ENABLED_UNSET)
372            sc->enabled = GNUTLS_ENABLED_FALSE;
373        if (sc->tickets == GNUTLS_ENABLED_UNSET)
374            sc->tickets = GNUTLS_ENABLED_TRUE;
375        if (sc->export_certificates_size < 0)
376            sc->export_certificates_size = 0;
377        if (sc->client_verify_mode ==  -1)
378            sc->client_verify_mode = GNUTLS_CERT_IGNORE;
379        if (sc->client_verify_method ==  mgs_cvm_unset)
380            sc->client_verify_method = mgs_cvm_cartel;
381
382
383        /* Check if the priorities have been set */
384        if (sc->priorities == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
385            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
386                    "GnuTLS: Host '%s:%d' is missing the GnuTLSPriorities directive!",
387                    s->server_hostname, s->port);
388            exit(-1);
389        }
390
391        /* Check if DH params have been set per host */
392        if (sc->dh_params != NULL) {
393            gnutls_certificate_set_dh_params(sc->certs, sc->dh_params);
394            gnutls_anon_set_server_dh_params(sc->anon_creds, sc->dh_params);
395        } else if (dh_params) {
396            gnutls_certificate_set_dh_params(sc->certs, dh_params);
397            gnutls_anon_set_server_dh_params(sc->anon_creds, dh_params);
398        }
399
400        gnutls_certificate_set_retrieve_function(sc->certs, cert_retrieve_fn);
401
402#ifdef ENABLE_SRP
403        if (sc->srp_tpasswd_conf_file != NULL
404                && sc->srp_tpasswd_file != NULL) {
405            rv = gnutls_srp_set_server_credentials_file
406                    (sc->srp_creds, sc->srp_tpasswd_file,
407                    sc->srp_tpasswd_conf_file);
408
409            if (rv < 0 && sc->enabled == GNUTLS_ENABLED_TRUE) {
410                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0,
411                        s,
412                        "[GnuTLS] - Host '%s:%d' is missing a "
413                        "SRP password or conf File!",
414                        s->server_hostname, s->port);
415                exit(-1);
416            }
417        }
418#endif
419
420        if ((sc->certs_x509_chain == NULL || sc->certs_x509_chain_num < 1) &&
421            sc->cert_pgp == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
422                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
423                                                "[GnuTLS] - Host '%s:%d' is missing a Certificate File!",
424                                                s->server_hostname, s->port);
425            exit(-1);
426        }
427
428        if (sc->enabled == GNUTLS_ENABLED_TRUE &&
429            ((sc->certs_x509_chain != NULL && sc->certs_x509_chain_num > 0 && sc->privkey_x509 == NULL) ||
430             (sc->cert_pgp != NULL && sc->privkey_pgp == NULL))) {
431                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
432                                                "[GnuTLS] - Host '%s:%d' is missing a Private Key File!",
433                                                s->server_hostname, s->port);
434            exit(-1);
435        }
436
437        if (sc->enabled == GNUTLS_ENABLED_TRUE) {
438            rv = -1;
439            if (sc->certs_x509_chain_num > 0) {
440                rv = read_crt_cn(s, p, sc->certs_x509_chain[0], &sc->cert_cn);
441            }
442            if (rv < 0 && sc->cert_pgp != NULL) {
443                rv = read_pgpcrt_cn(s, p, sc->cert_pgp, &sc->cert_cn);
444                        }
445
446            if (rv < 0) {
447                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
448                                                        "[GnuTLS] - Cannot find a certificate for host '%s:%d'!",
449                                                        s->server_hostname, s->port);
450                sc->cert_cn = NULL;
451                continue;
452            }
453        }
454    }
455
456
457    ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION);
458
459    {
460        const char* libvers = gnutls_check_version(NULL);
461        char* gnutls_version = NULL;
462        if(libvers && (gnutls_version = apr_psprintf(p, "GnuTLS/%s", libvers))) {
463            ap_add_version_component(p, gnutls_version);
464        } else {
465            // In case we could not create the above string go for the static version instead
466            ap_add_version_component(p, "GnuTLS/" GNUTLS_VERSION "-static");
467        }
468    }
469
470    return OK;
471}
472
473void mgs_hook_child_init(apr_pool_t * p, server_rec * s) {
474    apr_status_t rv = APR_SUCCESS;
475    mgs_srvconf_rec *sc = ap_get_module_config(s->module_config,
476            &gnutls_module);
477
478    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
479    if (sc->cache_type != mgs_cache_none) {
480        rv = mgs_cache_child_init(p, s, sc);
481        if (rv != APR_SUCCESS) {
482            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
483                    "[GnuTLS] - Failed to run Cache Init");
484        }
485    }
486    /* Block SIGPIPE Signals */
487    rv = apr_signal_block(SIGPIPE);
488    if(rv != APR_SUCCESS) {
489        /* error sending output */
490        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
491                "GnuTLS: Error Blocking SIGPIPE Signal!");
492    }
493}
494
495const char *mgs_hook_http_scheme(const request_rec * r) {
496    mgs_srvconf_rec *sc;
497
498    if (r == NULL)
499        return NULL;
500
501    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
502            server->module_config,
503            &gnutls_module);
504
505    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
506    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
507        return NULL;
508    }
509
510    return "https";
511}
512
513apr_port_t mgs_hook_default_port(const request_rec * r) {
514    mgs_srvconf_rec *sc;
515
516    if (r == NULL)
517        return 0;
518
519    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
520            server->module_config,
521            &gnutls_module);
522
523    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
524    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
525        return 0;
526    }
527
528    return 443;
529}
530
531#define MAX_HOST_LEN 255
532
533#if USING_2_1_RECENT
534
535typedef struct {
536    mgs_handle_t *ctxt;
537    mgs_srvconf_rec *sc;
538    const char *sni_name;
539} vhost_cb_rec;
540
541/**
542 * Matches the current vhost's ServerAlias directives
543 *
544 * @param x vhost callback record
545 * @param s server record
546 * @return true if a match, false otherwise
547 *
548 */
549int check_server_aliases(vhost_cb_rec *x, server_rec * s, mgs_srvconf_rec *tsc) {
550        apr_array_header_t *names;
551        int i,rv = 0;
552        char ** name;
553
554        /* Check ServerName First! */
555        if(apr_strnatcasecmp(x->sni_name, s->server_hostname) == 0) {
556                // We have a match, save this server configuration
557                x->sc = tsc;
558                rv = 1;
559        /* Check any ServerAlias directives */
560        } else if(s->names->nelts) {
561                names = s->names;
562                name = (char **)names->elts;
563                for (i = 0; i < names->nelts; ++i) {
564                        if (!name[i]) { continue; }
565                                if (apr_strnatcasecmp(x->sni_name, name[i]) == 0) {
566                                        // We have a match, save this server configuration
567                                        x->sc = tsc;
568                                        rv = 1;
569                        }
570                }
571        /* Wild any ServerAlias Directives */
572        } else if(s->wild_names->nelts) {
573                names = s->wild_names;
574        name = (char **)names->elts;
575                for (i = 0; i < names->nelts; ++i) {
576                        if (!name[i]) { continue; }
577                                if(apr_fnmatch(name[i], x->sni_name ,
578                                                                APR_FNM_CASE_BLIND|
579                                                                APR_FNM_PERIOD|
580                                                                APR_FNM_PATHNAME|
581                                                                APR_FNM_NOESCAPE) == APR_SUCCESS) {
582                                x->sc = tsc;
583                                rv = 1;
584                        }
585                }
586        }
587        return rv;
588}
589
590static int vhost_cb(void *baton, conn_rec * conn, server_rec * s) {
591    mgs_srvconf_rec *tsc;
592    vhost_cb_rec *x = baton;
593    int ret;
594
595    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
596    tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
597            &gnutls_module);
598
599    if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
600        return 0;
601    }
602
603    if (tsc->certs_x509_chain_num > 0) {
604        /* why are we doing this check? */
605        ret = gnutls_x509_crt_check_hostname(tsc->certs_x509_chain[0], s->server_hostname);
606        if (0 == ret)
607            ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
608                         "GnuTLS: Error checking certificate for hostname "
609                         "'%s'", s->server_hostname);
610    } else {
611        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
612                     "GnuTLS: SNI request for '%s' but no X.509 certs available at all",
613                     s->server_hostname);
614    }
615        return check_server_aliases(x, s, tsc);
616}
617#endif
618
619mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session) {
620    int rv;
621    unsigned int sni_type;
622    size_t data_len = MAX_HOST_LEN;
623    char sni_name[MAX_HOST_LEN];
624    mgs_handle_t *ctxt;
625#if USING_2_1_RECENT
626    vhost_cb_rec cbx;
627#else
628    server_rec *s;
629    mgs_srvconf_rec *tsc;
630#endif
631
632    if (session == NULL)
633        return NULL;
634
635    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
636    ctxt = gnutls_transport_get_ptr(session);
637
638    rv = gnutls_server_name_get(ctxt->session, sni_name,
639            &data_len, &sni_type, 0);
640
641    if (rv != 0) {
642        return NULL;
643    }
644
645    if (sni_type != GNUTLS_NAME_DNS) {
646        ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
647                ctxt->c->base_server,
648                "GnuTLS: Unknown type '%d' for SNI: "
649                "'%s'", sni_type, sni_name);
650        return NULL;
651    }
652
653    /**
654     * Code in the Core already sets up the c->base_server as the base
655     * for this IP/Port combo.  Trust that the core did the 'right' thing.
656     */
657#if USING_2_1_RECENT
658    cbx.ctxt = ctxt;
659    cbx.sc = NULL;
660    cbx.sni_name = sni_name;
661
662    rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
663    if (rv == 1) {
664        return cbx.sc;
665    }
666#else
667    for (s = ap_server_conf; s; s = s->next) {
668
669        tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
670                &gnutls_module);
671
672        if (tsc->enabled != GNUTLS_ENABLED_TRUE) { continue; }
673
674                                if(check_server_aliases(x, s, tsc)) {
675                                        return tsc;
676                                }
677#endif
678    return NULL;
679}
680
681static void create_gnutls_handle(conn_rec * c) {
682    mgs_handle_t *ctxt;
683    /* Get mod_gnutls Configuration Record */
684    mgs_srvconf_rec *sc =(mgs_srvconf_rec *)
685            ap_get_module_config(c->base_server->module_config,&gnutls_module);
686
687    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
688    ctxt = apr_pcalloc(c->pool, sizeof (*ctxt));
689    ctxt->c = c;
690    ctxt->sc = sc;
691    ctxt->status = 0;
692    ctxt->input_rc = APR_SUCCESS;
693    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
694    ctxt->input_cbuf.length = 0;
695    ctxt->output_rc = APR_SUCCESS;
696    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
697    ctxt->output_blen = 0;
698    ctxt->output_length = 0;
699    /* Initialize GnuTLS Library */
700    gnutls_init(&ctxt->session, GNUTLS_SERVER);
701    /* Initialize Session Tickets */
702    if (session_ticket_key.data != NULL && ctxt->sc->tickets != 0) {
703        gnutls_session_ticket_enable_server(ctxt->session,&session_ticket_key);
704    }
705
706    /* Set Default Priority */
707        gnutls_priority_set_direct (ctxt->session, "NORMAL", NULL);
708    /* Set Handshake function */
709    gnutls_handshake_set_post_client_hello_function(ctxt->session,
710            mgs_select_virtual_server_cb);
711    /* Initialize Session Cache */
712    mgs_cache_session_init(ctxt);
713
714    /* Set this config for this connection */
715    ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
716    /* Set pull, push & ptr functions */
717    gnutls_transport_set_pull_function(ctxt->session,
718            mgs_transport_read);
719    gnutls_transport_set_push_function(ctxt->session,
720            mgs_transport_write);
721    gnutls_transport_set_ptr(ctxt->session, ctxt);
722    /* Add IO filters */
723    ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME,
724            ctxt, NULL, c);
725    ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
726            ctxt, NULL, c);
727}
728
729int mgs_hook_pre_connection(conn_rec * c, void *csd) {
730    mgs_srvconf_rec *sc;
731
732    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
733
734    sc = (mgs_srvconf_rec *) ap_get_module_config(c->base_server->module_config,
735            &gnutls_module);
736
737    if (sc && (!sc->enabled || sc->proxy_enabled == GNUTLS_ENABLED_TRUE)) {
738        return DECLINED;
739    }
740
741    create_gnutls_handle(c);
742    return OK;
743}
744
745int mgs_hook_fixups(request_rec * r) {
746    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
747    char buf[AP_IOBUFSIZE];
748    const char *tmp;
749    size_t len;
750    mgs_handle_t *ctxt;
751    int rv = OK;
752
753    if (r == NULL)
754        return DECLINED;
755
756    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
757    apr_table_t *env = r->subprocess_env;
758
759    ctxt =
760            ap_get_module_config(r->connection->conn_config,
761            &gnutls_module);
762
763    if (!ctxt || ctxt->session == NULL) {
764        return DECLINED;
765    }
766
767    apr_table_setn(env, "HTTPS", "on");
768
769    apr_table_setn(env, "SSL_VERSION_LIBRARY",
770            "GnuTLS/" LIBGNUTLS_VERSION);
771    apr_table_setn(env, "SSL_VERSION_INTERFACE",
772            "mod_gnutls/" MOD_GNUTLS_VERSION);
773
774    apr_table_setn(env, "SSL_PROTOCOL",
775            gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session)));
776
777    /* should have been called SSL_CIPHERSUITE instead */
778    apr_table_setn(env, "SSL_CIPHER",
779            gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
780                                         gnutls_cipher_get(ctxt->session),
781                                         gnutls_mac_get(ctxt->session)));
782
783    apr_table_setn(env, "SSL_COMPRESS_METHOD",
784            gnutls_compression_get_name(gnutls_compression_get(ctxt->session)));
785
786#ifdef ENABLE_SRP
787    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
788        tmp = gnutls_srp_server_get_username(ctxt->session);
789        apr_table_setn(env, "SSL_SRP_USER", (tmp != NULL) ? tmp : "");
790    } else {
791        apr_table_unset(env, "SSL_SRP_USER");
792    }
793#endif
794
795    if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
796        apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
797
798    unsigned int key_size = 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
799    tmp = apr_psprintf(r->pool, "%u", key_size);
800
801    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
802
803    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
804
805    apr_table_setn(env, "SSL_CIPHER_EXPORT",
806            (key_size <= 40) ? "true" : "false");
807
808    int dhsize = gnutls_dh_get_prime_bits(ctxt->session);
809    if (dhsize > 0) {
810        tmp = apr_psprintf(r->pool, "%d", dhsize);
811        apr_table_setn(env, "SSL_DH_PRIME_BITS", tmp);
812    }
813
814    len = sizeof (sbuf);
815    gnutls_session_get_id(ctxt->session, sbuf, &len);
816    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
817    apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
818
819    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
820                mgs_add_common_cert_vars(r, ctxt->sc->certs_x509_chain[0], 0, ctxt->sc->export_certificates_size);
821        } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
822        mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_pgp, 0, ctxt->sc->export_certificates_size);
823        }
824
825    return rv;
826}
827
828int mgs_hook_authz(request_rec * r) {
829    int rv;
830    mgs_handle_t *ctxt;
831    mgs_dirconf_rec *dc;
832
833    if (r == NULL)
834        return DECLINED;
835
836    dc = ap_get_module_config(r->per_dir_config, &gnutls_module);
837
838    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
839    ctxt =
840            ap_get_module_config(r->connection->conn_config,
841            &gnutls_module);
842
843    if (!ctxt || ctxt->session == NULL) {
844        return DECLINED;
845    }
846
847    if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
848        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
849                "GnuTLS: Directory set to Ignore Client Certificate!");
850    } else {
851        if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
852            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
853                    "GnuTLS: Attempting to rehandshake with peer. %d %d",
854                    ctxt->sc->client_verify_mode,
855                    dc->client_verify_mode);
856
857            /* If we already have a client certificate, there's no point in
858             * re-handshaking... */
859            rv = mgs_cert_verify(r, ctxt);
860            if (rv != DECLINED && rv != HTTP_FORBIDDEN)
861                return rv;
862
863            gnutls_certificate_server_set_request
864                    (ctxt->session, dc->client_verify_mode);
865
866            if (mgs_rehandshake(ctxt) != 0) {
867                return HTTP_FORBIDDEN;
868            }
869        } else if (ctxt->sc->client_verify_mode ==
870                GNUTLS_CERT_IGNORE) {
871#if MOD_GNUTLS_DEBUG
872            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
873                    "GnuTLS: Peer is set to IGNORE");
874#endif
875            return DECLINED;
876        }
877        rv = mgs_cert_verify(r, ctxt);
878        if (rv != DECLINED &&
879                (rv != HTTP_FORBIDDEN ||
880                dc->client_verify_mode == GNUTLS_CERT_REQUIRE)) {
881            return rv;
882        }
883    }
884
885    return DECLINED;
886}
887
888/* variables that are not sent by default:
889 *
890 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
891 * SSL_SERVER_CERT      string  PEM-encoded client certificate
892 */
893
894/* @param side is either 0 for SERVER or 1 for CLIENT
895 *
896 * @param export_cert_size (int) maximum size for environment variable
897 * to use for the PEM-encoded certificate (0 means do not export)
898 */
899#define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT")
900
901static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, int export_cert_size) {
902    unsigned char sbuf[64]; /* buffer to hold serials */
903    char buf[AP_IOBUFSIZE];
904    const char *tmp;
905    char *tmp2;
906    size_t len;
907    int ret, i;
908
909    if (r == NULL)
910        return;
911
912    apr_table_t *env = r->subprocess_env;
913
914    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
915    if (export_cert_size > 0) {
916        len = 0;
917        ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL, &len);
918        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
919            if (len >= export_cert_size) {
920                apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL),
921                               "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
922                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
923                              "GnuTLS: Failed to export too-large X.509 certificate to environment");
924            } else {
925                char* cert_buf = apr_palloc(r->pool, len + 1);
926                if (cert_buf != NULL && gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0) {
927                    cert_buf[len] = 0;
928                    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL), cert_buf);
929                } else {
930                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
931                                  "GnuTLS: failed to export X.509 certificate");
932                }
933            }
934        } else {
935            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
936                          "GnuTLS: dazed and confused about X.509 certificate size");
937        }
938    }
939
940    len = sizeof (buf);
941    gnutls_x509_crt_get_dn(cert, buf, &len);
942    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_S_DN", NULL),
943            apr_pstrmemdup(r->pool, buf, len));
944
945    len = sizeof (buf);
946    gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
947    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_I_DN", NULL),
948            apr_pstrmemdup(r->pool, buf, len));
949
950    len = sizeof (sbuf);
951    gnutls_x509_crt_get_serial(cert, sbuf, &len);
952    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
953    apr_table_setn(env,
954            apr_pstrcat(r->pool, MGS_SIDE, "_M_SERIAL", NULL),
955            apr_pstrdup(r->pool, tmp));
956
957    ret = gnutls_x509_crt_get_version(cert);
958    if (ret > 0)
959        apr_table_setn(env,
960            apr_pstrcat(r->pool, MGS_SIDE, "_M_VERSION",
961            NULL), apr_psprintf(r->pool,
962            "%u", ret));
963
964    apr_table_setn(env,
965            apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL),
966            "X.509");
967
968    tmp =
969            mgs_time2sz(gnutls_x509_crt_get_expiration_time
970            (cert), buf, sizeof (buf));
971    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL),
972            apr_pstrdup(r->pool, tmp));
973
974    tmp =
975            mgs_time2sz(gnutls_x509_crt_get_activation_time
976            (cert), buf, sizeof (buf));
977    apr_table_setn(env,
978            apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL),
979            apr_pstrdup(r->pool, tmp));
980
981    ret = gnutls_x509_crt_get_signature_algorithm(cert);
982    if (ret >= 0) {
983        apr_table_setn(env,
984                apr_pstrcat(r->pool, MGS_SIDE, "_A_SIG",
985                NULL),
986                gnutls_sign_algorithm_get_name(ret));
987    }
988
989    ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
990    if (ret >= 0) {
991        apr_table_setn(env,
992                apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY",
993                NULL),
994                gnutls_pk_algorithm_get_name(ret));
995    }
996
997    /* export all the alternative names (DNS, RFC822 and URI) */
998    for (i = 0; !(ret < 0); i++) {
999        len = 0;
1000        ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
1001                NULL, &len,
1002                NULL);
1003
1004        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) {
1005            tmp2 = apr_palloc(r->pool, len + 1);
1006
1007            ret =
1008                    gnutls_x509_crt_get_subject_alt_name(cert, i,
1009                    tmp2,
1010                    &len,
1011                    NULL);
1012            tmp2[len] = 0;
1013
1014            if (ret == GNUTLS_SAN_DNSNAME) {
1015                apr_table_setn(env,
1016                        apr_psprintf(r->pool,
1017                        "%s_S_AN%u",
1018                        MGS_SIDE, i),
1019                        apr_psprintf(r->pool,
1020                        "DNSNAME:%s",
1021                        tmp2));
1022            } else if (ret == GNUTLS_SAN_RFC822NAME) {
1023                apr_table_setn(env,
1024                        apr_psprintf(r->pool,
1025                        "%s_S_AN%u",
1026                        MGS_SIDE, i),
1027                        apr_psprintf(r->pool,
1028                        "RFC822NAME:%s",
1029                        tmp2));
1030            } else if (ret == GNUTLS_SAN_URI) {
1031                apr_table_setn(env,
1032                        apr_psprintf(r->pool,
1033                        "%s_S_AN%u",
1034                        MGS_SIDE, i),
1035                        apr_psprintf(r->pool,
1036                        "URI:%s",
1037                        tmp2));
1038            } else {
1039                apr_table_setn(env,
1040                        apr_psprintf(r->pool,
1041                        "%s_S_AN%u",
1042                        MGS_SIDE, i),
1043                        "UNSUPPORTED");
1044            }
1045        }
1046    }
1047}
1048
1049
1050/* @param side 0: server, 1: client
1051 *
1052 * @param export_cert_size (int) maximum size for environment variable
1053 * to use for the PEM-encoded certificate (0 means do not export)
1054 */
1055static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, int export_cert_size) {
1056
1057        unsigned char sbuf[64]; /* buffer to hold serials */
1058    char buf[AP_IOBUFSIZE];
1059    const char *tmp;
1060    size_t len;
1061    int ret;
1062
1063    if (r == NULL)
1064        return;
1065
1066    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1067    apr_table_t *env = r->subprocess_env;
1068
1069    if (export_cert_size > 0) {
1070        len = 0;
1071        ret = gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, NULL, &len);
1072        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1073            if (len >= export_cert_size) {
1074                apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL),
1075                               "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1076                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1077                              "GnuTLS: Failed to export too-large OpenPGP certificate to environment");
1078            } else {
1079                char* cert_buf = apr_palloc(r->pool, len + 1);
1080                if (cert_buf != NULL && gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0) {
1081                    cert_buf[len] = 0;
1082                    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL), cert_buf);
1083                } else {
1084                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1085                                  "GnuTLS: failed to export OpenPGP certificate");
1086                }
1087            }
1088        } else {
1089            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1090                          "GnuTLS: dazed and confused about OpenPGP certificate size");
1091        }
1092    }
1093
1094    len = sizeof (buf);
1095    gnutls_openpgp_crt_get_name(cert, 0, buf, &len);
1096    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_NAME", NULL),
1097            apr_pstrmemdup(r->pool, buf, len));
1098
1099    len = sizeof (sbuf);
1100    gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len);
1101    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
1102    apr_table_setn(env,
1103            apr_pstrcat(r->pool, MGS_SIDE, "_FINGERPRINT",
1104            NULL), apr_pstrdup(r->pool, tmp));
1105
1106    ret = gnutls_openpgp_crt_get_version(cert);
1107    if (ret > 0)
1108        apr_table_setn(env,
1109            apr_pstrcat(r->pool, MGS_SIDE, "_M_VERSION",
1110            NULL), apr_psprintf(r->pool,
1111            "%u", ret));
1112
1113    apr_table_setn(env,
1114            apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL),
1115            "OPENPGP");
1116
1117    tmp =
1118            mgs_time2sz(gnutls_openpgp_crt_get_expiration_time
1119            (cert), buf, sizeof (buf));
1120    apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL),
1121            apr_pstrdup(r->pool, tmp));
1122
1123    tmp =
1124            mgs_time2sz(gnutls_openpgp_crt_get_creation_time
1125            (cert), buf, sizeof (buf));
1126    apr_table_setn(env,
1127            apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL),
1128            apr_pstrdup(r->pool, tmp));
1129
1130    ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL);
1131    if (ret >= 0) {
1132        apr_table_setn(env,
1133                apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY",
1134                NULL),
1135                gnutls_pk_algorithm_get_name(ret));
1136    }
1137
1138}
1139
1140/* TODO: Allow client sending a X.509 certificate chain */
1141static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) {
1142    const gnutls_datum_t *cert_list;
1143    unsigned int cert_list_size, status;
1144    int rv = GNUTLS_E_NO_CERTIFICATE_FOUND, ret;
1145    unsigned int ch_size = 0;
1146
1147    union {
1148        gnutls_x509_crt_t x509[MAX_CHAIN_SIZE];
1149        gnutls_openpgp_crt_t pgp;
1150    } cert;
1151    apr_time_t expiration_time, cur_time;
1152
1153    if (r == NULL || ctxt == NULL || ctxt->session == NULL)
1154        return HTTP_FORBIDDEN;
1155
1156    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1157    cert_list =
1158            gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1159
1160    if (cert_list == NULL || cert_list_size == 0) {
1161        /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
1162         */
1163        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1164            return OK;
1165
1166        /* no certificate provided by the client, but one was required. */
1167        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1168                "GnuTLS: Failed to Verify Peer: "
1169                "Client did not submit a certificate");
1170        return HTTP_FORBIDDEN;
1171    }
1172
1173    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1174        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1175                "GnuTLS: A Chain of %d certificate(s) was provided for validation",
1176                cert_list_size);
1177
1178        for (ch_size = 0; ch_size < cert_list_size; ch_size++) {
1179            gnutls_x509_crt_init(&cert.x509[ch_size]);
1180            rv = gnutls_x509_crt_import(cert.x509[ch_size],
1181                    &cert_list[ch_size],
1182                    GNUTLS_X509_FMT_DER);
1183            // When failure to import, leave the loop
1184            if (rv != GNUTLS_E_SUCCESS) {
1185                if (ch_size < 1) {
1186                    ap_log_rerror(APLOG_MARK,
1187                            APLOG_INFO, 0, r,
1188                            "GnuTLS: Failed to Verify Peer: "
1189                            "Failed to import peer certificates.");
1190                    ret = HTTP_FORBIDDEN;
1191                    goto exit;
1192                }
1193                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1194                        "GnuTLS: Failed to import some peer certificates. Using %d certificates",
1195                        ch_size);
1196                rv = GNUTLS_E_SUCCESS;
1197                break;
1198            }
1199        }
1200    } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
1201        if (cert_list_size > 1) {
1202            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1203                    "GnuTLS: Failed to Verify Peer: "
1204                    "Chained Client Certificates are not supported.");
1205            return HTTP_FORBIDDEN;
1206        }
1207
1208        gnutls_openpgp_crt_init(&cert.pgp);
1209        rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0],
1210                GNUTLS_OPENPGP_FMT_RAW);
1211
1212    } else
1213        return HTTP_FORBIDDEN;
1214
1215    if (rv < 0) {
1216        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1217                "GnuTLS: Failed to Verify Peer: "
1218                "Failed to import peer certificates.");
1219        ret = HTTP_FORBIDDEN;
1220        goto exit;
1221    }
1222
1223    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1224        apr_time_ansi_put(&expiration_time,
1225                gnutls_x509_crt_get_expiration_time
1226                (cert.x509[0]));
1227
1228        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1229                      "GnuTLS: Verifying list of %d certificate(s) via method '%s'",
1230                      ch_size, mgs_readable_cvm(ctxt->sc->client_verify_method));
1231        switch(ctxt->sc->client_verify_method) {
1232        case mgs_cvm_cartel:
1233            rv = gnutls_x509_crt_list_verify(cert.x509, ch_size,
1234                                             ctxt->sc->ca_list,
1235                                             ctxt->sc->ca_list_size,
1236                                             NULL, 0, 0, &status);
1237            break;
1238#ifdef ENABLE_MSVA
1239        case mgs_cvm_msva:
1240        {
1241            struct msv_response* resp = NULL;
1242            struct msv_query q = { .context="https", .peertype="client", .pkctype="x509pem" };
1243            msv_ctxt_t ctx = msv_ctxt_init(NULL);
1244            char cert_pem_buf[10 * 1024];
1245            size_t len = sizeof (cert_pem_buf);
1246
1247            rv = 0;
1248            if (gnutls_x509_crt_export(cert.x509[0], GNUTLS_X509_FMT_PEM, cert_pem_buf, &len) >= 0) {
1249                /* FIXME : put together a name from the cert we received, instead of hard-coding this value: */
1250                q.peername = mgs_x509_construct_uid(r, cert.x509[0]);
1251                q.pkcdata = cert_pem_buf;
1252                rv = msv_query_agent(ctx, q, &resp);
1253                if (rv == LIBMSV_ERROR_SUCCESS) {
1254                    status = 0;
1255                } else if (rv == LIBMSV_ERROR_INVALID) {
1256                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1257                                  "GnuTLS: Monkeysphere validation failed: (message: %s)", resp->message);
1258                    status = GNUTLS_CERT_INVALID;
1259                } else {
1260                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1261                                  "GnuTLS: Error communicating with the Monkeysphere Validation Agent: (%d) %s", rv, msv_strerror(ctx, rv));
1262                    status = GNUTLS_CERT_INVALID;
1263                    rv = -1;
1264                }
1265            } else {
1266                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1267                              "GnuTLS: Could not convert the client certificate to PEM format");
1268                status = GNUTLS_CERT_INVALID;
1269                rv = GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
1270            }
1271            msv_response_destroy(resp);
1272            msv_ctxt_destroy(ctx);
1273        }
1274            break;
1275#endif
1276        default:
1277            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1278                          "GnuTLS: Failed to Verify X.509 Peer: method '%s' is not supported",
1279                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1280        }
1281
1282    } else {
1283        apr_time_ansi_put(&expiration_time,
1284                gnutls_openpgp_crt_get_expiration_time
1285                (cert.pgp));
1286
1287        switch(ctxt->sc->client_verify_method) {
1288        case mgs_cvm_cartel:
1289            rv = gnutls_openpgp_crt_verify_ring(cert.pgp,
1290                                                ctxt->sc->pgp_list, 0,
1291                                                &status);
1292            break;
1293#ifdef ENABLE_MSVA
1294        case mgs_cvm_msva:
1295            /* need to set status and rv */
1296            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1297                          "GnuTLS:  OpenPGP verification via MSVA is not yet implemented");
1298            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1299            break;
1300#endif
1301        default:
1302            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1303                          "GnuTLS: Failed to Verify OpenPGP Peer: method '%s' is not supported",
1304                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1305        }
1306    }
1307
1308    if (rv < 0) {
1309        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1310                "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
1311                rv, gnutls_strerror(rv));
1312        if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1313            ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1314                "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?");
1315        ret = HTTP_FORBIDDEN;
1316        goto exit;
1317    }
1318
1319    /* TODO: X509 CRL Verification. */
1320    /* May add later if anyone needs it.
1321     */
1322    /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1323
1324    cur_time = apr_time_now();
1325
1326    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1327        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1328                "GnuTLS: Could not find Signer for Peer Certificate");
1329    }
1330
1331    if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
1332        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1333                "GnuTLS: Peer's Certificate signer is not a CA");
1334    }
1335
1336    if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
1337        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1338                "GnuTLS: Peer's Certificate is using insecure algorithms");
1339    }
1340
1341    if (status & GNUTLS_CERT_EXPIRED
1342            || status & GNUTLS_CERT_NOT_ACTIVATED) {
1343        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1344                "GnuTLS: Peer's Certificate signer is expired or not yet activated");
1345    }
1346
1347    if (status & GNUTLS_CERT_INVALID) {
1348        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1349                "GnuTLS: Peer Certificate is invalid.");
1350    } else if (status & GNUTLS_CERT_REVOKED) {
1351        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1352                "GnuTLS: Peer Certificate is revoked.");
1353    }
1354
1355    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
1356        mgs_add_common_cert_vars(r, cert.x509[0], 1, ctxt->sc->export_certificates_size);
1357    else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP)
1358        mgs_add_common_pgpcert_vars(r, cert.pgp, 1, ctxt->sc->export_certificates_size);
1359
1360    {
1361        /* days remaining */
1362        unsigned long remain =
1363                (apr_time_sec(expiration_time) -
1364                apr_time_sec(cur_time)) / 86400;
1365        apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
1366                apr_psprintf(r->pool, "%lu", remain));
1367    }
1368
1369    if (status == 0) {
1370        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1371                "SUCCESS");
1372        ret = OK;
1373    } else {
1374        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1375                "FAILED");
1376        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1377            ret = OK;
1378        else
1379            ret = HTTP_FORBIDDEN;
1380    }
1381
1382exit:
1383    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1384        int i;
1385        for (i = 0; i < ch_size; i++) {
1386            gnutls_x509_crt_deinit(cert.x509[i]);
1387        }
1388    } else if (gnutls_certificate_type_get(ctxt->session) ==
1389            GNUTLS_CRT_OPENPGP)
1390        gnutls_openpgp_crt_deinit(cert.pgp);
1391    return ret;
1392
1393
1394}
1395
1396static const char* mgs_x509_leaf_oid_from_dn(apr_pool_t *pool, const char* oid, gnutls_x509_crt_t cert) {
1397    int rv=GNUTLS_E_SUCCESS, i;
1398    size_t sz=0, lastsz=0;
1399    char* data=NULL;
1400
1401    i = -1;
1402    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1403        i++;
1404        lastsz=sz;
1405        sz=0;
1406        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i, 0, NULL, &sz);
1407    }
1408    if (i > 0) {
1409        data = apr_palloc(pool, lastsz);
1410        sz=lastsz;
1411        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i-1, 0, data, &sz);
1412        if (rv == GNUTLS_E_SUCCESS)
1413            return data;
1414    }
1415    return NULL;
1416}
1417
1418static const char* mgs_x509_first_type_from_san(apr_pool_t *pool, gnutls_x509_subject_alt_name_t target, gnutls_x509_crt_t cert) {
1419    int rv=GNUTLS_E_SUCCESS;
1420    size_t sz;
1421    char* data=NULL;
1422    unsigned int i;
1423    gnutls_x509_subject_alt_name_t thistype;
1424
1425    i = 0;
1426    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1427        sz = 0;
1428        rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, NULL, &sz, &thistype, NULL);
1429        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && thistype == target) {
1430            data = apr_palloc(pool, sz);
1431            rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, data, &sz, &thistype, NULL);
1432            if (rv == target)
1433                return data;
1434        }
1435        i++;
1436    }
1437    return NULL;
1438}
1439
1440/* Create a string representing a candidate User ID from an X.509
1441 * certificate
1442
1443 * We need this for client certification because a client gives us a
1444 * certificate, but doesn't tell us (in any other way) who they are
1445 * trying to authenticate as.
1446
1447 * TODO: we might need another parallel for OpenPGP, but for that it's
1448 * much simpler: we can just assume that the first User ID marked as
1449 * "primary" (or the first User ID, period) is the identity the user
1450 * is trying to present as.
1451
1452 * one complaint might be "but the user wanted to be another identity,
1453 * which is also in the certificate (e.g. in a SubjectAltName)"
1454 * However, given that any user can regenerate their own X.509
1455 * certificate with their own public key content, they should just do
1456 * so, and not expect us to guess at their identity :)
1457
1458 * This function allocates it's response from the pool given it.  When
1459 * that pool is reclaimed, the response will also be deallocated.
1460
1461 * FIXME: what about extracting a server-style cert
1462 *        (e.g. https://imposter.example) from the DN or any sAN?
1463
1464 * FIXME: what if we want to call this outside the context of a
1465 *        request?  That complicates the logging.
1466 */
1467static const char* mgs_x509_construct_uid(request_rec *r, gnutls_x509_crt_t cert) {
1468    /* basic strategy, assuming humans are the users: we are going to
1469     * try to reconstruct a "conventional" User ID by pulling in a
1470     * name, comment, and e-mail address.
1471     */
1472    apr_pool_t *pool = r->pool;
1473    const char *name=NULL, *comment=NULL, *email=NULL;
1474    const char *ret=NULL;
1475    /* subpool for temporary allocation: */
1476    apr_pool_t *sp=NULL;
1477
1478    if (APR_SUCCESS != apr_pool_create(&sp, pool))
1479        return NULL; /* i'm assuming that libapr would log this kind
1480                      * of error on its own */
1481
1482     /* Name
1483
1484     the name comes from the leaf commonName of the cert's Subject.
1485
1486     (MAYBE: should we look at trying to assemble a candidate from
1487             givenName, surName, suffix, etc?  the "name" field
1488             appears to be case-insensitive, which seems problematic
1489             from what we expect; see:
1490             http://www.itu.int/rec/T-REC-X.520-200102-s/e )
1491
1492     (MAYBE: should we try pulling a commonName or otherName or
1493             something from subjectAltName? see:
1494             https://tools.ietf.org/html/rfc5280#section-4.2.1.6
1495             GnuTLS does not support looking for Common Names in the
1496             SAN yet)
1497     */
1498    name = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_COMMON_NAME, cert);
1499
1500    /* Comment
1501
1502       I am inclined to punt on this for now, as Comment has been so
1503       atrociously misused in OpenPGP.  Perhaps if there is a
1504       pseudonym (OID 2.5.4.65, aka GNUTLS_OID_X520_PSEUDONYM) field
1505       in the subject or sAN?
1506    */
1507    comment = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_PSEUDONYM, cert);
1508
1509    /* E-mail
1510
1511       This should be the the first rfc822Name from the sAN.
1512
1513       failing that, we'll take the leaf email in the certificate's
1514       subject; this is a deprecated use though.
1515     */
1516    email = mgs_x509_first_type_from_san(sp, GNUTLS_SAN_RFC822NAME, cert);
1517    if (email == NULL)
1518        email = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_PKCS9_EMAIL, cert);
1519
1520    /* assemble all the parts: */
1521
1522    /* must have at least a name or an e-mail. */
1523    if (name == NULL && email == NULL) {
1524        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1525                "GnuTLS: Need either a name or an e-mail address to get a User ID from an X.509 certificate.");
1526        goto end;
1527    }
1528    if (name) {
1529        if (comment) {
1530            if (email) {
1531                ret = apr_psprintf(pool, "%s (%s) <%s>", name, comment, email);
1532            } else {
1533                ret = apr_psprintf(pool, "%s (%s)", name, comment);
1534            }
1535        } else {
1536            if (email) {
1537                ret = apr_psprintf(pool, "%s <%s>", name, email);
1538            } else {
1539                ret = apr_pstrdup(pool, name);
1540            }
1541        }
1542    } else {
1543        if (comment) {
1544            ret = apr_psprintf(pool, "(%s) <%s>", comment, email);
1545        } else {
1546            ret = apr_psprintf(pool, "<%s>", email);
1547        }
1548    }
1549
1550end:
1551    apr_pool_destroy(sp);
1552    return ret;
1553}
1554
1555static int mgs_status_hook(request_rec *r, int flags)
1556{
1557    mgs_srvconf_rec *sc;
1558
1559    if (r == NULL)
1560        return OK;
1561
1562    sc = (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, &gnutls_module);
1563
1564    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1565
1566    ap_rputs("<hr>\n", r);
1567    ap_rputs("<h2>GnuTLS Information:</h2>\n<dl>\n", r);
1568
1569    ap_rprintf(r, "<dt>GnuTLS version:</dt><dd>%s</dd>\n", gnutls_check_version(NULL));
1570    ap_rputs("<dt>Built against:</dt><dd>" GNUTLS_VERSION "</dd>\n", r);
1571    ap_rprintf(r, "<dt>using TLS:</dt><dd>%s</dd>\n", (sc->enabled == GNUTLS_ENABLED_FALSE ? "no" : "yes"));
1572    if (sc->enabled != GNUTLS_ENABLED_FALSE) {
1573        mgs_handle_t* ctxt;
1574        ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module);
1575        if (ctxt && ctxt->session != NULL) {
1576#if GNUTLS_VERSION_MAJOR < 3
1577            ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n",
1578                gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
1579                gnutls_cipher_get(ctxt->session),
1580                gnutls_mac_get(ctxt->session)));
1581#else
1582            char* z = NULL;
1583            z = gnutls_session_get_desc(ctxt->session);
1584            if (z) {
1585                ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n", z);
1586                gnutls_free(z);
1587            }
1588#endif
1589        }
1590    }
1591
1592    ap_rputs("</dl>\n", r);
1593    return OK;
1594}
1595
Note: See TracBrowser for help on using the repository browser.