source: mod_gnutls/src/gnutls_hooks.c @ 765cac2

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

clean up MGS_SIDE abuse of apr_pstrcat

We were allocating twice as many sections of RAM when exporting
environment variables as we needed to (the names of each env var as
well as the content).

This change statically allocates the names of most of the environment
variables, reducing the number of function calls and dynamic
allocations at runtime.

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