source: mod_gnutls/src/gnutls_hooks.c @ 4ec9183

debian/masterdebian/stretch-backportsjessie-backportsupstream
Last change on this file since 4ec9183 was 4ec9183, checked in by Daniel Kahn Gillmor <dkg@…>, 6 years ago

Include GnuTLS version as additional version component in Server string

It would be nice if mod_gnutls could add the used (runtime) version of
GnuTLS as an additional version component.

The below patch will add this information while falling back to using
the static version string (plus an indication) if the dynamic version
could not be constructed.

Regards,
BenBE.

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