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

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

Merge branch 'new-gnutls-api'

Merge my TLS proxy implementation with Nikos Mavrogiannopoulos' changes
to use the new GnuTLS key handling API. Some conflicts had to be
resolved.

In Nikos' branch, structures for credentials and priorities are
allocated in mgs_load_files (gnutls_config.c), rather than during server
config structure creation as before. This makes sense, but his patch
doesn't consider the proxy credentials because they didn't exist at the
time.

To minimize additional changes during the merge, proxy credentials are
now allocated in load_proxy_x509_credentials (gnutls_hooks.c), and
mgs_set_priorities (gnutls_config.c) treats proxy and front end
credentials differently (value of GnuTLSPriorities is stored for
mgs_load_files, GnuTLSProxyPriorities is parsed immediately).

Unified handling of priority strings in mgs_set_priorities should be
restored later (towards parsing in post config), handling front end and
proxy credentials separately makes sense because the latter need only be
loaded when TLS proxy operation is enabled and there are some
differences between client (proxy back end) and server (front end)
operation.

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