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

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

Workarounds for OpenPGP key handling

Commit 031acac9c6541034777f8917633164b51f6bd10a 'Use the new (3.1.3+)
GnuTLS APIs to obtain private keys' led to failed handshakes when using
OpenPGP keys for authentication. Debugging revealed two separate issues,
this commit adds workarounds for both.

The first problem was that the supported certificate types for the
session were not set correctly. This is a known bug in
gnutls_certificate_set_retrieve_function2 [1], the workaround comes from
[2]. The bug should be fixed in GnuTLS 3.3.12, hence the version guard.

After this problem was fixed, segfaults occurred during handshake. A
Valgrind trace showed attemts to access memory that had been free'd in
gnutls_privkey_import_openpgp_raw. I could work around the issue by
loading the key into a gnutls_openpgp_privkey_t structure first and then
importing it into the gnutls_privkey_t using
gnutls_privkey_import_openpgp afterwards.

Thank you to Nikos Mavrogiannopoulos for very fast help with debugging!

[1] https://lists.gnupg.org/pipermail/gnutls-devel/2015-January/007377.html
[2] https://github.com/vanrein/tlspool/commit/4938102d3d1b086491d147e6c8e4e2a02825fc12

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