source: mod_gnutls/src/gnutls_hooks.c @ 99f8375

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

Ensure safe defaults during client verification

Initialize the validation status to "invalid". Two error codepaths do
not set status, though it might be read later. They should never be hit
(invalid verification modes should be rejected during server config),
but this adds another line of defense.

Also, the error paths now set the "rv" variable to an error code, which
means client verification fails without checking of the status variable.

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