source: mod_gnutls/src/gnutls_hooks.c @ 5a8a32b

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 5a8a32b was 5a8a32b, checked in by Thomas Klute <thomas2.klute@…>, 5 years ago

TLS Client auth: Check server verify mode if unset for dir

The authentication hook (mgs_hook_authz) failed to consider the server's
client verify mode, even if the verify mode was unset in the directory
configuration. As a result, invalid certificates were ignored and
clients could connect and receive data as long as they presented any
certificate whatsoever. Logs showed that authorization was granted
despite the certificate being invalid (timestamps removed for
readability):

[:debug] [pid 10806:tid 140242057148160] gnutls_hooks.c(1198): [client ::1:40992] GnuTLS: Verifying list of 1 certificate(s) via method 'cartel'
[:info] [pid 10806:tid 140242057148160] [client ::1:40992] GnuTLS: Could not find Signer for Peer Certificate
[:info] [pid 10806:tid 140242057148160] [client ::1:40992] GnuTLS: Peer Certificate is invalid.
[authz_core:debug] [pid 10806:tid 140242057148160] mod_authz_core.c(835): [client ::1:40992] AH01628: authorization result: granted (no directives)

This commit adds a check for undefined verify mode in the directory
configuration and applies the server wide configuration in that case.

  • Property mode set to 100644
File size: 53.9 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{
686    /* Get mod_gnutls server configuration */
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
692    /* Get connection specific configuration */
693    mgs_handle_t *ctxt = (mgs_handle_t *) ap_get_module_config(c->conn_config, &gnutls_module);
694    if (ctxt == NULL)
695    {
696        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s: allocating connection memory", __func__);
697        ctxt = apr_pcalloc(c->pool, sizeof (*ctxt));
698        ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
699    }
700    ctxt->enabled = GNUTLS_ENABLED_TRUE;
701    ctxt->c = c;
702    ctxt->sc = sc;
703    ctxt->status = 0;
704    ctxt->input_rc = APR_SUCCESS;
705    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
706    ctxt->input_cbuf.length = 0;
707    ctxt->output_rc = APR_SUCCESS;
708    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
709    ctxt->output_blen = 0;
710    ctxt->output_length = 0;
711
712    /* Initialize GnuTLS Library */
713    int err = gnutls_init(&ctxt->session, GNUTLS_SERVER);
714    if (err != GNUTLS_E_SUCCESS)
715        ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c, "gnutls_init failed!");
716    /* Initialize Session Tickets */
717    if (session_ticket_key.data != NULL && ctxt->sc->tickets != 0) {
718        err = gnutls_session_ticket_enable_server(ctxt->session, &session_ticket_key);
719        if (err != GNUTLS_E_SUCCESS)
720            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c, "gnutls_session_ticket_enable_server failed!");
721    }
722
723    /* Set Default Priority */
724        err = gnutls_priority_set_direct(ctxt->session, "NORMAL", NULL);
725    if (err != GNUTLS_E_SUCCESS)
726        ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c, "gnutls_priority_set_direct failed!");
727    /* Set Handshake function */
728    gnutls_handshake_set_post_client_hello_function(ctxt->session,
729            mgs_select_virtual_server_cb);
730    /* Initialize Session Cache */
731    mgs_cache_session_init(ctxt);
732
733    /* Set pull, push & ptr functions */
734    gnutls_transport_set_pull_function(ctxt->session,
735            mgs_transport_read);
736    gnutls_transport_set_push_function(ctxt->session,
737            mgs_transport_write);
738    gnutls_transport_set_ptr(ctxt->session, ctxt);
739    /* Add IO filters */
740    ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME,
741            ctxt, NULL, c);
742    ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
743            ctxt, NULL, c);
744}
745
746int mgs_hook_pre_connection(conn_rec * c, void *csd __attribute__((unused)))
747{
748    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
749
750    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
751        ap_get_module_config(c->base_server->module_config, &gnutls_module);
752    mgs_handle_t *ctxt = (mgs_handle_t *)
753        ap_get_module_config(c->conn_config, &gnutls_module);
754
755    if ((sc && (!sc->enabled || sc->proxy_enabled == GNUTLS_ENABLED_TRUE))
756        || (ctxt && ctxt->enabled == GNUTLS_ENABLED_FALSE))
757    {
758        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s declined connection",
759                      __func__);
760        return DECLINED;
761    }
762
763    create_gnutls_handle(c);
764    return OK;
765}
766
767int mgs_hook_fixups(request_rec * r) {
768    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
769    char buf[AP_IOBUFSIZE];
770    const char *tmp;
771    size_t len;
772    mgs_handle_t *ctxt;
773    int rv = OK;
774
775    if (r == NULL)
776        return DECLINED;
777
778    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
779    apr_table_t *env = r->subprocess_env;
780
781    ctxt = ap_get_module_config(r->connection->conn_config,
782                                &gnutls_module);
783
784    if (!ctxt || ctxt->enabled != GNUTLS_ENABLED_TRUE || ctxt->session == NULL)
785    {
786        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "request declined in %s", __func__);
787        return DECLINED;
788    }
789
790    apr_table_setn(env, "HTTPS", "on");
791
792    apr_table_setn(env, "SSL_VERSION_LIBRARY",
793            "GnuTLS/" LIBGNUTLS_VERSION);
794    apr_table_setn(env, "SSL_VERSION_INTERFACE",
795            "mod_gnutls/" MOD_GNUTLS_VERSION);
796
797    apr_table_setn(env, "SSL_PROTOCOL",
798            gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session)));
799
800    /* should have been called SSL_CIPHERSUITE instead */
801    apr_table_setn(env, "SSL_CIPHER",
802            gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
803                                         gnutls_cipher_get(ctxt->session),
804                                         gnutls_mac_get(ctxt->session)));
805
806    apr_table_setn(env, "SSL_COMPRESS_METHOD",
807            gnutls_compression_get_name(gnutls_compression_get(ctxt->session)));
808
809#ifdef ENABLE_SRP
810    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
811        tmp = gnutls_srp_server_get_username(ctxt->session);
812        apr_table_setn(env, "SSL_SRP_USER", (tmp != NULL) ? tmp : "");
813    } else {
814        apr_table_unset(env, "SSL_SRP_USER");
815    }
816#endif
817
818    if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
819        apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
820
821    unsigned int key_size = 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
822    tmp = apr_psprintf(r->pool, "%u", key_size);
823
824    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
825
826    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
827
828    apr_table_setn(env, "SSL_CIPHER_EXPORT",
829            (key_size <= 40) ? "true" : "false");
830
831    int dhsize = gnutls_dh_get_prime_bits(ctxt->session);
832    if (dhsize > 0) {
833        tmp = apr_psprintf(r->pool, "%d", dhsize);
834        apr_table_setn(env, "SSL_DH_PRIME_BITS", tmp);
835    }
836
837    len = sizeof (sbuf);
838    gnutls_session_get_id(ctxt->session, sbuf, &len);
839    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
840    apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
841
842    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
843                mgs_add_common_cert_vars(r, ctxt->sc->certs_x509_chain[0], 0, ctxt->sc->export_certificates_size);
844        } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
845        mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_pgp, 0, ctxt->sc->export_certificates_size);
846        }
847
848    return rv;
849}
850
851int mgs_hook_authz(request_rec * r) {
852    int rv;
853    mgs_handle_t *ctxt;
854    mgs_dirconf_rec *dc;
855
856    if (r == NULL)
857        return DECLINED;
858
859    dc = ap_get_module_config(r->per_dir_config, &gnutls_module);
860
861    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
862    ctxt =
863            ap_get_module_config(r->connection->conn_config,
864            &gnutls_module);
865
866    if (!ctxt || ctxt->session == NULL) {
867        return DECLINED;
868    }
869
870    if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
871        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
872                "GnuTLS: Directory set to Ignore Client Certificate!");
873    } else {
874        if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
875            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
876                    "GnuTLS: Attempting to rehandshake with peer. %d %d",
877                    ctxt->sc->client_verify_mode,
878                    dc->client_verify_mode);
879
880            /* If we already have a client certificate, there's no point in
881             * re-handshaking... */
882            rv = mgs_cert_verify(r, ctxt);
883            if (rv != DECLINED && rv != HTTP_FORBIDDEN)
884                return rv;
885
886            gnutls_certificate_server_set_request
887                    (ctxt->session, dc->client_verify_mode);
888
889            if (mgs_rehandshake(ctxt) != 0) {
890                return HTTP_FORBIDDEN;
891            }
892        } else if (ctxt->sc->client_verify_mode ==
893                GNUTLS_CERT_IGNORE) {
894#if MOD_GNUTLS_DEBUG
895            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
896                    "GnuTLS: Peer is set to IGNORE");
897#endif
898            return DECLINED;
899        }
900        rv = mgs_cert_verify(r, ctxt);
901        if (rv != DECLINED
902            && (rv != HTTP_FORBIDDEN
903                || dc->client_verify_mode == GNUTLS_CERT_REQUIRE
904                || (dc->client_verify_mode == -1
905                    && ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUIRE)))
906        {
907            return rv;
908        }
909    }
910
911    return DECLINED;
912}
913
914/* variables that are not sent by default:
915 *
916 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
917 * SSL_SERVER_CERT      string  PEM-encoded client certificate
918 */
919
920/* @param side is either 0 for SERVER or 1 for CLIENT
921 *
922 * @param export_cert_size (int) maximum size for environment variable
923 * to use for the PEM-encoded certificate (0 means do not export)
924 */
925#define MGS_SIDE(suffix) ((side==0) ? "SSL_SERVER" suffix : "SSL_CLIENT" suffix)
926
927static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, size_t export_cert_size) {
928    unsigned char sbuf[64]; /* buffer to hold serials */
929    char buf[AP_IOBUFSIZE];
930    const char *tmp;
931    char *tmp2;
932    size_t len;
933    int ret, i;
934
935    if (r == NULL)
936        return;
937
938    apr_table_t *env = r->subprocess_env;
939
940    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
941    if (export_cert_size > 0) {
942        len = 0;
943        ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL, &len);
944        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
945            if (len >= export_cert_size) {
946                apr_table_setn(env, MGS_SIDE("_CERT"), "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
947                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
948                              "GnuTLS: Failed to export too-large X.509 certificate to environment");
949            } else {
950                char* cert_buf = apr_palloc(r->pool, len + 1);
951                if (cert_buf != NULL && gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0) {
952                    cert_buf[len] = 0;
953                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
954                } else {
955                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
956                                  "GnuTLS: failed to export X.509 certificate");
957                }
958            }
959        } else {
960            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
961                          "GnuTLS: dazed and confused about X.509 certificate size");
962        }
963    }
964
965    len = sizeof (buf);
966    gnutls_x509_crt_get_dn(cert, buf, &len);
967    apr_table_setn(env, MGS_SIDE("_S_DN"), apr_pstrmemdup(r->pool, buf, len));
968
969    len = sizeof (buf);
970    gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
971    apr_table_setn(env, MGS_SIDE("_I_DN"), apr_pstrmemdup(r->pool, buf, len));
972
973    len = sizeof (sbuf);
974    gnutls_x509_crt_get_serial(cert, sbuf, &len);
975    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
976    apr_table_setn(env, MGS_SIDE("_M_SERIAL"), apr_pstrdup(r->pool, tmp));
977
978    ret = gnutls_x509_crt_get_version(cert);
979    if (ret > 0)
980        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
981                       apr_psprintf(r->pool, "%u", ret));
982
983    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "X.509");
984
985    tmp =
986            mgs_time2sz(gnutls_x509_crt_get_expiration_time
987            (cert), buf, sizeof (buf));
988    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
989
990    tmp =
991            mgs_time2sz(gnutls_x509_crt_get_activation_time
992            (cert), buf, sizeof (buf));
993    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
994
995    ret = gnutls_x509_crt_get_signature_algorithm(cert);
996    if (ret >= 0) {
997        apr_table_setn(env, MGS_SIDE("_A_SIG"),
998                gnutls_sign_algorithm_get_name(ret));
999    }
1000
1001    ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
1002    if (ret >= 0) {
1003        apr_table_setn(env, MGS_SIDE("_A_KEY"),
1004                gnutls_pk_algorithm_get_name(ret));
1005    }
1006
1007    /* export all the alternative names (DNS, RFC822 and URI) */
1008    for (i = 0; !(ret < 0); i++) {
1009        const char *san, *sanlabel;
1010        len = 0;
1011        ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
1012                NULL, &len,
1013                NULL);
1014
1015        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) {
1016            tmp2 = apr_palloc(r->pool, len + 1);
1017
1018            ret =
1019                    gnutls_x509_crt_get_subject_alt_name(cert, i,
1020                    tmp2,
1021                    &len,
1022                    NULL);
1023            tmp2[len] = 0;
1024
1025            sanlabel = apr_psprintf(r->pool, "%s%u", MGS_SIDE("_S_AN"), i);
1026            if (ret == GNUTLS_SAN_DNSNAME) {
1027                san = apr_psprintf(r->pool, "DNSNAME:%s", tmp2);
1028            } else if (ret == GNUTLS_SAN_RFC822NAME) {
1029                san = apr_psprintf(r->pool, "RFC822NAME:%s", tmp2);
1030            } else if (ret == GNUTLS_SAN_URI) {
1031                san = apr_psprintf(r->pool, "URI:%s", tmp2);
1032            } else {
1033                san = "UNSUPPORTED";
1034            }
1035            apr_table_setn(env, sanlabel, san);
1036        }
1037    }
1038}
1039
1040
1041/* @param side 0: server, 1: client
1042 *
1043 * @param export_cert_size (int) maximum size for environment variable
1044 * to use for the PEM-encoded certificate (0 means do not export)
1045 */
1046static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, size_t export_cert_size) {
1047
1048        unsigned char sbuf[64]; /* buffer to hold serials */
1049    char buf[AP_IOBUFSIZE];
1050    const char *tmp;
1051    size_t len;
1052    int ret;
1053
1054    if (r == NULL)
1055        return;
1056
1057    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1058    apr_table_t *env = r->subprocess_env;
1059
1060    if (export_cert_size > 0) {
1061        len = 0;
1062        ret = gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, NULL, &len);
1063        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1064            if (len >= export_cert_size) {
1065                apr_table_setn(env, MGS_SIDE("_CERT"),
1066                               "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1067                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1068                              "GnuTLS: Failed to export too-large OpenPGP certificate to environment");
1069            } else {
1070                char* cert_buf = apr_palloc(r->pool, len + 1);
1071                if (cert_buf != NULL && gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0) {
1072                    cert_buf[len] = 0;
1073                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
1074                } else {
1075                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1076                                  "GnuTLS: failed to export OpenPGP certificate");
1077                }
1078            }
1079        } else {
1080            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1081                          "GnuTLS: dazed and confused about OpenPGP certificate size");
1082        }
1083    }
1084
1085    len = sizeof (buf);
1086    gnutls_openpgp_crt_get_name(cert, 0, buf, &len);
1087    apr_table_setn(env, MGS_SIDE("_NAME"), apr_pstrmemdup(r->pool, buf, len));
1088
1089    len = sizeof (sbuf);
1090    gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len);
1091    tmp = mgs_session_id2sz(sbuf, len, buf, sizeof (buf));
1092    apr_table_setn(env, MGS_SIDE("_FINGERPRINT"), apr_pstrdup(r->pool, tmp));
1093
1094    ret = gnutls_openpgp_crt_get_version(cert);
1095    if (ret > 0)
1096        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1097                       apr_psprintf(r->pool, "%u", ret));
1098
1099    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "OPENPGP");
1100
1101    tmp =
1102            mgs_time2sz(gnutls_openpgp_crt_get_expiration_time
1103            (cert), buf, sizeof (buf));
1104    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
1105
1106    tmp =
1107            mgs_time2sz(gnutls_openpgp_crt_get_creation_time
1108            (cert), buf, sizeof (buf));
1109    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
1110
1111    ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL);
1112    if (ret >= 0) {
1113        apr_table_setn(env, MGS_SIDE("_A_KEY"), gnutls_pk_algorithm_get_name(ret));
1114    }
1115
1116}
1117
1118/* TODO: Allow client sending a X.509 certificate chain */
1119static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) {
1120    const gnutls_datum_t *cert_list;
1121    unsigned int cert_list_size, status;
1122    int rv = GNUTLS_E_NO_CERTIFICATE_FOUND, ret;
1123    unsigned int ch_size = 0;
1124
1125    union {
1126        gnutls_x509_crt_t x509[MAX_CHAIN_SIZE];
1127        gnutls_openpgp_crt_t pgp;
1128    } cert;
1129    apr_time_t expiration_time, cur_time;
1130
1131    if (r == NULL || ctxt == NULL || ctxt->session == NULL)
1132        return HTTP_FORBIDDEN;
1133
1134    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1135    cert_list =
1136            gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1137
1138    if (cert_list == NULL || cert_list_size == 0) {
1139        /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
1140         */
1141        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1142            return OK;
1143
1144        /* no certificate provided by the client, but one was required. */
1145        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1146                "GnuTLS: Failed to Verify Peer: "
1147                "Client did not submit a certificate");
1148        return HTTP_FORBIDDEN;
1149    }
1150
1151    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1152        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1153                "GnuTLS: A Chain of %d certificate(s) was provided for validation",
1154                cert_list_size);
1155
1156        for (ch_size = 0; ch_size < cert_list_size; ch_size++) {
1157            gnutls_x509_crt_init(&cert.x509[ch_size]);
1158            rv = gnutls_x509_crt_import(cert.x509[ch_size],
1159                    &cert_list[ch_size],
1160                    GNUTLS_X509_FMT_DER);
1161            // When failure to import, leave the loop
1162            if (rv != GNUTLS_E_SUCCESS) {
1163                if (ch_size < 1) {
1164                    ap_log_rerror(APLOG_MARK,
1165                            APLOG_INFO, 0, r,
1166                            "GnuTLS: Failed to Verify Peer: "
1167                            "Failed to import peer certificates.");
1168                    ret = HTTP_FORBIDDEN;
1169                    goto exit;
1170                }
1171                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1172                        "GnuTLS: Failed to import some peer certificates. Using %d certificates",
1173                        ch_size);
1174                rv = GNUTLS_E_SUCCESS;
1175                break;
1176            }
1177        }
1178    } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
1179        if (cert_list_size > 1) {
1180            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1181                    "GnuTLS: Failed to Verify Peer: "
1182                    "Chained Client Certificates are not supported.");
1183            return HTTP_FORBIDDEN;
1184        }
1185
1186        gnutls_openpgp_crt_init(&cert.pgp);
1187        rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0],
1188                GNUTLS_OPENPGP_FMT_RAW);
1189
1190    } else
1191        return HTTP_FORBIDDEN;
1192
1193    if (rv < 0) {
1194        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1195                "GnuTLS: Failed to Verify Peer: "
1196                "Failed to import peer certificates.");
1197        ret = HTTP_FORBIDDEN;
1198        goto exit;
1199    }
1200
1201    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1202        apr_time_ansi_put(&expiration_time,
1203                gnutls_x509_crt_get_expiration_time
1204                (cert.x509[0]));
1205
1206        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1207                      "GnuTLS: Verifying list of %d certificate(s) via method '%s'",
1208                      ch_size, mgs_readable_cvm(ctxt->sc->client_verify_method));
1209        switch(ctxt->sc->client_verify_method) {
1210        case mgs_cvm_cartel:
1211            rv = gnutls_x509_crt_list_verify(cert.x509, ch_size,
1212                                             ctxt->sc->ca_list,
1213                                             ctxt->sc->ca_list_size,
1214                                             NULL, 0, 0, &status);
1215            break;
1216#ifdef ENABLE_MSVA
1217        case mgs_cvm_msva:
1218        {
1219            struct msv_response* resp = NULL;
1220            struct msv_query q = { .context="https", .peertype="client", .pkctype="x509pem" };
1221            msv_ctxt_t ctx = msv_ctxt_init(NULL);
1222            char cert_pem_buf[10 * 1024];
1223            size_t len = sizeof (cert_pem_buf);
1224
1225            rv = 0;
1226            if (gnutls_x509_crt_export(cert.x509[0], GNUTLS_X509_FMT_PEM, cert_pem_buf, &len) >= 0) {
1227                /* FIXME : put together a name from the cert we received, instead of hard-coding this value: */
1228                q.peername = mgs_x509_construct_uid(r, cert.x509[0]);
1229                q.pkcdata = cert_pem_buf;
1230                rv = msv_query_agent(ctx, q, &resp);
1231                if (rv == LIBMSV_ERROR_SUCCESS) {
1232                    status = 0;
1233                } else if (rv == LIBMSV_ERROR_INVALID) {
1234                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1235                                  "GnuTLS: Monkeysphere validation failed: (message: %s)", resp->message);
1236                    status = GNUTLS_CERT_INVALID;
1237                } else {
1238                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1239                                  "GnuTLS: Error communicating with the Monkeysphere Validation Agent: (%d) %s", rv, msv_strerror(ctx, rv));
1240                    status = GNUTLS_CERT_INVALID;
1241                    rv = -1;
1242                }
1243            } else {
1244                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1245                              "GnuTLS: Could not convert the client certificate to PEM format");
1246                status = GNUTLS_CERT_INVALID;
1247                rv = GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
1248            }
1249            msv_response_destroy(resp);
1250            msv_ctxt_destroy(ctx);
1251        }
1252            break;
1253#endif
1254        default:
1255            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1256                          "GnuTLS: Failed to Verify X.509 Peer: method '%s' is not supported",
1257                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1258        }
1259
1260    } else {
1261        apr_time_ansi_put(&expiration_time,
1262                gnutls_openpgp_crt_get_expiration_time
1263                (cert.pgp));
1264
1265        switch(ctxt->sc->client_verify_method) {
1266        case mgs_cvm_cartel:
1267            rv = gnutls_openpgp_crt_verify_ring(cert.pgp,
1268                                                ctxt->sc->pgp_list, 0,
1269                                                &status);
1270            break;
1271#ifdef ENABLE_MSVA
1272        case mgs_cvm_msva:
1273            /* need to set status and rv */
1274            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1275                          "GnuTLS:  OpenPGP verification via MSVA is not yet implemented");
1276            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1277            break;
1278#endif
1279        default:
1280            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1281                          "GnuTLS: Failed to Verify OpenPGP Peer: method '%s' is not supported",
1282                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1283        }
1284    }
1285
1286    if (rv < 0) {
1287        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1288                "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
1289                rv, gnutls_strerror(rv));
1290        if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1291            ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1292                "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?");
1293        ret = HTTP_FORBIDDEN;
1294        goto exit;
1295    }
1296
1297    /* TODO: X509 CRL Verification. */
1298    /* May add later if anyone needs it.
1299     */
1300    /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1301
1302    cur_time = apr_time_now();
1303
1304    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1305        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1306                "GnuTLS: Could not find Signer for Peer Certificate");
1307    }
1308
1309    if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
1310        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1311                "GnuTLS: Peer's Certificate signer is not a CA");
1312    }
1313
1314    if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
1315        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1316                "GnuTLS: Peer's Certificate is using insecure algorithms");
1317    }
1318
1319    if (status & GNUTLS_CERT_EXPIRED
1320            || status & GNUTLS_CERT_NOT_ACTIVATED) {
1321        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1322                "GnuTLS: Peer's Certificate signer is expired or not yet activated");
1323    }
1324
1325    if (status & GNUTLS_CERT_INVALID) {
1326        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1327                "GnuTLS: Peer Certificate is invalid.");
1328    } else if (status & GNUTLS_CERT_REVOKED) {
1329        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1330                "GnuTLS: Peer Certificate is revoked.");
1331    }
1332
1333    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
1334        mgs_add_common_cert_vars(r, cert.x509[0], 1, ctxt->sc->export_certificates_size);
1335    else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP)
1336        mgs_add_common_pgpcert_vars(r, cert.pgp, 1, ctxt->sc->export_certificates_size);
1337
1338    {
1339        /* days remaining */
1340        unsigned long remain =
1341                (apr_time_sec(expiration_time) -
1342                apr_time_sec(cur_time)) / 86400;
1343        apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
1344                apr_psprintf(r->pool, "%lu", remain));
1345    }
1346
1347    if (status == 0) {
1348        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1349                "SUCCESS");
1350        ret = OK;
1351    } else {
1352        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1353                "FAILED");
1354        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1355            ret = OK;
1356        else
1357            ret = HTTP_FORBIDDEN;
1358    }
1359
1360exit:
1361    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1362        unsigned int i;
1363        for (i = 0; i < ch_size; i++) {
1364            gnutls_x509_crt_deinit(cert.x509[i]);
1365        }
1366    } else if (gnutls_certificate_type_get(ctxt->session) ==
1367            GNUTLS_CRT_OPENPGP)
1368        gnutls_openpgp_crt_deinit(cert.pgp);
1369    return ret;
1370
1371
1372}
1373
1374#ifdef ENABLE_MSVA
1375/* this section of code is used only when trying to talk to the MSVA */
1376static const char* mgs_x509_leaf_oid_from_dn(apr_pool_t *pool, const char* oid, gnutls_x509_crt_t cert) {
1377    int rv=GNUTLS_E_SUCCESS, i;
1378    size_t sz=0, lastsz=0;
1379    char* data=NULL;
1380
1381    i = -1;
1382    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1383        i++;
1384        lastsz=sz;
1385        sz=0;
1386        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i, 0, NULL, &sz);
1387    }
1388    if (i > 0) {
1389        data = apr_palloc(pool, lastsz);
1390        sz=lastsz;
1391        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i-1, 0, data, &sz);
1392        if (rv == GNUTLS_E_SUCCESS)
1393            return data;
1394    }
1395    return NULL;
1396}
1397
1398static const char* mgs_x509_first_type_from_san(apr_pool_t *pool, gnutls_x509_subject_alt_name_t target, gnutls_x509_crt_t cert) {
1399    int rv=GNUTLS_E_SUCCESS;
1400    size_t sz;
1401    char* data=NULL;
1402    unsigned int i;
1403    gnutls_x509_subject_alt_name_t thistype;
1404
1405    i = 0;
1406    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1407        sz = 0;
1408        rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, NULL, &sz, &thistype, NULL);
1409        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && thistype == target) {
1410            data = apr_palloc(pool, sz);
1411            rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, data, &sz, &thistype, NULL);
1412            if (rv >=0 && (thistype == target))
1413                return data;
1414        }
1415        i++;
1416    }
1417    return NULL;
1418}
1419
1420
1421/* Create a string representing a candidate User ID from an X.509
1422 * certificate
1423
1424 * We need this for client certification because a client gives us a
1425 * certificate, but doesn't tell us (in any other way) who they are
1426 * trying to authenticate as.
1427
1428 * TODO: we might need another parallel for OpenPGP, but for that it's
1429 * much simpler: we can just assume that the first User ID marked as
1430 * "primary" (or the first User ID, period) is the identity the user
1431 * is trying to present as.
1432
1433 * one complaint might be "but the user wanted to be another identity,
1434 * which is also in the certificate (e.g. in a SubjectAltName)"
1435 * However, given that any user can regenerate their own X.509
1436 * certificate with their own public key content, they should just do
1437 * so, and not expect us to guess at their identity :)
1438
1439 * This function allocates it's response from the pool given it.  When
1440 * that pool is reclaimed, the response will also be deallocated.
1441
1442 * FIXME: what about extracting a server-style cert
1443 *        (e.g. https://imposter.example) from the DN or any sAN?
1444
1445 * FIXME: what if we want to call this outside the context of a
1446 *        request?  That complicates the logging.
1447 */
1448static const char* mgs_x509_construct_uid(request_rec *r, gnutls_x509_crt_t cert) {
1449    /* basic strategy, assuming humans are the users: we are going to
1450     * try to reconstruct a "conventional" User ID by pulling in a
1451     * name, comment, and e-mail address.
1452     */
1453    apr_pool_t *pool = r->pool;
1454    const char *name=NULL, *comment=NULL, *email=NULL;
1455    const char *ret=NULL;
1456    /* subpool for temporary allocation: */
1457    apr_pool_t *sp=NULL;
1458
1459    if (APR_SUCCESS != apr_pool_create(&sp, pool))
1460        return NULL; /* i'm assuming that libapr would log this kind
1461                      * of error on its own */
1462
1463     /* Name
1464
1465     the name comes from the leaf commonName of the cert's Subject.
1466
1467     (MAYBE: should we look at trying to assemble a candidate from
1468             givenName, surName, suffix, etc?  the "name" field
1469             appears to be case-insensitive, which seems problematic
1470             from what we expect; see:
1471             http://www.itu.int/rec/T-REC-X.520-200102-s/e )
1472
1473     (MAYBE: should we try pulling a commonName or otherName or
1474             something from subjectAltName? see:
1475             https://tools.ietf.org/html/rfc5280#section-4.2.1.6
1476             GnuTLS does not support looking for Common Names in the
1477             SAN yet)
1478     */
1479    name = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_COMMON_NAME, cert);
1480
1481    /* Comment
1482
1483       I am inclined to punt on this for now, as Comment has been so
1484       atrociously misused in OpenPGP.  Perhaps if there is a
1485       pseudonym (OID 2.5.4.65, aka GNUTLS_OID_X520_PSEUDONYM) field
1486       in the subject or sAN?
1487    */
1488    comment = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_PSEUDONYM, cert);
1489
1490    /* E-mail
1491
1492       This should be the the first rfc822Name from the sAN.
1493
1494       failing that, we'll take the leaf email in the certificate's
1495       subject; this is a deprecated use though.
1496     */
1497    email = mgs_x509_first_type_from_san(sp, GNUTLS_SAN_RFC822NAME, cert);
1498    if (email == NULL)
1499        email = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_PKCS9_EMAIL, cert);
1500
1501    /* assemble all the parts: */
1502
1503    /* must have at least a name or an e-mail. */
1504    if (name == NULL && email == NULL) {
1505        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1506                "GnuTLS: Need either a name or an e-mail address to get a User ID from an X.509 certificate.");
1507        goto end;
1508    }
1509    if (name) {
1510        if (comment) {
1511            if (email) {
1512                ret = apr_psprintf(pool, "%s (%s) <%s>", name, comment, email);
1513            } else {
1514                ret = apr_psprintf(pool, "%s (%s)", name, comment);
1515            }
1516        } else {
1517            if (email) {
1518                ret = apr_psprintf(pool, "%s <%s>", name, email);
1519            } else {
1520                ret = apr_pstrdup(pool, name);
1521            }
1522        }
1523    } else {
1524        if (comment) {
1525            ret = apr_psprintf(pool, "(%s) <%s>", comment, email);
1526        } else {
1527            ret = apr_psprintf(pool, "<%s>", email);
1528        }
1529    }
1530
1531end:
1532    apr_pool_destroy(sp);
1533    return ret;
1534}
1535#endif /* ENABLE_MSVA */
1536
1537static int mgs_status_hook(request_rec *r, int flags __attribute__((unused)))
1538{
1539    mgs_srvconf_rec *sc;
1540
1541    if (r == NULL)
1542        return OK;
1543
1544    sc = (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config, &gnutls_module);
1545
1546    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1547
1548    ap_rputs("<hr>\n", r);
1549    ap_rputs("<h2>GnuTLS Information:</h2>\n<dl>\n", r);
1550
1551    ap_rprintf(r, "<dt>GnuTLS version:</dt><dd>%s</dd>\n", gnutls_check_version(NULL));
1552    ap_rputs("<dt>Built against:</dt><dd>" GNUTLS_VERSION "</dd>\n", r);
1553    ap_rprintf(r, "<dt>using TLS:</dt><dd>%s</dd>\n", (sc->enabled == GNUTLS_ENABLED_FALSE ? "no" : "yes"));
1554    if (sc->enabled != GNUTLS_ENABLED_FALSE) {
1555        mgs_handle_t* ctxt;
1556        ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module);
1557        if (ctxt && ctxt->session != NULL) {
1558#if GNUTLS_VERSION_MAJOR < 3
1559            ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n",
1560                gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
1561                gnutls_cipher_get(ctxt->session),
1562                gnutls_mac_get(ctxt->session)));
1563#else
1564            char* z = NULL;
1565            z = gnutls_session_get_desc(ctxt->session);
1566            if (z) {
1567                ap_rprintf(r, "<dt>This TLS Session:</dt><dd>%s</dd>\n", z);
1568                gnutls_free(z);
1569            }
1570#endif
1571        }
1572    }
1573
1574    ap_rputs("</dl>\n", r);
1575    return OK;
1576}
1577
Note: See TracBrowser for help on using the repository browser.