source: mod_gnutls/src/gnutls_hooks.c @ beb14d9

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

Proof of concept: Support for proxy back end connections using TLS

This commit enables TLS on proxy back end connections if requested from
mod_proxy. Since mod_gnutls acts as client instead of server on proxy
back end connections, TLS session setup is quite different.

Note that this implementation is not finished, in particular the proxy
back end connection is hard coded to use the same X.509 credentials as
the server side, which severely restricts usable certificate
combinations.

Some typos in comments and an error message related to TLS handshake are
fixed as well.

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