source: mod_gnutls/src/gnutls_hooks.c @ b429e4c

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

Cleanup handler for proxy TLS connections

When handling client connections, the TLS connection is closed when the
the data source announces "end of connection" with an EOC bucket in the
output bucket brigade. For proxy back end connections there is no such
mechanism.

This commit adds a pre cleanup hook to the connection memory pool of
proxy back end connections, which will try to close the TLS connection
and then deinit the GnuTLS session.

Note that mod_proxy might not close connections immediately, so there is
no guarantee as to when exactly the cleanup will happen. This means that
the TLS session termination might be too late to be meaningful to the
peer, but either way the GnuTLS session structure will be deinitialized
properly. If you need to ensure that connections are closed immediately,
you might want to look at the "proxy-nokeepalive" environment variable
for mod_proxy_http.

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