source: mod_gnutls/src/gnutls_hooks.c @ fd82e59

asynciodebian/masterdebian/stretch-backportsjessie-backportsmainproxy-ticketupstream
Last change on this file since fd82e59 was fd82e59, checked in by Daniel Kahn Gillmor <dkg@…>, 9 years ago

use strict compiler arguments by default (-Wall -Werror -Wextra)

Because apache modules make heavy use of generic hooks that have to
have arguments that cover every corner use case, and we don't need all
that data, many mod_gnutls functions have unused parameters, which
have now been explicitly designated as unused.

We also have at least one generic function signature declared for our
interaction with GnuTLS as well, and we aren't using some of those
parameters either.

A useful future review might be to read up on how the unused
parameters are used by other apache modules or users of GnuTLS, to see
if we might gather useful ideas.

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