source: mod_gnutls/src/gnutls_hooks.c @ 412ee84

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

Use TLS session cleanup hook for all connection pools

If a TLS session hasn't been terminated by the time Apache releases
the resources of the associated connection, this hook ensures the
session is deinitialized properly. With this change, the hook is used
for all connections, not just proxy connections as before.

Note that the cleanup hook is just a second line of defense against
memory leaks. The I/O filter functions should close TLS sessions on
EOF, and cleanup_gnutls_session will log a warning if it has to close
a session.

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