source: mod_gnutls/src/gnutls_hooks.c @ c39ae1a

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

Disable GnuTLSSessionTickets by default as described in handbook

The handbook clearly states that the default value for
GnuTLSSessionTickets is "off", but the actual setting in post config
was the opposite (which matched mod_ssl behavior). The code has been
changed to match documentation.

Additionally the handbook has been expanded regarding session ticket
use and security. The comment about the cache timeout being used for
session ticket expiration has been removed for being plainly wrong.

  • Property mode set to 100644
File size: 71.3 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2008, 2014 Nikos Mavrogiannopoulos
4 *  Copyright 2011 Dash Shendy
5 *  Copyright 2013-2014 Daniel Kahn Gillmor
6 *  Copyright 2015-2016 Thomas Klute
7 *
8 *  Licensed under the Apache License, Version 2.0 (the "License");
9 *  you may not use this file except in compliance with the License.
10 *  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 *  Unless required by applicable law or agreed to in writing, software
15 *  distributed under the License is distributed on an "AS IS" BASIS,
16 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 *  See the License for the specific language governing permissions and
18 *  limitations under the License.
19 *
20 */
21
22#include "mod_gnutls.h"
23#include "gnutls_cache.h"
24#include "gnutls_ocsp.h"
25#include "http_vhost.h"
26#include "ap_mpm.h"
27#include "mod_status.h"
28#include <util_mutex.h>
29#include <apr_escape.h>
30
31#ifdef ENABLE_MSVA
32#include <msv/msv.h>
33#endif
34
35#ifdef APLOG_USE_MODULE
36APLOG_USE_MODULE(gnutls);
37#endif
38
39#if MOD_GNUTLS_DEBUG
40static apr_file_t *debug_log_fp;
41#endif
42
43#define IS_PROXY_STR(c) \
44    ((c->is_proxy == GNUTLS_ENABLED_TRUE) ? "proxy " : "")
45
46static gnutls_datum_t session_ticket_key = {NULL, 0};
47
48static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt);
49/* use side==0 for server and side==1 for client */
50static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, size_t export_cert_size);
51static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, size_t export_cert_size);
52static int mgs_status_hook(request_rec *r, int flags);
53#ifdef ENABLE_MSVA
54static const char* mgs_x509_construct_uid(request_rec * pool, gnutls_x509_crt_t cert);
55#endif
56static int load_proxy_x509_credentials(apr_pool_t *pconf, apr_pool_t *ptemp, server_rec *s)
57    __attribute__((nonnull));
58
59/* Pool Cleanup Function */
60apr_status_t mgs_cleanup_pre_config(void *data __attribute__((unused)))
61{
62    /* Free session ticket master key */
63#if GNUTLS_VERSION_NUMBER >= 0x030400
64    gnutls_memset(session_ticket_key.data, 0, session_ticket_key.size);
65#endif
66    gnutls_free(session_ticket_key.data);
67    session_ticket_key.data = NULL;
68    session_ticket_key.size = 0;
69        /* Deinitialize GnuTLS Library */
70    gnutls_global_deinit();
71    return APR_SUCCESS;
72}
73
74/* Logging Function for Maintainers */
75#if MOD_GNUTLS_DEBUG
76static void gnutls_debug_log_all(int level, const char *str) {
77    apr_file_printf(debug_log_fp, "<%d> %s", level, str);
78}
79#define _gnutls_log apr_file_printf
80#else
81#define _gnutls_log(...)
82#endif
83
84static const char* mgs_readable_cvm(mgs_client_verification_method_e m) {
85    switch(m) {
86    case mgs_cvm_unset:
87        return "unset";
88    case mgs_cvm_cartel:
89        return "cartel";
90    case mgs_cvm_msva:
91        return "msva";
92    }
93    return "unknown";
94}
95
96/* Pre-Configuration HOOK: Runs First */
97int mgs_hook_pre_config(apr_pool_t * pconf, apr_pool_t * plog, apr_pool_t * ptemp __attribute__((unused))) {
98
99/* Maintainer Logging */
100#if MOD_GNUTLS_DEBUG
101    apr_file_open(&debug_log_fp, "/tmp/gnutls_debug", APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, pconf);
102    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
103    gnutls_global_set_log_level(9);
104    gnutls_global_set_log_function(gnutls_debug_log_all);
105    _gnutls_log(debug_log_fp, "gnutls: %s\n", gnutls_check_version(NULL));
106#endif
107
108    int ret;
109
110        /* Check for required GnuTLS Library Version */
111    if (gnutls_check_version(LIBGNUTLS_VERSION) == NULL) {
112                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_check_version() failed. Required: "
113                                        "gnutls-%s Found: gnutls-%s", LIBGNUTLS_VERSION, gnutls_check_version(NULL));
114        return DONE;
115    }
116
117        /* Initialize GnuTLS Library */
118    ret = gnutls_global_init();
119    if (ret < 0) {
120                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_global_init: %s", gnutls_strerror(ret));
121                return DONE;
122    }
123
124        /* Generate a Session Key */
125    ret = gnutls_session_ticket_key_generate(&session_ticket_key);
126    if (ret < 0) {
127                ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, plog, "gnutls_session_ticket_key_generate: %s", gnutls_strerror(ret));
128                return DONE;
129    }
130
131    AP_OPTIONAL_HOOK(status_hook, mgs_status_hook, NULL, NULL, APR_HOOK_MIDDLE);
132
133    ap_mutex_register(pconf, MGS_CACHE_MUTEX_NAME, NULL, APR_LOCK_DEFAULT, 0);
134    ap_mutex_register(pconf, MGS_OCSP_MUTEX_NAME, NULL, APR_LOCK_DEFAULT, 0);
135
136    /* Register a pool clean-up function */
137    apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config, apr_pool_cleanup_null);
138
139    return OK;
140}
141
142static int mgs_select_virtual_server_cb(gnutls_session_t session) {
143
144    mgs_handle_t *ctxt = NULL;
145    mgs_srvconf_rec *tsc = NULL;
146    int ret = 0;
147
148    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
149
150    ctxt = gnutls_transport_get_ptr(session);
151
152    /* find the virtual server */
153    tsc = mgs_find_sni_server(session);
154
155    if (tsc != NULL) {
156        // Found a TLS vhost based on the SNI from the client; use it instead.
157        ctxt->sc = tsc;
158        }
159
160    gnutls_certificate_server_set_request(session, ctxt->sc->client_verify_mode);
161
162    /* Set x509 credentials */
163    gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctxt->sc->certs);
164    /* Set Anon credentials */
165    gnutls_credentials_set(session, GNUTLS_CRD_ANON, ctxt->sc->anon_creds);
166
167    if (ctxt->sc->ocsp_staple)
168    {
169        gnutls_certificate_set_ocsp_status_request_function(ctxt->sc->certs,
170                                                            mgs_get_ocsp_response,
171                                                            ctxt);
172    }
173
174#ifdef ENABLE_SRP
175        /* Set SRP credentials */
176    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
177        gnutls_credentials_set(session, GNUTLS_CRD_SRP, ctxt->sc->srp_creds);
178    }
179#endif
180
181    /* update the priorities - to avoid negotiating a ciphersuite that is not
182     * enabled on this virtual server. Note that here we ignore the version
183     * negotiation.
184     */
185
186    ret = gnutls_priority_set(session, ctxt->sc->priorities);
187    /* actually it shouldn't fail since we have checked at startup */
188    return ret;
189
190}
191
192static int cert_retrieve_fn(gnutls_session_t session,
193                            const gnutls_datum_t * req_ca_rdn __attribute__((unused)),
194                            int nreqs __attribute__((unused)),
195                            const gnutls_pk_algorithm_t * pk_algos __attribute__((unused)),
196                            int pk_algos_length __attribute__((unused)),
197                            gnutls_pcert_st **pcerts,
198                            unsigned int *pcert_length,
199                            gnutls_privkey_t *privkey)
200{
201    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
202
203    mgs_handle_t *ctxt;
204
205    if (session == NULL) {
206                // ERROR INVALID SESSION
207        return -1;
208    }
209
210    ctxt = gnutls_transport_get_ptr(session);
211
212    if (gnutls_certificate_type_get(session) == GNUTLS_CRT_X509) {
213                // X509 CERTIFICATE
214        *pcerts = ctxt->sc->certs_x509_chain;
215        *pcert_length = ctxt->sc->certs_x509_chain_num;
216        *privkey = ctxt->sc->privkey_x509;
217        return 0;
218    } else if (gnutls_certificate_type_get(session) == GNUTLS_CRT_OPENPGP) {
219                // OPENPGP CERTIFICATE
220        *pcerts = ctxt->sc->cert_pgp;
221        *pcert_length = 1;
222        *privkey = ctxt->sc->privkey_pgp;
223        return 0;
224    } else {
225                // UNKNOWN CERTIFICATE
226            return -1;
227        }
228}
229
230/* Read the common name or the alternative name of the certificate.
231 * We only support a single name per certificate.
232 *
233 * Returns negative on error.
234 */
235static int read_crt_cn(server_rec * s, apr_pool_t * p, gnutls_x509_crt_t cert, char **cert_cn) {
236
237    int rv = 0;
238    size_t data_len;
239
240
241    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
242    *cert_cn = NULL;
243
244    data_len = 0;
245    rv = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, NULL, &data_len);
246
247    if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
248        *cert_cn = apr_palloc(p, data_len);
249        rv = gnutls_x509_crt_get_dn_by_oid(cert,
250                GNUTLS_OID_X520_COMMON_NAME,
251                0, 0, *cert_cn,
252                &data_len);
253    } else { /* No CN return subject alternative name */
254        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
255                "No common name found in certificate for '%s:%d'. Looking for subject alternative name...",
256                s->server_hostname, s->port);
257        rv = 0;
258        /* read subject alternative name */
259        for (int i = 0; !(rv < 0); i++)
260        {
261            data_len = 0;
262            rv = gnutls_x509_crt_get_subject_alt_name(cert, i,
263                    NULL,
264                    &data_len,
265                    NULL);
266
267            if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER
268                    && data_len > 1) {
269                /* FIXME: not very efficient. What if we have several alt names
270                 * before DNSName?
271                 */
272                *cert_cn = apr_palloc(p, data_len + 1);
273
274                rv = gnutls_x509_crt_get_subject_alt_name
275                        (cert, i, *cert_cn, &data_len, NULL);
276                (*cert_cn)[data_len] = 0;
277
278                if (rv == GNUTLS_SAN_DNSNAME)
279                    break;
280            }
281        }
282    }
283
284    return rv;
285}
286
287static int read_pgpcrt_cn(server_rec * s, apr_pool_t * p,
288        gnutls_openpgp_crt_t cert, char **cert_cn) {
289    int rv = 0;
290    size_t data_len;
291
292
293    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
294    *cert_cn = NULL;
295
296    data_len = 0;
297    rv = gnutls_openpgp_crt_get_name(cert, 0, NULL, &data_len);
298
299    if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
300        *cert_cn = apr_palloc(p, data_len);
301        rv = gnutls_openpgp_crt_get_name(cert, 0, *cert_cn,
302                &data_len);
303    } else { /* No CN return subject alternative name */
304        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
305                "No name found in PGP certificate for '%s:%d'.",
306                s->server_hostname, s->port);
307    }
308
309    return rv;
310}
311
312/*
313 * Must return OK or DECLINED on success, something else on
314 * error. These codes are defined in Apache httpd.h along with the
315 * HTTP status codes, so I'm going to use HTTP error codes both for
316 * fun (and to avoid conflicts).
317 */
318int mgs_hook_post_config(apr_pool_t *pconf,
319                         apr_pool_t *plog __attribute__((unused)),
320                         apr_pool_t *ptemp,
321                         server_rec *base_server)
322{
323    int rv;
324    server_rec *s;
325    gnutls_dh_params_t dh_params = NULL;
326    mgs_srvconf_rec *sc;
327    mgs_srvconf_rec *sc_base;
328    void *data = NULL;
329    const char *userdata_key = "mgs_init";
330
331    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
332
333    apr_pool_userdata_get(&data, userdata_key, base_server->process->pool);
334    if (data == NULL) {
335        apr_pool_userdata_set((const void *) 1, userdata_key, apr_pool_cleanup_null, base_server->process->pool);
336    }
337
338    s = base_server;
339    sc_base = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
340
341
342    rv = mgs_cache_post_config(pconf, s, sc_base);
343    if (rv != APR_SUCCESS)
344    {
345        ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
346                     "Post config for cache failed.");
347        return HTTP_INSUFFICIENT_STORAGE;
348    }
349
350    if (sc_base->ocsp_mutex == NULL)
351    {
352        rv = ap_global_mutex_create(&sc_base->ocsp_mutex, NULL,
353                                    MGS_OCSP_MUTEX_NAME, NULL,
354                                    base_server, pconf, 0);
355        if (rv != APR_SUCCESS)
356        {
357            ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, base_server,
358                         "Failed to create mutex '" MGS_OCSP_MUTEX_NAME
359                         "'.");
360            return HTTP_INTERNAL_SERVER_ERROR;
361        }
362    }
363
364    /* If GnuTLSP11Module is set, load the listed PKCS #11
365     * modules. Otherwise system defaults will be used. */
366    if (sc_base->p11_modules != NULL)
367    {
368        rv = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
369        if (rv < 0)
370        {
371            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
372                         "GnuTLS: Initializing PKCS #11 "
373                         "failed: %s (%d).",
374                         gnutls_strerror(rv), rv);
375        }
376        else
377        {
378            for (int i = 0; i < sc_base->p11_modules->nelts; i++)
379            {
380                char *p11_module =
381                    APR_ARRAY_IDX(sc_base->p11_modules, i, char *);
382                rv = gnutls_pkcs11_add_provider(p11_module, NULL);
383                if (rv != GNUTLS_E_SUCCESS)
384                    ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
385                                 "GnuTLS: Loading PKCS #11 provider module %s "
386                                 "failed: %s (%d).",
387                                 p11_module, gnutls_strerror(rv), rv);
388            }
389        }
390    }
391
392    for (s = base_server; s; s = s->next)
393    {
394        sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, &gnutls_module);
395        sc->cache_type = sc_base->cache_type;
396        sc->cache_config = sc_base->cache_config;
397        sc->cache_timeout = sc_base->cache_timeout;
398        sc->cache = sc_base->cache;
399
400        rv = mgs_load_files(pconf, ptemp, s);
401        if (rv != 0) {
402            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
403                "GnuTLS: Loading required files failed."
404                " Shutting Down.");
405            return HTTP_NOT_FOUND;
406        }
407
408        if (sc->ocsp_staple == GNUTLS_ENABLED_UNSET)
409            sc->ocsp_staple = GNUTLS_ENABLED_FALSE;
410
411        sc->ocsp_mutex = sc_base->ocsp_mutex;
412        /* init OCSP configuration if OCSP is enabled for this host */
413        if (sc->ocsp_staple)
414        {
415            rv = mgs_ocsp_post_config_server(pconf, ptemp, s);
416            if (rv != OK && rv != DECLINED)
417                return rv;
418        }
419
420        /* defaults for unset values: */
421        if (sc->enabled == GNUTLS_ENABLED_UNSET)
422            sc->enabled = GNUTLS_ENABLED_FALSE;
423        if (sc->tickets == GNUTLS_ENABLED_UNSET)
424            sc->tickets = GNUTLS_ENABLED_FALSE;
425        if (sc->export_certificates_size < 0)
426            sc->export_certificates_size = 0;
427        if (sc->client_verify_mode == -1)
428            sc->client_verify_mode = GNUTLS_CERT_IGNORE;
429        if (sc->client_verify_method == mgs_cvm_unset)
430            sc->client_verify_method = mgs_cvm_cartel;
431
432        /* Check if the priorities have been set */
433        if (sc->priorities == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
434            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
435                    "GnuTLS: Host '%s:%d' is missing the GnuTLSPriorities directive!",
436                    s->server_hostname, s->port);
437            return HTTP_NOT_ACCEPTABLE;
438        }
439
440        /* Check if DH params have been set per host */
441        if (sc->dh_params != NULL) {
442            gnutls_certificate_set_dh_params(sc->certs, sc->dh_params);
443            gnutls_anon_set_server_dh_params(sc->anon_creds, sc->dh_params);
444        } else if (dh_params) {
445            gnutls_certificate_set_dh_params(sc->certs, dh_params);
446            gnutls_anon_set_server_dh_params(sc->anon_creds, dh_params);
447        }
448
449        /* The call after this comment is a workaround for bug in
450         * gnutls_certificate_set_retrieve_function2 that ignores
451         * supported certificate types. Should be fixed in GnuTLS
452         * 3.3.12.
453         *
454         * Details:
455         * https://lists.gnupg.org/pipermail/gnutls-devel/2015-January/007377.html
456         * Workaround from:
457         * https://github.com/vanrein/tlspool/commit/4938102d3d1b086491d147e6c8e4e2a02825fc12 */
458#if GNUTLS_VERSION_NUMBER < 0x030312
459        gnutls_certificate_set_retrieve_function(sc->certs, (void *) exit);
460#endif
461
462        gnutls_certificate_set_retrieve_function2(sc->certs, cert_retrieve_fn);
463
464        if ((sc->certs_x509_chain == NULL || sc->certs_x509_chain_num < 1) &&
465            sc->cert_pgp == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) {
466                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
467                                                "GnuTLS: Host '%s:%d' is missing a Certificate File!",
468                                                s->server_hostname, s->port);
469            return HTTP_UNAUTHORIZED;
470        }
471        if (sc->enabled == GNUTLS_ENABLED_TRUE &&
472            ((sc->certs_x509_chain_num > 0 && sc->privkey_x509 == NULL) ||
473             (sc->cert_crt_pgp != NULL && sc->privkey_pgp == NULL)))
474        {
475                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
476                                                "GnuTLS: Host '%s:%d' is missing a Private Key File!",
477                                                s->server_hostname, s->port);
478            return HTTP_UNAUTHORIZED;
479        }
480
481        if (sc->enabled == GNUTLS_ENABLED_TRUE) {
482            rv = -1;
483            if (sc->certs_x509_chain_num > 0) {
484                rv = read_crt_cn(s, pconf, sc->certs_x509_crt_chain[0], &sc->cert_cn);
485            }
486            if (rv < 0 && sc->cert_pgp != NULL) {
487                rv = read_pgpcrt_cn(s, pconf, sc->cert_crt_pgp[0], &sc->cert_cn);
488                        }
489
490            if (rv < 0) {
491                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
492                                                        "GnuTLS: Cannot find a certificate for host '%s:%d'!",
493                                                        s->server_hostname, s->port);
494                sc->cert_cn = NULL;
495                continue;
496            }
497        }
498
499        if (sc->enabled == GNUTLS_ENABLED_TRUE
500            && sc->proxy_enabled == GNUTLS_ENABLED_TRUE
501            && load_proxy_x509_credentials(pconf, ptemp, s) != APR_SUCCESS)
502        {
503            ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
504                         "%s: loading proxy credentials for host "
505                         "'%s:%d' failed, exiting!",
506                         __func__, s->server_hostname, s->port);
507            return HTTP_PROXY_AUTHENTICATION_REQUIRED;
508        }
509    }
510
511
512    ap_add_version_component(pconf, "mod_gnutls/" MOD_GNUTLS_VERSION);
513
514    {
515        const char* libvers = gnutls_check_version(NULL);
516        char* gnutls_version = NULL;
517        if(libvers && (gnutls_version = apr_psprintf(pconf, "GnuTLS/%s", libvers))) {
518            ap_add_version_component(pconf, gnutls_version);
519        } else {
520            // In case we could not create the above string go for the static version instead
521            ap_add_version_component(pconf, "GnuTLS/" GNUTLS_VERSION "-static");
522        }
523    }
524
525    return OK;
526}
527
528void mgs_hook_child_init(apr_pool_t *p, server_rec *s)
529{
530    apr_status_t rv = APR_SUCCESS;
531    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
532        ap_get_module_config(s->module_config, &gnutls_module);
533
534    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
535
536    /* if we use PKCS #11 reinitialize it */
537    if (mgs_pkcs11_reinit(s) < 0) {
538            ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
539                    "GnuTLS: Failed to reinitialize PKCS #11");
540            exit(-1);
541    }
542
543    if (sc->cache_type != mgs_cache_none) {
544        rv = mgs_cache_child_init(p, s, sc);
545        if (rv != APR_SUCCESS) {
546            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
547                    "GnuTLS: Failed to run Cache Init");
548        }
549    }
550
551    /* reinit OCSP mutex */
552    const char *lockfile = apr_global_mutex_lockfile(sc->ocsp_mutex);
553    rv = apr_global_mutex_child_init(&sc->ocsp_mutex, lockfile, p);
554    if (rv != APR_SUCCESS)
555        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
556                     "Failed to reinit mutex '" MGS_OCSP_MUTEX_NAME "'.");
557
558    /* Block SIGPIPE Signals */
559    rv = apr_signal_block(SIGPIPE);
560    if(rv != APR_SUCCESS) {
561        /* error sending output */
562        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
563                "GnuTLS: Error Blocking SIGPIPE Signal!");
564    }
565}
566
567const char *mgs_hook_http_scheme(const request_rec * r) {
568    mgs_srvconf_rec *sc;
569
570    if (r == NULL)
571        return NULL;
572
573    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
574            server->module_config,
575            &gnutls_module);
576
577    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
578    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
579        return NULL;
580    }
581
582    return "https";
583}
584
585apr_port_t mgs_hook_default_port(const request_rec * r) {
586    mgs_srvconf_rec *sc;
587
588    if (r == NULL)
589        return 0;
590
591    sc = (mgs_srvconf_rec *) ap_get_module_config(r->
592            server->module_config,
593            &gnutls_module);
594
595    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
596    if (sc->enabled == GNUTLS_ENABLED_FALSE) {
597        return 0;
598    }
599
600    return 443;
601}
602
603#define MAX_HOST_LEN 255
604
605typedef struct {
606    mgs_handle_t *ctxt;
607    mgs_srvconf_rec *sc;
608    const char *sni_name;
609} vhost_cb_rec;
610
611/**
612 * Matches the current vhost's ServerAlias directives
613 *
614 * @param x vhost callback record
615 * @param s server record
616 * @return true if a match, false otherwise
617 *
618 */
619int check_server_aliases(vhost_cb_rec *x, server_rec * s, mgs_srvconf_rec *tsc) {
620        apr_array_header_t *names;
621        int rv = 0;
622        char ** name;
623
624        /* Check ServerName First! */
625        if(apr_strnatcasecmp(x->sni_name, s->server_hostname) == 0) {
626                // We have a match, save this server configuration
627                x->sc = tsc;
628                rv = 1;
629        /* Check any ServerAlias directives */
630        } else if(s->names->nelts) {
631                names = s->names;
632                name = (char **)names->elts;
633                for (int i = 0; i < names->nelts; ++i)
634        {
635                        if (!name[i]) { continue; }
636                                if (apr_strnatcasecmp(x->sni_name, name[i]) == 0) {
637                                        // We have a match, save this server configuration
638                                        x->sc = tsc;
639                                        rv = 1;
640                        }
641                }
642        /* Wild any ServerAlias Directives */
643        } else if(s->wild_names->nelts) {
644                names = s->wild_names;
645        name = (char **)names->elts;
646                for (int i = 0; i < names->nelts; ++i)
647        {
648                        if (!name[i]) { continue; }
649                                if(apr_fnmatch(name[i], x->sni_name ,
650                                                                APR_FNM_CASE_BLIND|
651                                                                APR_FNM_PERIOD|
652                                                                APR_FNM_PATHNAME|
653                                                                APR_FNM_NOESCAPE) == APR_SUCCESS) {
654                                x->sc = tsc;
655                                rv = 1;
656                        }
657                }
658        }
659        return rv;
660}
661
662static int vhost_cb(void *baton, conn_rec *conn, server_rec * s)
663{
664    mgs_srvconf_rec *tsc;
665    vhost_cb_rec *x = baton;
666    int ret;
667
668    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
669    tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
670            &gnutls_module);
671
672    if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
673        return 0;
674    }
675
676    if (tsc->certs_x509_chain_num > 0) {
677        /* this check is there to warn administrator of any missing hostname
678         * in the certificate. */
679        ret = gnutls_x509_crt_check_hostname(tsc->certs_x509_crt_chain[0], s->server_hostname);
680        if (0 == ret)
681            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn,
682                          "GnuTLS: the certificate doesn't match requested "
683                          "hostname '%s'", s->server_hostname);
684    } else {
685        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, conn,
686                      "GnuTLS: SNI request for '%s' but no X.509 certs "
687                      "available at all",
688                      s->server_hostname);
689    }
690        return check_server_aliases(x, s, tsc);
691}
692
693mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
694{
695    int rv;
696    unsigned int sni_type;
697    size_t data_len = MAX_HOST_LEN;
698    char sni_name[MAX_HOST_LEN];
699    mgs_handle_t *ctxt;
700    vhost_cb_rec cbx;
701
702    if (session == NULL)
703        return NULL;
704
705    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
706    ctxt = gnutls_transport_get_ptr(session);
707
708    rv = gnutls_server_name_get(ctxt->session, sni_name,
709            &data_len, &sni_type, 0);
710
711    if (rv != 0) {
712        return NULL;
713    }
714
715    if (sni_type != GNUTLS_NAME_DNS) {
716        ap_log_cerror(APLOG_MARK, APLOG_CRIT, 0, ctxt->c,
717                      "GnuTLS: Unknown type '%d' for SNI: '%s'",
718                      sni_type, sni_name);
719        return NULL;
720    }
721
722    /**
723     * Code in the Core already sets up the c->base_server as the base
724     * for this IP/Port combo.  Trust that the core did the 'right' thing.
725     */
726    cbx.ctxt = ctxt;
727    cbx.sc = NULL;
728    cbx.sni_name = sni_name;
729
730    rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
731    if (rv == 1) {
732        return cbx.sc;
733    }
734    return NULL;
735}
736
737/**
738 * This function is intended as a cleanup handler for connections
739 * using GnuTLS. If attached to the connection pool, it ensures that
740 * session resources are released with the connection pool even if the
741 * session wasn't terminated properly.
742 *
743 * @param data must point to the mgs_handle_t associated with the
744 * connection
745 */
746static apr_status_t cleanup_gnutls_session(void *data)
747{
748    /* nothing to do */
749    if (data == NULL)
750        return APR_SUCCESS;
751
752    /* check if session needs closing */
753    mgs_handle_t *ctxt = (mgs_handle_t *) data;
754    if (ctxt->session != NULL)
755    {
756        ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_ECONNABORTED, ctxt->c,
757                      "%s: connection pool cleanup in progress but %sTLS "
758                      "session hasn't been terminated, trying to close",
759                      __func__, IS_PROXY_STR(ctxt));
760        int ret;
761        /* Try A Clean Shutdown */
762        do
763            ret = gnutls_bye(ctxt->session, GNUTLS_SHUT_WR);
764        while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN);
765        if (ret != GNUTLS_E_SUCCESS)
766            ap_log_cerror(APLOG_MARK, APLOG_INFO, APR_EGENERAL, ctxt->c,
767                          "%s: error while closing TLS %sconnection: %s (%d)",
768                          __func__, IS_PROXY_STR(ctxt),
769                          gnutls_strerror(ret), ret);
770        else
771            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ctxt->c,
772                          "%s: TLS %sconnection closed.",
773                          __func__, IS_PROXY_STR(ctxt));
774        /* De-Initialize Session */
775        gnutls_deinit(ctxt->session);
776        ctxt->session = NULL;
777    }
778    return APR_SUCCESS;
779}
780
781static void create_gnutls_handle(conn_rec * c)
782{
783    /* Get mod_gnutls server configuration */
784    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
785            ap_get_module_config(c->base_server->module_config, &gnutls_module);
786
787    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
788
789    /* Get connection specific configuration */
790    mgs_handle_t *ctxt = (mgs_handle_t *) ap_get_module_config(c->conn_config, &gnutls_module);
791    if (ctxt == NULL)
792    {
793        ctxt = apr_pcalloc(c->pool, sizeof (*ctxt));
794        ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
795        ctxt->is_proxy = GNUTLS_ENABLED_FALSE;
796    }
797    ctxt->enabled = GNUTLS_ENABLED_TRUE;
798    ctxt->c = c;
799    ctxt->sc = sc;
800    ctxt->status = 0;
801    ctxt->input_rc = APR_SUCCESS;
802    ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
803    ctxt->input_cbuf.length = 0;
804    ctxt->output_rc = APR_SUCCESS;
805    ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
806    ctxt->output_blen = 0;
807    ctxt->output_length = 0;
808
809    /* Initialize GnuTLS Library */
810    int err = 0;
811    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
812    {
813        /* this is an outgoing proxy connection, client mode */
814        err = gnutls_init(&ctxt->session, GNUTLS_CLIENT);
815        if (err != GNUTLS_E_SUCCESS)
816            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
817                          "gnutls_init for proxy connection failed: %s (%d)",
818                          gnutls_strerror(err), err);
819        err = gnutls_session_ticket_enable_client(ctxt->session);
820        if (err != GNUTLS_E_SUCCESS)
821            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
822                          "gnutls_session_ticket_enable_client failed: %s (%d)",
823                          gnutls_strerror(err), err);
824    }
825    else
826    {
827        /* incoming connection, server mode */
828        err = gnutls_init(&ctxt->session, GNUTLS_SERVER);
829        if (err != GNUTLS_E_SUCCESS)
830            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
831                          "gnutls_init for server side failed: %s (%d)",
832                          gnutls_strerror(err), err);
833        /* Initialize Session Tickets */
834        if (session_ticket_key.data != NULL &&
835            ctxt->sc->tickets == GNUTLS_ENABLED_TRUE)
836        {
837            err = gnutls_session_ticket_enable_server(ctxt->session, &session_ticket_key);
838            if (err != GNUTLS_E_SUCCESS)
839                ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
840                              "gnutls_session_ticket_enable_server failed: %s (%d)",
841                              gnutls_strerror(err), err);
842        }
843    }
844
845    /* Ensure TLS session resources are released when the connection
846     * pool is cleared, if the filters haven't done that already. */
847    apr_pool_pre_cleanup_register(c->pool, ctxt, cleanup_gnutls_session);
848
849    /* Set Default Priority */
850        err = gnutls_priority_set_direct(ctxt->session, "NORMAL", NULL);
851    if (err != GNUTLS_E_SUCCESS)
852        ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c, "gnutls_priority_set_direct failed!");
853    /* Set Handshake function */
854    gnutls_handshake_set_post_client_hello_function(ctxt->session,
855            mgs_select_virtual_server_cb);
856
857    /* Set GnuTLS user pointer, so we can access the module session
858     * context in GnuTLS callbacks */
859    gnutls_session_set_ptr(ctxt->session, ctxt);
860
861    /* If mod_gnutls is the TLS server, mgs_select_virtual_server_cb
862     * will load appropriate credentials during handshake. However,
863     * when handling a proxy backend connection, mod_gnutls acts as
864     * TLS client and credentials must be loaded here. */
865    if (ctxt->is_proxy == GNUTLS_ENABLED_TRUE)
866    {
867        /* Set anonymous client credentials for proxy connections */
868        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_ANON,
869                               ctxt->sc->anon_client_creds);
870        /* Set x509 credentials */
871        gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE,
872                               ctxt->sc->proxy_x509_creds);
873        /* Load priorities from the server configuration */
874        err = gnutls_priority_set(ctxt->session, ctxt->sc->proxy_priorities);
875        if (err != GNUTLS_E_SUCCESS)
876            ap_log_cerror(APLOG_MARK, APLOG_ERR, err, c,
877                          "%s: setting priorities for proxy connection "
878                          "failed: %s (%d)",
879                          __func__, gnutls_strerror(err), err);
880    }
881
882    /* Initialize Session Cache */
883    mgs_cache_session_init(ctxt);
884
885    /* Set pull, push & ptr functions */
886    gnutls_transport_set_pull_function(ctxt->session,
887            mgs_transport_read);
888    gnutls_transport_set_push_function(ctxt->session,
889            mgs_transport_write);
890    gnutls_transport_set_ptr(ctxt->session, ctxt);
891    /* Add IO filters */
892    ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME,
893            ctxt, NULL, c);
894    ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME,
895            ctxt, NULL, c);
896}
897
898int mgs_hook_pre_connection(conn_rec * c, void *csd __attribute__((unused)))
899{
900    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
901
902    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
903        ap_get_module_config(c->base_server->module_config, &gnutls_module);
904    mgs_handle_t *ctxt = (mgs_handle_t *)
905        ap_get_module_config(c->conn_config, &gnutls_module);
906
907    if ((sc && (!sc->enabled)) || (ctxt && ctxt->enabled == GNUTLS_ENABLED_FALSE))
908    {
909        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s declined connection",
910                      __func__);
911        return DECLINED;
912    }
913
914    create_gnutls_handle(c);
915    return OK;
916}
917
918int mgs_hook_fixups(request_rec * r) {
919    unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
920    const char *tmp;
921    size_t len;
922    mgs_handle_t *ctxt;
923    int rv = OK;
924
925    if (r == NULL)
926        return DECLINED;
927
928    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
929    apr_table_t *env = r->subprocess_env;
930
931    ctxt = ap_get_module_config(r->connection->conn_config,
932                                &gnutls_module);
933
934    if (!ctxt || ctxt->enabled != GNUTLS_ENABLED_TRUE || ctxt->session == NULL)
935    {
936        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "request declined in %s", __func__);
937        return DECLINED;
938    }
939
940    apr_table_setn(env, "HTTPS", "on");
941
942    apr_table_setn(env, "SSL_VERSION_LIBRARY",
943            "GnuTLS/" LIBGNUTLS_VERSION);
944    apr_table_setn(env, "SSL_VERSION_INTERFACE",
945            "mod_gnutls/" MOD_GNUTLS_VERSION);
946
947    apr_table_setn(env, "SSL_PROTOCOL",
948            gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session)));
949
950    /* should have been called SSL_CIPHERSUITE instead */
951    apr_table_setn(env, "SSL_CIPHER",
952            gnutls_cipher_suite_get_name(gnutls_kx_get(ctxt->session),
953                                         gnutls_cipher_get(ctxt->session),
954                                         gnutls_mac_get(ctxt->session)));
955
956    apr_table_setn(env, "SSL_COMPRESS_METHOD",
957            gnutls_compression_get_name(gnutls_compression_get(ctxt->session)));
958
959#ifdef ENABLE_SRP
960    if (ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
961        tmp = gnutls_srp_server_get_username(ctxt->session);
962        apr_table_setn(env, "SSL_SRP_USER", (tmp != NULL) ? tmp : "");
963    } else {
964        apr_table_unset(env, "SSL_SRP_USER");
965    }
966#endif
967
968    if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
969        apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
970
971    unsigned int key_size = 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
972    tmp = apr_psprintf(r->pool, "%u", key_size);
973
974    apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
975
976    apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
977
978    apr_table_setn(env, "SSL_CIPHER_EXPORT",
979            (key_size <= 40) ? "true" : "false");
980
981    int dhsize = gnutls_dh_get_prime_bits(ctxt->session);
982    if (dhsize > 0) {
983        tmp = apr_psprintf(r->pool, "%d", dhsize);
984        apr_table_setn(env, "SSL_DH_PRIME_BITS", tmp);
985    }
986
987    len = sizeof (sbuf);
988    gnutls_session_get_id(ctxt->session, sbuf, &len);
989    apr_table_setn(env, "SSL_SESSION_ID",
990                   apr_pescape_hex(r->pool, sbuf, len, 0));
991
992    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
993        mgs_add_common_cert_vars(r, ctxt->sc->certs_x509_crt_chain[0], 0, ctxt->sc->export_certificates_size);
994    } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
995        mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_crt_pgp[0], 0, ctxt->sc->export_certificates_size);
996    }
997
998    return rv;
999}
1000
1001int mgs_hook_authz(request_rec * r) {
1002    int rv;
1003    mgs_handle_t *ctxt;
1004    mgs_dirconf_rec *dc;
1005
1006    if (r == NULL)
1007        return DECLINED;
1008
1009    dc = ap_get_module_config(r->per_dir_config, &gnutls_module);
1010
1011    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1012    ctxt =
1013            ap_get_module_config(r->connection->conn_config,
1014            &gnutls_module);
1015
1016    if (!ctxt || ctxt->session == NULL) {
1017        return DECLINED;
1018    }
1019
1020    if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
1021        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1022                "GnuTLS: Directory set to Ignore Client Certificate!");
1023    } else {
1024        if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
1025            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1026                    "GnuTLS: Attempting to rehandshake with peer. %d %d",
1027                    ctxt->sc->client_verify_mode,
1028                    dc->client_verify_mode);
1029
1030            /* If we already have a client certificate, there's no point in
1031             * re-handshaking... */
1032            rv = mgs_cert_verify(r, ctxt);
1033            if (rv != DECLINED && rv != HTTP_FORBIDDEN)
1034                return rv;
1035
1036            gnutls_certificate_server_set_request
1037                    (ctxt->session, dc->client_verify_mode);
1038
1039            if (mgs_rehandshake(ctxt) != 0) {
1040                return HTTP_FORBIDDEN;
1041            }
1042        } else if (ctxt->sc->client_verify_mode ==
1043                GNUTLS_CERT_IGNORE) {
1044#if MOD_GNUTLS_DEBUG
1045            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1046                    "GnuTLS: Peer is set to IGNORE");
1047#endif
1048            return DECLINED;
1049        }
1050        rv = mgs_cert_verify(r, ctxt);
1051        if (rv != DECLINED
1052            && (rv != HTTP_FORBIDDEN
1053                || dc->client_verify_mode == GNUTLS_CERT_REQUIRE
1054                || (dc->client_verify_mode == -1
1055                    && ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUIRE)))
1056        {
1057            return rv;
1058        }
1059    }
1060
1061    return DECLINED;
1062}
1063
1064/* variables that are not sent by default:
1065 *
1066 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
1067 * SSL_SERVER_CERT      string  PEM-encoded client certificate
1068 */
1069
1070/* @param side is either 0 for SERVER or 1 for CLIENT
1071 *
1072 * @param export_cert_size (int) maximum size for environment variable
1073 * to use for the PEM-encoded certificate (0 means do not export)
1074 */
1075#define MGS_SIDE(suffix) ((side==0) ? "SSL_SERVER" suffix : "SSL_CLIENT" suffix)
1076
1077static void mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side, size_t export_cert_size) {
1078    unsigned char sbuf[64]; /* buffer to hold serials */
1079    char buf[AP_IOBUFSIZE];
1080    const char *tmp;
1081    char *tmp2;
1082    size_t len;
1083    int ret;
1084
1085    if (r == NULL)
1086        return;
1087
1088    apr_table_t *env = r->subprocess_env;
1089
1090    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1091    if (export_cert_size > 0) {
1092        len = 0;
1093        ret = gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, NULL, &len);
1094        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1095            if (len >= export_cert_size) {
1096                apr_table_setn(env, MGS_SIDE("_CERT"), "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1097                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1098                              "GnuTLS: Failed to export too-large X.509 certificate to environment");
1099            } else {
1100                char* cert_buf = apr_palloc(r->pool, len + 1);
1101                if (cert_buf != NULL && gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0) {
1102                    cert_buf[len] = 0;
1103                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
1104                } else {
1105                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1106                                  "GnuTLS: failed to export X.509 certificate");
1107                }
1108            }
1109        } else {
1110            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1111                          "GnuTLS: dazed and confused about X.509 certificate size");
1112        }
1113    }
1114
1115    len = sizeof (buf);
1116    gnutls_x509_crt_get_dn(cert, buf, &len);
1117    apr_table_setn(env, MGS_SIDE("_S_DN"), apr_pstrmemdup(r->pool, buf, len));
1118
1119    len = sizeof (buf);
1120    gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
1121    apr_table_setn(env, MGS_SIDE("_I_DN"), apr_pstrmemdup(r->pool, buf, len));
1122
1123    len = sizeof (sbuf);
1124    gnutls_x509_crt_get_serial(cert, sbuf, &len);
1125    apr_table_setn(env, MGS_SIDE("_M_SERIAL"),
1126                   apr_pescape_hex(r->pool, sbuf, len, 0));
1127
1128    ret = gnutls_x509_crt_get_version(cert);
1129    if (ret > 0)
1130        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1131                       apr_psprintf(r->pool, "%u", ret));
1132
1133    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "X.509");
1134
1135    tmp =
1136            mgs_time2sz(gnutls_x509_crt_get_expiration_time
1137            (cert), buf, sizeof (buf));
1138    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
1139
1140    tmp =
1141            mgs_time2sz(gnutls_x509_crt_get_activation_time
1142            (cert), buf, sizeof (buf));
1143    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
1144
1145    ret = gnutls_x509_crt_get_signature_algorithm(cert);
1146    if (ret >= 0) {
1147        apr_table_setn(env, MGS_SIDE("_A_SIG"),
1148                gnutls_sign_algorithm_get_name(ret));
1149    }
1150
1151    ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
1152    if (ret >= 0) {
1153        apr_table_setn(env, MGS_SIDE("_A_KEY"),
1154                gnutls_pk_algorithm_get_name(ret));
1155    }
1156
1157    /* export all the alternative names (DNS, RFC822 and URI) */
1158    for (int i = 0; !(ret < 0); i++)
1159    {
1160        const char *san, *sanlabel;
1161        len = 0;
1162        ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
1163                NULL, &len,
1164                NULL);
1165
1166        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) {
1167            tmp2 = apr_palloc(r->pool, len + 1);
1168
1169            ret =
1170                    gnutls_x509_crt_get_subject_alt_name(cert, i,
1171                    tmp2,
1172                    &len,
1173                    NULL);
1174            tmp2[len] = 0;
1175
1176            sanlabel = apr_psprintf(r->pool, "%s%u", MGS_SIDE("_S_AN"), i);
1177            if (ret == GNUTLS_SAN_DNSNAME) {
1178                san = apr_psprintf(r->pool, "DNSNAME:%s", tmp2);
1179            } else if (ret == GNUTLS_SAN_RFC822NAME) {
1180                san = apr_psprintf(r->pool, "RFC822NAME:%s", tmp2);
1181            } else if (ret == GNUTLS_SAN_URI) {
1182                san = apr_psprintf(r->pool, "URI:%s", tmp2);
1183            } else {
1184                san = "UNSUPPORTED";
1185            }
1186            apr_table_setn(env, sanlabel, san);
1187        }
1188    }
1189}
1190
1191
1192/* @param side 0: server, 1: client
1193 *
1194 * @param export_cert_size (int) maximum size for environment variable
1195 * to use for the PEM-encoded certificate (0 means do not export)
1196 */
1197static void mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert, int side, size_t export_cert_size) {
1198
1199        unsigned char sbuf[64]; /* buffer to hold serials */
1200    char buf[AP_IOBUFSIZE];
1201    const char *tmp;
1202    size_t len;
1203    int ret;
1204
1205    if (r == NULL)
1206        return;
1207
1208    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1209    apr_table_t *env = r->subprocess_env;
1210
1211    if (export_cert_size > 0) {
1212        len = 0;
1213        ret = gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, NULL, &len);
1214        if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) {
1215            if (len >= export_cert_size) {
1216                apr_table_setn(env, MGS_SIDE("_CERT"),
1217                               "GNUTLS_CERTIFICATE_SIZE_LIMIT_EXCEEDED");
1218                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1219                              "GnuTLS: Failed to export too-large OpenPGP certificate to environment");
1220            } else {
1221                char* cert_buf = apr_palloc(r->pool, len + 1);
1222                if (cert_buf != NULL && gnutls_openpgp_crt_export(cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0) {
1223                    cert_buf[len] = 0;
1224                    apr_table_setn(env, MGS_SIDE("_CERT"), cert_buf);
1225                } else {
1226                    ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1227                                  "GnuTLS: failed to export OpenPGP certificate");
1228                }
1229            }
1230        } else {
1231            ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
1232                          "GnuTLS: dazed and confused about OpenPGP certificate size");
1233        }
1234    }
1235
1236    len = sizeof (buf);
1237    gnutls_openpgp_crt_get_name(cert, 0, buf, &len);
1238    apr_table_setn(env, MGS_SIDE("_NAME"), apr_pstrmemdup(r->pool, buf, len));
1239
1240    len = sizeof (sbuf);
1241    gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len);
1242    apr_table_setn(env, MGS_SIDE("_FINGERPRINT"),
1243                   apr_pescape_hex(r->pool, sbuf, len, 0));
1244
1245    ret = gnutls_openpgp_crt_get_version(cert);
1246    if (ret > 0)
1247        apr_table_setn(env, MGS_SIDE("_M_VERSION"),
1248                       apr_psprintf(r->pool, "%u", ret));
1249
1250    apr_table_setn(env, MGS_SIDE("_CERT_TYPE"), "OPENPGP");
1251
1252    tmp =
1253            mgs_time2sz(gnutls_openpgp_crt_get_expiration_time
1254            (cert), buf, sizeof (buf));
1255    apr_table_setn(env, MGS_SIDE("_V_END"), apr_pstrdup(r->pool, tmp));
1256
1257    tmp =
1258            mgs_time2sz(gnutls_openpgp_crt_get_creation_time
1259            (cert), buf, sizeof (buf));
1260    apr_table_setn(env, MGS_SIDE("_V_START"), apr_pstrdup(r->pool, tmp));
1261
1262    ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL);
1263    if (ret >= 0) {
1264        apr_table_setn(env, MGS_SIDE("_A_KEY"), gnutls_pk_algorithm_get_name(ret));
1265    }
1266
1267}
1268
1269/* TODO: Allow client sending a X.509 certificate chain */
1270static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt) {
1271    const gnutls_datum_t *cert_list;
1272    unsigned int cert_list_size;
1273    /* assume the certificate is invalid unless explicitly set
1274     * otherwise */
1275    unsigned int status = GNUTLS_CERT_INVALID;
1276    int rv = GNUTLS_E_NO_CERTIFICATE_FOUND, ret;
1277    unsigned int ch_size = 0;
1278
1279    union {
1280        gnutls_x509_crt_t x509[MAX_CHAIN_SIZE];
1281        gnutls_openpgp_crt_t pgp;
1282    } cert;
1283    apr_time_t expiration_time, cur_time;
1284
1285    if (r == NULL || ctxt == NULL || ctxt->session == NULL)
1286        return HTTP_FORBIDDEN;
1287
1288    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1289    cert_list =
1290            gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1291
1292    if (cert_list == NULL || cert_list_size == 0) {
1293        /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
1294         */
1295        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1296            return OK;
1297
1298        /* no certificate provided by the client, but one was required. */
1299        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1300                "GnuTLS: Failed to Verify Peer: "
1301                "Client did not submit a certificate");
1302        return HTTP_FORBIDDEN;
1303    }
1304
1305    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1306        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1307                "GnuTLS: A Chain of %d certificate(s) was provided for validation",
1308                cert_list_size);
1309
1310        for (ch_size = 0; ch_size < cert_list_size; ch_size++) {
1311            gnutls_x509_crt_init(&cert.x509[ch_size]);
1312            rv = gnutls_x509_crt_import(cert.x509[ch_size],
1313                    &cert_list[ch_size],
1314                    GNUTLS_X509_FMT_DER);
1315            // When failure to import, leave the loop
1316            if (rv != GNUTLS_E_SUCCESS) {
1317                if (ch_size < 1) {
1318                    ap_log_rerror(APLOG_MARK,
1319                            APLOG_INFO, 0, r,
1320                            "GnuTLS: Failed to Verify Peer: "
1321                            "Failed to import peer certificates.");
1322                    ret = HTTP_FORBIDDEN;
1323                    goto exit;
1324                }
1325                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1326                        "GnuTLS: Failed to import some peer certificates. Using %d certificates",
1327                        ch_size);
1328                rv = GNUTLS_E_SUCCESS;
1329                break;
1330            }
1331        }
1332    } else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP) {
1333        if (cert_list_size > 1) {
1334            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1335                    "GnuTLS: Failed to Verify Peer: "
1336                    "Chained Client Certificates are not supported.");
1337            return HTTP_FORBIDDEN;
1338        }
1339
1340        gnutls_openpgp_crt_init(&cert.pgp);
1341        rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0],
1342                GNUTLS_OPENPGP_FMT_RAW);
1343
1344    } else
1345        return HTTP_FORBIDDEN;
1346
1347    if (rv < 0) {
1348        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1349                "GnuTLS: Failed to Verify Peer: "
1350                "Failed to import peer certificates.");
1351        ret = HTTP_FORBIDDEN;
1352        goto exit;
1353    }
1354
1355    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1356        apr_time_ansi_put(&expiration_time,
1357                gnutls_x509_crt_get_expiration_time
1358                (cert.x509[0]));
1359
1360        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1361                      "GnuTLS: Verifying list of %d certificate(s) via method '%s'",
1362                      ch_size, mgs_readable_cvm(ctxt->sc->client_verify_method));
1363        switch(ctxt->sc->client_verify_method) {
1364        case mgs_cvm_cartel:
1365            rv = gnutls_x509_crt_list_verify(cert.x509, ch_size,
1366                                             ctxt->sc->ca_list,
1367                                             ctxt->sc->ca_list_size,
1368                                             NULL, 0, 0, &status);
1369            break;
1370#ifdef ENABLE_MSVA
1371        case mgs_cvm_msva:
1372        {
1373            struct msv_response* resp = NULL;
1374            struct msv_query q = { .context="https", .peertype="client", .pkctype="x509pem" };
1375            msv_ctxt_t ctx = msv_ctxt_init(NULL);
1376            char cert_pem_buf[10 * 1024];
1377            size_t len = sizeof (cert_pem_buf);
1378
1379            rv = 0;
1380            if (gnutls_x509_crt_export(cert.x509[0], GNUTLS_X509_FMT_PEM, cert_pem_buf, &len) >= 0) {
1381                /* FIXME : put together a name from the cert we received, instead of hard-coding this value: */
1382                q.peername = mgs_x509_construct_uid(r, cert.x509[0]);
1383                q.pkcdata = cert_pem_buf;
1384                rv = msv_query_agent(ctx, q, &resp);
1385                if (rv == LIBMSV_ERROR_SUCCESS) {
1386                    status = 0;
1387                } else if (rv == LIBMSV_ERROR_INVALID) {
1388                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1389                                  "GnuTLS: Monkeysphere validation failed: (message: %s)", resp->message);
1390                    status = GNUTLS_CERT_INVALID;
1391                } else {
1392                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1393                                  "GnuTLS: Error communicating with the Monkeysphere Validation Agent: (%d) %s", rv, msv_strerror(ctx, rv));
1394                    status = GNUTLS_CERT_INVALID;
1395                    rv = -1;
1396                }
1397            } else {
1398                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1399                              "GnuTLS: Could not convert the client certificate to PEM format");
1400                status = GNUTLS_CERT_INVALID;
1401                rv = GNUTLS_E_ASN1_ELEMENT_NOT_FOUND;
1402            }
1403            msv_response_destroy(resp);
1404            msv_ctxt_destroy(ctx);
1405        }
1406            break;
1407#endif
1408        default:
1409            /* If this block is reached, that indicates a
1410             * configuration error or bug in mod_gnutls (invalid value
1411             * of ctxt->sc->client_verify_method). */
1412            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1413                          "GnuTLS: Failed to Verify X.509 Peer: method '%s' is not supported",
1414                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1415            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1416        }
1417
1418    } else {
1419        apr_time_ansi_put(&expiration_time,
1420                gnutls_openpgp_crt_get_expiration_time
1421                (cert.pgp));
1422
1423        switch(ctxt->sc->client_verify_method) {
1424        case mgs_cvm_cartel:
1425            rv = gnutls_openpgp_crt_verify_ring(cert.pgp,
1426                                                ctxt->sc->pgp_list, 0,
1427                                                &status);
1428            break;
1429#ifdef ENABLE_MSVA
1430        case mgs_cvm_msva:
1431            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1432                          "GnuTLS:  OpenPGP verification via MSVA is not yet implemented");
1433            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1434            break;
1435#endif
1436        default:
1437            /* If this block is reached, that indicates a
1438             * configuration error or bug in mod_gnutls (invalid value
1439             * of ctxt->sc->client_verify_method). */
1440            ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1441                          "GnuTLS: Failed to Verify OpenPGP Peer: method '%s' is not supported",
1442                          mgs_readable_cvm(ctxt->sc->client_verify_method));
1443            rv = GNUTLS_E_UNIMPLEMENTED_FEATURE;
1444        }
1445    }
1446
1447    /* "goto exit" at the end of this block skips evaluation of the
1448     * "status" variable */
1449    if (rv < 0) {
1450        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1451                "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
1452                rv, gnutls_strerror(rv));
1453        if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1454            ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1455                "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?");
1456        ret = HTTP_FORBIDDEN;
1457        goto exit;
1458    }
1459
1460    /* TODO: X509 CRL Verification. */
1461    /* May add later if anyone needs it.
1462     */
1463    /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1464
1465    cur_time = apr_time_now();
1466
1467    if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1468        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1469                "GnuTLS: Could not find Signer for Peer Certificate");
1470    }
1471
1472    if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
1473        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1474                "GnuTLS: Peer's Certificate signer is not a CA");
1475    }
1476
1477    if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
1478        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1479                "GnuTLS: Peer's Certificate is using insecure algorithms");
1480    }
1481
1482    if (status & GNUTLS_CERT_EXPIRED
1483            || status & GNUTLS_CERT_NOT_ACTIVATED) {
1484        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1485                "GnuTLS: Peer's Certificate signer is expired or not yet activated");
1486    }
1487
1488    if (status & GNUTLS_CERT_INVALID) {
1489        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1490                "GnuTLS: Peer Certificate is invalid.");
1491    } else if (status & GNUTLS_CERT_REVOKED) {
1492        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1493                "GnuTLS: Peer Certificate is revoked.");
1494    }
1495
1496    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
1497        mgs_add_common_cert_vars(r, cert.x509[0], 1, ctxt->sc->export_certificates_size);
1498    else if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_OPENPGP)
1499        mgs_add_common_pgpcert_vars(r, cert.pgp, 1, ctxt->sc->export_certificates_size);
1500
1501    {
1502        /* days remaining */
1503        unsigned long remain =
1504                (apr_time_sec(expiration_time) -
1505                apr_time_sec(cur_time)) / 86400;
1506        apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
1507                apr_psprintf(r->pool, "%lu", remain));
1508    }
1509
1510    if (status == 0) {
1511        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1512                "SUCCESS");
1513        ret = OK;
1514    } else {
1515        apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1516                "FAILED");
1517        if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1518            ret = OK;
1519        else
1520            ret = HTTP_FORBIDDEN;
1521    }
1522
1523exit:
1524    if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
1525        for (unsigned int i = 0; i < ch_size; i++)
1526            gnutls_x509_crt_deinit(cert.x509[i]);
1527    else if (gnutls_certificate_type_get(ctxt->session) ==
1528             GNUTLS_CRT_OPENPGP)
1529        gnutls_openpgp_crt_deinit(cert.pgp);
1530    return ret;
1531}
1532
1533
1534
1535#ifdef ENABLE_MSVA
1536/* this section of code is used only when trying to talk to the MSVA */
1537static const char* mgs_x509_leaf_oid_from_dn(apr_pool_t *pool, const char* oid, gnutls_x509_crt_t cert) {
1538    int rv=GNUTLS_E_SUCCESS, i;
1539    size_t sz=0, lastsz=0;
1540    char* data=NULL;
1541
1542    i = -1;
1543    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1544        i++;
1545        lastsz=sz;
1546        sz=0;
1547        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i, 0, NULL, &sz);
1548    }
1549    if (i > 0) {
1550        data = apr_palloc(pool, lastsz);
1551        sz=lastsz;
1552        rv = gnutls_x509_crt_get_dn_by_oid (cert, oid, i-1, 0, data, &sz);
1553        if (rv == GNUTLS_E_SUCCESS)
1554            return data;
1555    }
1556    return NULL;
1557}
1558
1559static const char* mgs_x509_first_type_from_san(apr_pool_t *pool, gnutls_x509_subject_alt_name_t target, gnutls_x509_crt_t cert) {
1560    int rv=GNUTLS_E_SUCCESS;
1561    size_t sz;
1562    char* data=NULL;
1563    unsigned int i;
1564    gnutls_x509_subject_alt_name_t thistype;
1565
1566    i = 0;
1567    while(rv != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
1568        sz = 0;
1569        rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, NULL, &sz, &thistype, NULL);
1570        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && thistype == target) {
1571            data = apr_palloc(pool, sz);
1572            rv = gnutls_x509_crt_get_subject_alt_name2(cert, i, data, &sz, &thistype, NULL);
1573            if (rv >=0 && (thistype == target))
1574                return data;
1575        }
1576        i++;
1577    }
1578    return NULL;
1579}
1580
1581
1582/* Create a string representing a candidate User ID from an X.509
1583 * certificate
1584
1585 * We need this for client certification because a client gives us a
1586 * certificate, but doesn't tell us (in any other way) who they are
1587 * trying to authenticate as.
1588
1589 * TODO: we might need another parallel for OpenPGP, but for that it's
1590 * much simpler: we can just assume that the first User ID marked as
1591 * "primary" (or the first User ID, period) is the identity the user
1592 * is trying to present as.
1593
1594 * one complaint might be "but the user wanted to be another identity,
1595 * which is also in the certificate (e.g. in a SubjectAltName)"
1596 * However, given that any user can regenerate their own X.509
1597 * certificate with their own public key content, they should just do
1598 * so, and not expect us to guess at their identity :)
1599
1600 * This function allocates it's response from the pool given it.  When
1601 * that pool is reclaimed, the response will also be deallocated.
1602
1603 * FIXME: what about extracting a server-style cert
1604 *        (e.g. https://imposter.example) from the DN or any sAN?
1605
1606 * FIXME: what if we want to call this outside the context of a
1607 *        request?  That complicates the logging.
1608 */
1609static const char* mgs_x509_construct_uid(request_rec *r, gnutls_x509_crt_t cert) {
1610    /* basic strategy, assuming humans are the users: we are going to
1611     * try to reconstruct a "conventional" User ID by pulling in a
1612     * name, comment, and e-mail address.
1613     */
1614    apr_pool_t *pool = r->pool;
1615    const char *name=NULL, *comment=NULL, *email=NULL;
1616    const char *ret=NULL;
1617    /* subpool for temporary allocation: */
1618    apr_pool_t *sp=NULL;
1619
1620    if (APR_SUCCESS != apr_pool_create(&sp, pool))
1621        return NULL; /* i'm assuming that libapr would log this kind
1622                      * of error on its own */
1623
1624     /* Name
1625
1626     the name comes from the leaf commonName of the cert's Subject.
1627
1628     (MAYBE: should we look at trying to assemble a candidate from
1629             givenName, surName, suffix, etc?  the "name" field
1630             appears to be case-insensitive, which seems problematic
1631             from what we expect; see:
1632             http://www.itu.int/rec/T-REC-X.520-200102-s/e )
1633
1634     (MAYBE: should we try pulling a commonName or otherName or
1635             something from subjectAltName? see:
1636             https://tools.ietf.org/html/rfc5280#section-4.2.1.6
1637             GnuTLS does not support looking for Common Names in the
1638             SAN yet)
1639     */
1640    name = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_COMMON_NAME, cert);
1641
1642    /* Comment
1643
1644       I am inclined to punt on this for now, as Comment has been so
1645       atrociously misused in OpenPGP.  Perhaps if there is a
1646       pseudonym (OID 2.5.4.65, aka GNUTLS_OID_X520_PSEUDONYM) field
1647       in the subject or sAN?
1648    */
1649    comment = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_X520_PSEUDONYM, cert);
1650
1651    /* E-mail
1652
1653       This should be the the first rfc822Name from the sAN.
1654
1655       failing that, we'll take the leaf email in the certificate's
1656       subject; this is a deprecated use though.
1657     */
1658    email = mgs_x509_first_type_from_san(sp, GNUTLS_SAN_RFC822NAME, cert);
1659    if (email == NULL)
1660        email = mgs_x509_leaf_oid_from_dn(sp, GNUTLS_OID_PKCS9_EMAIL, cert);
1661
1662    /* assemble all the parts: */
1663
1664    /* must have at least a name or an e-mail. */
1665    if (name == NULL && email == NULL) {
1666        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1667                "GnuTLS: Need either a name or an e-mail address to get a User ID from an X.509 certificate.");
1668        goto end;
1669    }
1670    if (name) {
1671        if (comment) {
1672            if (email) {
1673                ret = apr_psprintf(pool, "%s (%s) <%s>", name, comment, email);
1674            } else {
1675                ret = apr_psprintf(pool, "%s (%s)", name, comment);
1676            }
1677        } else {
1678            if (email) {
1679                ret = apr_psprintf(pool, "%s <%s>", name, email);
1680            } else {
1681                ret = apr_pstrdup(pool, name);
1682            }
1683        }
1684    } else {
1685        if (comment) {
1686            ret = apr_psprintf(pool, "(%s) <%s>", comment, email);
1687        } else {
1688            ret = apr_psprintf(pool, "<%s>", email);
1689        }
1690    }
1691
1692end:
1693    apr_pool_destroy(sp);
1694    return ret;
1695}
1696#endif /* ENABLE_MSVA */
1697
1698
1699
1700/*
1701 * This hook writes the mod_gnutls status message for a mod_status
1702 * report. According to the comments in mod_status.h, the "flags"
1703 * parameter is a bitwise OR of the AP_STATUS_ flags.
1704 *
1705 * Note that this implementation gives flags explicitly requesting a
1706 * simple response priority, e.g. if AP_STATUS_SHORT is set, flags
1707 * requesting an HTML report will be ignored. As of Apache 2.4.10, the
1708 * following flags were defined in mod_status.h:
1709 *
1710 * AP_STATUS_SHORT (short, non-HTML report requested)
1711 * AP_STATUS_NOTABLE (HTML report without tables)
1712 * AP_STATUS_EXTENDED (detailed report)
1713 */
1714static int mgs_status_hook(request_rec *r, int flags)
1715{
1716    if (r == NULL)
1717        return OK;
1718
1719    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1720        ap_get_module_config(r->server->module_config, &gnutls_module);
1721
1722    _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1723
1724    if (flags & AP_STATUS_SHORT)
1725    {
1726        ap_rprintf(r, "Using GnuTLS version: %s\n", gnutls_check_version(NULL));
1727        ap_rputs("Built against GnuTLS version: " GNUTLS_VERSION "\n", r);
1728    }
1729    else
1730    {
1731        ap_rputs("<hr>\n", r);
1732        ap_rputs("<h2>GnuTLS Information:</h2>\n<dl>\n", r);
1733
1734        ap_rprintf(r, "<dt>Using GnuTLS version:</dt><dd>%s</dd>\n",
1735                   gnutls_check_version(NULL));
1736        ap_rputs("<dt>Built against GnuTLS version:</dt><dd>"
1737                 GNUTLS_VERSION "</dd>\n", r);
1738        ap_rprintf(r, "<dt>Using TLS:</dt><dd>%s</dd>\n",
1739                   (sc->enabled == GNUTLS_ENABLED_FALSE ? "no" : "yes"));
1740    }
1741
1742    if (sc->enabled != GNUTLS_ENABLED_FALSE)
1743    {
1744        mgs_handle_t* ctxt =
1745            ap_get_module_config(r->connection->conn_config, &gnutls_module);
1746        if (ctxt && ctxt->session != NULL)
1747        {
1748            char* s_info = gnutls_session_get_desc(ctxt->session);
1749            if (s_info)
1750            {
1751                if (flags & AP_STATUS_SHORT)
1752                    ap_rprintf(r, "Current TLS session: %s\n", s_info);
1753                else
1754                    ap_rprintf(r, "<dt>Current TLS session:</dt><dd>%s</dd>\n",
1755                               s_info);
1756                gnutls_free(s_info);
1757            }
1758        }
1759    }
1760
1761    if (!(flags & AP_STATUS_SHORT))
1762        ap_rputs("</dl>\n", r);
1763
1764    return OK;
1765}
1766
1767
1768
1769/*
1770 * Callback to check the server certificate for proxy HTTPS
1771 * connections, to be used with
1772 * gnutls_certificate_set_verify_function.
1773
1774 * Returns: 0 if certificate check was successful (certificate
1775 * trusted), non-zero otherwise (error during check or untrusted
1776 * certificate).
1777 */
1778static int gtls_check_server_cert(gnutls_session_t session)
1779{
1780    mgs_handle_t *ctxt = (mgs_handle_t *) gnutls_session_get_ptr(session);
1781    unsigned int status;
1782
1783    /* Get peer hostname from a note left by mod_proxy */
1784    const char *peer_hostname =
1785        apr_table_get(ctxt->c->notes, "proxy-request-hostname");
1786    if (peer_hostname == NULL)
1787        ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c,
1788                      "%s: proxy-request-hostname is NULL, cannot check "
1789                      "peer's hostname", __func__);
1790
1791    /* Verify certificate, including hostname match. Should
1792     * peer_hostname be NULL for some reason, the name is not
1793     * checked. */
1794    int err = gnutls_certificate_verify_peers3(session, peer_hostname,
1795                                               &status);
1796    if (err != GNUTLS_E_SUCCESS)
1797    {
1798        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, ctxt->c,
1799                      "%s: server certificate check failed: %s (%d)",
1800                      __func__, gnutls_strerror(err), err);
1801        return err;
1802    }
1803
1804    if (status == 0)
1805        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctxt->c,
1806                      "%s: server certificate is trusted.",
1807                      __func__);
1808    else
1809    {
1810        gnutls_datum_t out;
1811        /* GNUTLS_CRT_X509: ATM, only X509 is supported for proxy
1812         * certs 0: according to function API, the last argument
1813         * should be 0 */
1814        err = gnutls_certificate_verification_status_print(status,
1815                                                           GNUTLS_CRT_X509,
1816                                                           &out, 0);
1817        if (err != GNUTLS_E_SUCCESS)
1818            ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, ctxt->c,
1819                          "%s: server verify print failed: %s (%d)",
1820                          __func__, gnutls_strerror(err), err);
1821        else
1822            ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, ctxt->c,
1823                          "%s: %s",
1824                          __func__, out.data);
1825        gnutls_free(out.data);
1826    }
1827
1828    return status;
1829}
1830
1831
1832
1833static apr_status_t cleanup_proxy_x509_credentials(void *arg)
1834{
1835    mgs_srvconf_rec *sc = (mgs_srvconf_rec *) arg;
1836
1837    if (sc->proxy_x509_creds)
1838    {
1839        /* This implicitly releases the associated trust list
1840         * sc->proxy_x509_tl, too. */
1841        gnutls_certificate_free_credentials(sc->proxy_x509_creds);
1842        sc->proxy_x509_creds = NULL;
1843        sc->proxy_x509_tl = NULL;
1844    }
1845
1846    if (sc->anon_client_creds)
1847    {
1848        gnutls_anon_free_client_credentials(sc->anon_client_creds);
1849        sc->anon_client_creds = NULL;
1850    }
1851
1852    if (sc->proxy_priorities)
1853    {
1854        gnutls_priority_deinit(sc->proxy_priorities);
1855        sc->proxy_priorities = NULL;
1856    }
1857
1858    return APR_SUCCESS;
1859}
1860
1861
1862
1863static apr_status_t load_proxy_x509_credentials(apr_pool_t *pconf,
1864                                                apr_pool_t *ptemp,
1865                                                server_rec *s)
1866{
1867    mgs_srvconf_rec *sc = (mgs_srvconf_rec *)
1868        ap_get_module_config(s->module_config, &gnutls_module);
1869
1870    if (sc == NULL)
1871        return APR_EGENERAL;
1872
1873    apr_status_t ret = APR_EINIT;
1874    int err = GNUTLS_E_SUCCESS;
1875
1876    /* Cleanup function for the GnuTLS structures allocated below */
1877    apr_pool_cleanup_register(pconf, sc, cleanup_proxy_x509_credentials,
1878                              apr_pool_cleanup_null);
1879
1880    /* Function pool, gets destroyed before exit. */
1881    apr_pool_t *pool;
1882    ret = apr_pool_create(&pool, ptemp);
1883    if (ret != APR_SUCCESS)
1884    {
1885        ap_log_error(APLOG_MARK, APLOG_ERR, ret, s,
1886                     "%s: failed to allocate function memory pool.", __func__);
1887        return ret;
1888    }
1889
1890    /* allocate credentials structures */
1891    err = gnutls_certificate_allocate_credentials(&sc->proxy_x509_creds);
1892    if (err != GNUTLS_E_SUCCESS)
1893    {
1894        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1895                     "%s: Failed to initialize proxy credentials: (%d) %s",
1896                     __func__, err, gnutls_strerror(err));
1897        return APR_EGENERAL;
1898    }
1899    err = gnutls_anon_allocate_client_credentials(&sc->anon_client_creds);
1900    if (err != GNUTLS_E_SUCCESS)
1901    {
1902        ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1903                     "%s: Failed to initialize anon credentials for proxy: "
1904                     "(%d) %s", __func__, err, gnutls_strerror(err));
1905        return APR_EGENERAL;
1906    }
1907
1908    /* Check if the proxy priorities have been set, fail immediately
1909     * if not */
1910    if (sc->proxy_priorities_str == NULL)
1911    {
1912        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
1913                     "Host '%s:%d' is missing the GnuTLSProxyPriorities "
1914                     "directive!",
1915                     s->server_hostname, s->port);
1916        return APR_EGENERAL;
1917    }
1918    /* parse proxy priorities */
1919    const char *err_pos = NULL;
1920    err = gnutls_priority_init(&sc->proxy_priorities,
1921                               sc->proxy_priorities_str, &err_pos);
1922    if (err != GNUTLS_E_SUCCESS)
1923    {
1924        if (ret == GNUTLS_E_INVALID_REQUEST)
1925            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1926                         "%s: Syntax error parsing proxy priorities "
1927                         "string at: %s",
1928                         __func__, err_pos);
1929        else
1930            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1931                         "Error setting proxy priorities: %s (%d)",
1932                         gnutls_strerror(err), err);
1933        ret = APR_EGENERAL;
1934    }
1935
1936    /* load certificate and key for client auth, if configured */
1937    if (sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
1938    {
1939        char* cert_file = ap_server_root_relative(pool,
1940                                                  sc->proxy_x509_cert_file);
1941        char* key_file = ap_server_root_relative(pool,
1942                                                 sc->proxy_x509_key_file);
1943        err = gnutls_certificate_set_x509_key_file(sc->proxy_x509_creds,
1944                                                   cert_file,
1945                                                   key_file,
1946                                                   GNUTLS_X509_FMT_PEM);
1947        if (err != GNUTLS_E_SUCCESS)
1948        {
1949            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1950                         "%s: loading proxy client credentials failed: %s (%d)",
1951                         __func__, gnutls_strerror(err), err);
1952            ret = APR_EGENERAL;
1953        }
1954    }
1955    else if (!sc->proxy_x509_key_file && sc->proxy_x509_cert_file)
1956    {
1957        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1958                     "%s: proxy key file not set!", __func__);
1959        ret = APR_EGENERAL;
1960    }
1961    else if (!sc->proxy_x509_cert_file && sc->proxy_x509_key_file)
1962    {
1963        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
1964                     "%s: proxy certificate file not set!", __func__);
1965        ret = APR_EGENERAL;
1966    }
1967    else
1968        /* if both key and cert are NULL, client auth is not used */
1969        ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
1970                     "%s: no client credentials for proxy", __func__);
1971
1972    /* must be set if the server certificate is to be checked */
1973    if (sc->proxy_x509_ca_file)
1974    {
1975        /* initialize the trust list */
1976        err = gnutls_x509_trust_list_init(&sc->proxy_x509_tl, 0);
1977        if (err != GNUTLS_E_SUCCESS)
1978        {
1979            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
1980                         "%s: gnutls_x509_trust_list_init failed: %s (%d)",
1981                         __func__, gnutls_strerror(err), err);
1982            ret = APR_EGENERAL;
1983        }
1984
1985        char* ca_file = ap_server_root_relative(pool,
1986                                                sc->proxy_x509_ca_file);
1987        /* if no CRL is used, sc->proxy_x509_crl_file is NULL */
1988        char* crl_file = NULL;
1989        if (sc->proxy_x509_crl_file)
1990            crl_file = ap_server_root_relative(pool,
1991                                               sc->proxy_x509_crl_file);
1992
1993        /* returns number of loaded elements */
1994        err = gnutls_x509_trust_list_add_trust_file(sc->proxy_x509_tl,
1995                                                    ca_file,
1996                                                    crl_file,
1997                                                    GNUTLS_X509_FMT_PEM,
1998                                                    0 /* tl_flags */,
1999                                                    0 /* tl_vflags */);
2000        if (err > 0)
2001            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
2002                         "%s: proxy CA trust list: %d structures loaded",
2003                         __func__, err);
2004        else if (err == 0)
2005            ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
2006                         "%s: proxy CA trust list is empty (%d)",
2007                         __func__, err);
2008        else /* err < 0 */
2009        {
2010            ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
2011                         "%s: error loading proxy CA trust list: %s (%d)",
2012                         __func__, gnutls_strerror(err), err);
2013            ret = APR_EGENERAL;
2014        }
2015
2016        /* attach trust list to credentials */
2017        gnutls_certificate_set_trust_list(sc->proxy_x509_creds,
2018                                          sc->proxy_x509_tl, 0);
2019    }
2020    else
2021        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
2022                     "%s: no CA trust list for proxy connections, "
2023                     "TLS connections will fail!", __func__);
2024
2025    gnutls_certificate_set_verify_function(sc->proxy_x509_creds,
2026                                           gtls_check_server_cert);
2027    apr_pool_destroy(pool);
2028    return ret;
2029}
Note: See TracBrowser for help on using the repository browser.