source: mod_gnutls/src/gnutls_hooks.c @ e02dd8c

debian/masterdebian/stretch-backportsjessie-backportsmsvaupstream
Last change on this file since e02dd8c was e02dd8c, checked in by Nikos Mavrogiannopoulos <nmav@…>, 9 years ago

indented code

  • Property mode set to 100644
File size: 35.6 KB
Line 
1/**
2 *  Copyright 2004-2005 Paul Querna
3 *  Copyright 2007 Nikos Mavrogiannopoulos
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 */
18
19#include "mod_gnutls.h"
20#include "http_vhost.h"
21#include "ap_mpm.h"
22
23#if APR_HAS_THREADS
24# if GNUTLS_VERSION_MAJOR <= 2 && GNUTLS_VERSION_MINOR < 11
25#include <gcrypt.h>
26GCRY_THREAD_OPTION_PTHREAD_IMPL;
27# endif
28#endif
29
30#if !USING_2_1_RECENT
31extern server_rec *ap_server_conf;
32#endif
33
34#if MOD_GNUTLS_DEBUG
35static apr_file_t *debug_log_fp;
36#endif
37
38static int mpm_is_threaded;
39static gnutls_datum session_ticket_key = { NULL, 0 };
40
41static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt);
42/* use side==0 for server and side==1 for client */
43static void mgs_add_common_cert_vars(request_rec * r,
44                                     gnutls_x509_crt_t cert, int side,
45                                     int export_certificates_enabled);
46static void mgs_add_common_pgpcert_vars(request_rec * r,
47                                        gnutls_openpgp_crt_t cert,
48                                        int side,
49                                        int export_certificates_enabled);
50
51static apr_status_t mgs_cleanup_pre_config(void *data)
52{
53        gnutls_free(session_ticket_key.data);
54        session_ticket_key.data = NULL;
55        session_ticket_key.size = 0;
56        gnutls_global_deinit();
57        return APR_SUCCESS;
58}
59
60#if MOD_GNUTLS_DEBUG
61static void gnutls_debug_log_all(int level, const char *str)
62{
63        apr_file_printf(debug_log_fp, "<%d> %s\n", level, str);
64}
65
66#define _gnutls_log apr_file_printf
67#else
68# define _gnutls_log(...)
69#endif
70
71int
72mgs_hook_pre_config(apr_pool_t * pconf,
73                    apr_pool_t * plog, apr_pool_t * ptemp)
74{
75        int ret;
76
77#if MOD_GNUTLS_DEBUG
78        apr_file_open(&debug_log_fp, "/tmp/gnutls_debug",
79                      APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
80                      pconf);
81
82        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
83
84        gnutls_global_set_log_level(9);
85        gnutls_global_set_log_function(gnutls_debug_log_all);
86        _gnutls_log(debug_log_fp, "gnutls: %s\n",
87                    gnutls_check_version(NULL));
88#endif
89
90#if APR_HAS_THREADS
91        ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_is_threaded);
92#if (GNUTLS_VERSION_MAJOR == 2 && GNUTLS_VERSION_MINOR < 11) || GNUTLS_VERSION_MAJOR < 2
93        if (mpm_is_threaded) {
94                gcry_control(GCRYCTL_SET_THREAD_CBS,
95                             &gcry_threads_pthread);
96        }
97#endif
98#else
99        mpm_is_threaded = 0;
100#endif
101
102
103        if (gnutls_check_version(LIBGNUTLS_VERSION) == NULL) {
104                _gnutls_log(debug_log_fp,
105                            "gnutls_check_version() failed. Required: gnutls-%s Found: gnutls-%s\n",
106                            LIBGNUTLS_VERSION, gnutls_check_version(NULL));
107                return -3;
108        }
109
110        ret = gnutls_global_init();
111        if (ret < 0) {
112                _gnutls_log(debug_log_fp, "gnutls_global_init: %s\n",
113                            gnutls_strerror(ret));
114                return -3;
115        }
116
117        ret = gnutls_session_ticket_key_generate(&session_ticket_key);
118        if (ret < 0) {
119                _gnutls_log(debug_log_fp,
120                            "gnutls_session_ticket_key_generate: %s\n",
121                            gnutls_strerror(ret));
122        }
123
124        apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config,
125                                  apr_pool_cleanup_null);
126
127
128        return OK;
129}
130
131static int mgs_select_virtual_server_cb(gnutls_session_t session)
132{
133        mgs_handle_t *ctxt;
134        mgs_srvconf_rec *tsc;
135        int ret;
136        int cprio[2];
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                ctxt->sc = tsc;
147
148        gnutls_certificate_server_set_request(session,
149                                              ctxt->
150                                              sc->client_verify_mode);
151
152        /* set the new server credentials
153         */
154
155        gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
156                               ctxt->sc->certs);
157
158        gnutls_credentials_set(session, GNUTLS_CRD_ANON,
159                               ctxt->sc->anon_creds);
160
161#ifdef ENABLE_SRP
162        if (ctxt->sc->srp_tpasswd_conf_file != NULL
163            && ctxt->sc->srp_tpasswd_file != NULL) {
164                gnutls_credentials_set(session, GNUTLS_CRD_SRP,
165                                       ctxt->sc->srp_creds);
166        }
167#endif
168
169        /* update the priorities - to avoid negotiating a ciphersuite that is not
170         * enabled on this virtual server. Note that here we ignore the version
171         * negotiation.
172         */
173        ret = gnutls_priority_set(session, ctxt->sc->priorities);
174        /* actually it shouldn't fail since we have checked at startup */
175        if (ret < 0)
176                return ret;
177
178        /* If both certificate types are not present disallow them from
179         * being negotiated.
180         */
181        if (ctxt->sc->certs_x509[0] != NULL && ctxt->sc->cert_pgp == NULL) {
182                cprio[0] = GNUTLS_CRT_X509;
183                cprio[1] = 0;
184                gnutls_certificate_type_set_priority(session, cprio);
185        } else if (ctxt->sc->cert_pgp != NULL
186                   && ctxt->sc->certs_x509[0] == NULL) {
187                cprio[0] = GNUTLS_CRT_OPENPGP;
188                cprio[1] = 0;
189                gnutls_certificate_type_set_priority(session, cprio);
190        }
191
192        return 0;
193}
194
195static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st * ret)
196{
197        mgs_handle_t *ctxt;
198
199        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
200        ctxt = gnutls_transport_get_ptr(session);
201
202        if (ctxt == NULL)
203                return GNUTLS_E_INTERNAL_ERROR;
204
205        if (gnutls_certificate_type_get(session) == GNUTLS_CRT_X509) {
206                ret->type = GNUTLS_CRT_X509;
207                ret->ncerts = ctxt->sc->certs_x509_num;
208                ret->deinit_all = 0;
209
210                ret->cert.x509 = ctxt->sc->certs_x509;
211                ret->key.x509 = ctxt->sc->privkey_x509;
212
213                return 0;
214        } else if (gnutls_certificate_type_get(session) ==
215                   GNUTLS_CRT_OPENPGP) {
216                ret->type = GNUTLS_CRT_OPENPGP;
217                ret->ncerts = 1;
218                ret->deinit_all = 0;
219
220                ret->cert.pgp = ctxt->sc->cert_pgp;
221                ret->key.pgp = ctxt->sc->privkey_pgp;
222
223                return 0;
224
225        }
226
227        return GNUTLS_E_INTERNAL_ERROR;
228}
229
230/* 2048-bit group parameters from SRP specification */
231const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n"
232    "MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n"
233    "gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n"
234    "KRipli8Lk7hV+XmT7Jde6qgNdArb9P90c1nQQdXDPqcdKB5EaxR3O8qXtDoj+4AW\n"
235    "dr0gekNsZIHx0rkHhxdGGludMuaI+HdIVEUjtSSw1X1ep3onddLs+gMs+9v1L7N4\n"
236    "YWAnkATleuavh05zA85TKZzMBBx7wwjYKlaY86jQw4JxrjX46dv7tpS1yAPYn3rk\n"
237    "Nd4jbVJfVHWbZeNy/NaO8g+nER+eSv9zAgEC\n"
238    "-----END DH PARAMETERS-----\n";
239
240/* Read the common name or the alternative name of the certificate.
241 * We only support a single name per certificate.
242 *
243 * Returns negative on error.
244 */
245static int read_crt_cn(server_rec * s, apr_pool_t * p,
246                       gnutls_x509_crt_t cert, char **cert_cn)
247{
248        int rv = 0, i;
249        size_t data_len;
250
251
252        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
253        *cert_cn = NULL;
254
255        data_len = 0;
256        rv = gnutls_x509_crt_get_dn_by_oid(cert,
257                                           GNUTLS_OID_X520_COMMON_NAME,
258                                           0, 0, NULL, &data_len);
259
260        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
261                *cert_cn = apr_palloc(p, data_len);
262                rv = gnutls_x509_crt_get_dn_by_oid(cert,
263                                                   GNUTLS_OID_X520_COMMON_NAME,
264                                                   0, 0, *cert_cn,
265                                                   &data_len);
266        } else {                /* No CN return subject alternative name */
267                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
268                             "No common name found in certificate for '%s:%d'. Looking for subject alternative name...",
269                             s->server_hostname, s->port);
270                rv = 0;
271                /* read subject alternative name */
272                for (i = 0; !(rv < 0); i++) {
273                        data_len = 0;
274                        rv = gnutls_x509_crt_get_subject_alt_name(cert, i,
275                                                                  NULL,
276                                                                  &data_len,
277                                                                  NULL);
278
279                        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER
280                            && data_len > 1) {
281                                /* FIXME: not very efficient. What if we have several alt names
282                                 * before DNSName?
283                                 */
284                                *cert_cn = apr_palloc(p, data_len + 1);
285
286                                rv = gnutls_x509_crt_get_subject_alt_name
287                                    (cert, i, *cert_cn, &data_len, NULL);
288                                (*cert_cn)[data_len] = 0;
289
290                                if (rv == GNUTLS_SAN_DNSNAME)
291                                        break;
292                        }
293                }
294        }
295
296        return rv;
297}
298
299static int read_pgpcrt_cn(server_rec * s, apr_pool_t * p,
300                          gnutls_openpgp_crt_t cert, char **cert_cn)
301{
302        int rv = 0;
303        size_t data_len;
304
305
306        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
307        *cert_cn = NULL;
308
309        data_len = 0;
310        rv = gnutls_openpgp_crt_get_name(cert, 0, NULL, &data_len);
311
312        if (rv == GNUTLS_E_SHORT_MEMORY_BUFFER && data_len > 1) {
313                *cert_cn = apr_palloc(p, data_len);
314                rv = gnutls_openpgp_crt_get_name(cert, 0, *cert_cn,
315                                                 &data_len);
316        } else {                /* No CN return subject alternative name */
317                ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
318                             "No name found in PGP certificate for '%s:%d'.",
319                             s->server_hostname, s->port);
320        }
321
322        return rv;
323}
324
325
326int
327mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
328                     apr_pool_t * ptemp, server_rec * base_server)
329{
330        int rv;
331        server_rec *s;
332        gnutls_dh_params_t dh_params = NULL;
333        gnutls_rsa_params_t rsa_params = NULL;
334        mgs_srvconf_rec *sc;
335        mgs_srvconf_rec *sc_base;
336        void *data = NULL;
337        int first_run = 0;
338        const char *userdata_key = "mgs_init";
339
340        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
341        apr_pool_userdata_get(&data, userdata_key,
342                              base_server->process->pool);
343        if (data == NULL) {
344                first_run = 1;
345                apr_pool_userdata_set((const void *) 1, userdata_key,
346                                      apr_pool_cleanup_null,
347                                      base_server->process->pool);
348        }
349
350
351        s = base_server;
352        sc_base =
353            (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
354                                                     &gnutls_module);
355
356        gnutls_dh_params_init(&dh_params);
357
358        if (sc_base->dh_params == NULL) {
359                gnutls_datum pdata = {
360                        (void *) static_dh_params,
361                        sizeof(static_dh_params)
362                };
363                /* loading defaults */
364                rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata,
365                                                   GNUTLS_X509_FMT_PEM);
366
367                if (rv < 0) {
368                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
369                                     "GnuTLS: Unable to load DH Params: (%d) %s",
370                                     rv, gnutls_strerror(rv));
371                        exit(rv);
372                }
373        } else
374                dh_params = sc_base->dh_params;
375
376        if (sc_base->rsa_params != NULL)
377                rsa_params = sc_base->rsa_params;
378
379        /* else not an error but RSA-EXPORT ciphersuites are not available
380         */
381
382        rv = mgs_cache_post_config(p, s, sc_base);
383        if (rv != 0) {
384                ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
385                             "GnuTLS: Post Config for GnuTLSCache Failed."
386                             " Shutting Down.");
387                exit(-1);
388        }
389
390        for (s = base_server; s; s = s->next) {
391                void *load = NULL;
392                sc = (mgs_srvconf_rec *)
393                    ap_get_module_config(s->module_config, &gnutls_module);
394                sc->cache_type = sc_base->cache_type;
395                sc->cache_config = sc_base->cache_config;
396
397                /* Check if the priorities have been set */
398                if (sc->priorities == NULL
399                    && sc->enabled == GNUTLS_ENABLED_TRUE) {
400                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
401                                     "GnuTLS: Host '%s:%d' is missing the GnuTLSPriorities directive!",
402                                     s->server_hostname, s->port);
403                        exit(-1);
404                }
405
406                /* Check if DH or RSA params have been set per host */
407                if (sc->rsa_params != NULL)
408                        load = sc->rsa_params;
409                else if (rsa_params)
410                        load = rsa_params;
411
412                if (load != NULL)
413                        gnutls_certificate_set_rsa_export_params(sc->certs,
414                                                                 load);
415
416
417                load = NULL;
418                if (sc->dh_params != NULL)
419                        load = sc->dh_params;
420                else if (dh_params)
421                        load = dh_params;
422
423                if (load != NULL) {     /* not needed but anyway */
424                        gnutls_certificate_set_dh_params(sc->certs, load);
425                        gnutls_anon_set_server_dh_params(sc->anon_creds,
426                                                         load);
427                }
428
429                gnutls_certificate_server_set_retrieve_function(sc->certs,
430                                                                cert_retrieve_fn);
431
432#ifdef ENABLE_SRP
433                if (sc->srp_tpasswd_conf_file != NULL
434                    && sc->srp_tpasswd_file != NULL) {
435                        rv = gnutls_srp_set_server_credentials_file
436                            (sc->srp_creds, sc->srp_tpasswd_file,
437                             sc->srp_tpasswd_conf_file);
438
439                        if (rv < 0 && sc->enabled == GNUTLS_ENABLED_TRUE) {
440                                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0,
441                                             s,
442                                             "[GnuTLS] - Host '%s:%d' is missing a "
443                                             "SRP password or conf File!",
444                                             s->server_hostname, s->port);
445                                exit(-1);
446                        }
447                }
448#endif
449
450                if (sc->certs_x509[0] == NULL &&
451                    sc->cert_pgp == NULL &&
452                    sc->enabled == GNUTLS_ENABLED_TRUE) {
453                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
454                                     "[GnuTLS] - Host '%s:%d' is missing a "
455                                     "Certificate File!",
456                                     s->server_hostname, s->port);
457                        exit(-1);
458                }
459
460                if (sc->enabled == GNUTLS_ENABLED_TRUE &&
461                    ((sc->certs_x509[0] != NULL
462                      && sc->privkey_x509 == NULL) || (sc->cert_pgp != NULL
463                                                       && sc->privkey_pgp
464                                                       == NULL))) {
465                        ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
466                                     "[GnuTLS] - Host '%s:%d' is missing a "
467                                     "Private Key File!",
468                                     s->server_hostname, s->port);
469                        exit(-1);
470                }
471
472                if (sc->enabled == GNUTLS_ENABLED_TRUE) {
473                        rv = read_crt_cn(s, p, sc->certs_x509[0],
474                                         &sc->cert_cn);
475                        if (rv < 0 && sc->cert_pgp != NULL)     /* try openpgp certificate */
476                                rv = read_pgpcrt_cn(s, p, sc->cert_pgp,
477                                                    &sc->cert_cn);
478
479                        if (rv < 0) {
480                                ap_log_error(APLOG_MARK, APLOG_STARTUP, 0,
481                                             s,
482                                             "[GnuTLS] - Cannot find a certificate for host '%s:%d'!",
483                                             s->server_hostname, s->port);
484                                sc->cert_cn = NULL;
485                                continue;
486                        }
487                }
488        }
489
490
491        ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION);
492
493        return OK;
494}
495
496void mgs_hook_child_init(apr_pool_t * p, server_rec * s)
497{
498        apr_status_t rv = APR_SUCCESS;
499        mgs_srvconf_rec *sc = ap_get_module_config(s->module_config,
500                                                   &gnutls_module);
501
502        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
503        if (sc->cache_type != mgs_cache_none) {
504                rv = mgs_cache_child_init(p, s, sc);
505                if (rv != APR_SUCCESS) {
506                        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
507                                     "[GnuTLS] - Failed to run Cache Init");
508                }
509        } else {
510                ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
511                             "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache");
512        }
513}
514
515const char *mgs_hook_http_scheme(const request_rec * r)
516{
517        mgs_srvconf_rec *sc;
518
519        if (r == NULL)
520                return NULL;
521
522        sc = (mgs_srvconf_rec *) ap_get_module_config(r->
523                                                      server->module_config,
524                                                      &gnutls_module);
525
526        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
527        if (sc->enabled == GNUTLS_ENABLED_FALSE) {
528                return NULL;
529        }
530
531        return "https";
532}
533
534apr_port_t mgs_hook_default_port(const request_rec * r)
535{
536        mgs_srvconf_rec *sc;
537
538        if (r == NULL)
539                return 0;
540
541        sc = (mgs_srvconf_rec *) ap_get_module_config(r->
542                                                      server->module_config,
543                                                      &gnutls_module);
544
545        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
546        if (sc->enabled == GNUTLS_ENABLED_FALSE) {
547                return 0;
548        }
549
550        return 443;
551}
552
553#define MAX_HOST_LEN 255
554
555#if USING_2_1_RECENT
556typedef struct {
557        mgs_handle_t *ctxt;
558        mgs_srvconf_rec *sc;
559        const char *sni_name;
560} vhost_cb_rec;
561
562static int vhost_cb(void *baton, conn_rec * conn, server_rec * s)
563{
564        mgs_srvconf_rec *tsc;
565        vhost_cb_rec *x = baton;
566
567        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
568        tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
569                                                       &gnutls_module);
570
571        if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
572                return 0;
573        }
574
575        /* The CN can contain a * -- this will match those too. */
576        if (ap_strcasecmp_match(x->sni_name, tsc->cert_cn) == 0) {
577                /* found a match */
578#if MOD_GNUTLS_DEBUG
579                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
580                             x->ctxt->c->base_server,
581                             "GnuTLS: Virtual Host CB: "
582                             "'%s' == '%s'", tsc->cert_cn, x->sni_name);
583#endif
584                /* Because we actually change the server used here, we need to reset
585                 * things like ClientVerify.
586                 */
587                x->sc = tsc;
588                /* Shit. Crap. Dammit. We *really* should rehandshake here, as our
589                 * certificate structure *should* change when the server changes.
590                 * acccckkkkkk.
591                 */
592                return 1;
593        } else {
594#if MOD_GNUTLS_DEBUG
595                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
596                             x->ctxt->c->base_server,
597                             "GnuTLS: Virtual Host CB: "
598                             "'%s' != '%s'", tsc->cert_cn, x->sni_name);
599#endif
600
601        }
602        return 0;
603}
604#endif
605
606mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
607{
608        int rv;
609        unsigned int sni_type;
610        size_t data_len = MAX_HOST_LEN;
611        char sni_name[MAX_HOST_LEN];
612        mgs_handle_t *ctxt;
613#if USING_2_1_RECENT
614        vhost_cb_rec cbx;
615#else
616        server_rec *s;
617        mgs_srvconf_rec *tsc;
618#endif
619
620        if (session == NULL)
621                return NULL;
622
623        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
624        ctxt = gnutls_transport_get_ptr(session);
625
626        rv = gnutls_server_name_get(ctxt->session, sni_name,
627                                    &data_len, &sni_type, 0);
628
629        if (rv != 0) {
630                return NULL;
631        }
632
633        if (sni_type != GNUTLS_NAME_DNS) {
634                ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
635                             ctxt->c->base_server,
636                             "GnuTLS: Unknown type '%d' for SNI: "
637                             "'%s'", sni_type, sni_name);
638                return NULL;
639        }
640
641    /**
642     * Code in the Core already sets up the c->base_server as the base
643     * for this IP/Port combo.  Trust that the core did the 'right' thing.
644     */
645#if USING_2_1_RECENT
646        cbx.ctxt = ctxt;
647        cbx.sc = NULL;
648        cbx.sni_name = sni_name;
649
650        rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
651        if (rv == 1) {
652                return cbx.sc;
653        }
654#else
655        for (s = ap_server_conf; s; s = s->next) {
656
657                tsc =
658                    (mgs_srvconf_rec *)
659                    ap_get_module_config(s->module_config, &gnutls_module);
660                if (tsc->enabled != GNUTLS_ENABLED_TRUE) {
661                        continue;
662                }
663#if MOD_GNUTLS_DEBUG
664                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
665                             ctxt->c->base_server,
666                             "GnuTLS: sni-x509 cn: %s/%d pk: %s s: 0x%08X s->n: 0x%08X  sc: 0x%08X",
667                             tsc->cert_cn, rv,
668                             gnutls_pk_algorithm_get_name
669                             (gnutls_x509_privkey_get_pk_algorithm
670                              (ctxt->sc->privkey_x509)), (unsigned int) s,
671                             (unsigned int) s->next, (unsigned int) tsc);
672#endif
673                /* The CN can contain a * -- this will match those too. */
674                if (ap_strcasecmp_match(sni_name, tsc->cert_cn) == 0) {
675#if MOD_GNUTLS_DEBUG
676                        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
677                                     ctxt->c->base_server,
678                                     "GnuTLS: Virtual Host: "
679                                     "'%s' == '%s'", tsc->cert_cn,
680                                     sni_name);
681#endif
682                        return tsc;
683                }
684        }
685#endif
686        return NULL;
687}
688
689
690static const int protocol_priority[] = {
691        GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0
692};
693
694
695static mgs_handle_t *create_gnutls_handle(apr_pool_t * pool, conn_rec * c)
696{
697        mgs_handle_t *ctxt;
698        mgs_srvconf_rec *sc =
699            (mgs_srvconf_rec *) ap_get_module_config(c->base_server->
700                                                     module_config,
701                                                     &gnutls_module);
702
703        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
704        ctxt = apr_pcalloc(pool, sizeof(*ctxt));
705        ctxt->c = c;
706        ctxt->sc = sc;
707        ctxt->status = 0;
708
709        ctxt->input_rc = APR_SUCCESS;
710        ctxt->input_bb = apr_brigade_create(c->pool, c->bucket_alloc);
711        ctxt->input_cbuf.length = 0;
712
713        ctxt->output_rc = APR_SUCCESS;
714        ctxt->output_bb = apr_brigade_create(c->pool, c->bucket_alloc);
715        ctxt->output_blen = 0;
716        ctxt->output_length = 0;
717
718        gnutls_init(&ctxt->session, GNUTLS_SERVER);
719        if (session_ticket_key.data != NULL && ctxt->sc->tickets != 0)
720                gnutls_session_ticket_enable_server(ctxt->session,
721                                                    &session_ticket_key);
722
723        /* because we don't set any default priorities here (we set later at
724         * the user hello callback) we need to at least set this in order for
725         * gnutls to be able to read packets.
726         */
727        gnutls_protocol_set_priority(ctxt->session, protocol_priority);
728
729        gnutls_handshake_set_post_client_hello_function(ctxt->session,
730                                                        mgs_select_virtual_server_cb);
731
732        mgs_cache_session_init(ctxt);
733
734        return ctxt;
735}
736
737int mgs_hook_pre_connection(conn_rec * c, void *csd)
738{
739        mgs_handle_t *ctxt;
740        mgs_srvconf_rec *sc;
741
742        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
743
744        if (c == NULL)
745                return DECLINED;
746
747        sc = (mgs_srvconf_rec *) ap_get_module_config(c->base_server->
748                                                      module_config,
749                                                      &gnutls_module);
750
751        if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) {
752                return DECLINED;
753        }
754
755        if (c->remote_addr->hostname)
756                /* Connection initiated by Apache (mod_proxy) => ignore */
757                return OK;
758
759        ctxt = create_gnutls_handle(c->pool, c);
760
761        ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
762
763        gnutls_transport_set_pull_function(ctxt->session,
764                                           mgs_transport_read);
765        gnutls_transport_set_push_function(ctxt->session,
766                                           mgs_transport_write);
767        gnutls_transport_set_ptr(ctxt->session, ctxt);
768
769        ctxt->input_filter =
770            ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c);
771        ctxt->output_filter =
772            ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c);
773
774        return OK;
775}
776
777int mgs_hook_fixups(request_rec * r)
778{
779        unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
780        char buf[AP_IOBUFSIZE];
781        const char *tmp;
782        size_t len;
783        mgs_handle_t *ctxt;
784        int rv = OK;
785
786        if (r == NULL)
787                return DECLINED;
788
789        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
790        apr_table_t *env = r->subprocess_env;
791
792        ctxt =
793            ap_get_module_config(r->connection->conn_config,
794                                 &gnutls_module);
795
796        if (!ctxt || ctxt->session == NULL) {
797                return DECLINED;
798        }
799
800        apr_table_setn(env, "HTTPS", "on");
801
802        apr_table_setn(env, "SSL_VERSION_LIBRARY",
803                       "GnuTLS/" LIBGNUTLS_VERSION);
804        apr_table_setn(env, "SSL_VERSION_INTERFACE",
805                       "mod_gnutls/" MOD_GNUTLS_VERSION);
806
807        apr_table_setn(env, "SSL_PROTOCOL",
808                       gnutls_protocol_get_name(gnutls_protocol_get_version
809                                                (ctxt->session)));
810
811        /* should have been called SSL_CIPHERSUITE instead */
812        apr_table_setn(env, "SSL_CIPHER",
813                       gnutls_cipher_suite_get_name(gnutls_kx_get
814                                                    (ctxt->session),
815                                                    gnutls_cipher_get
816                                                    (ctxt->session),
817                                                    gnutls_mac_get
818                                                    (ctxt->session)));
819
820        apr_table_setn(env, "SSL_COMPRESS_METHOD",
821                       gnutls_compression_get_name(gnutls_compression_get
822                                                   (ctxt->session)));
823
824#ifdef ENABLE_SRP
825        tmp = gnutls_srp_server_get_username(ctxt->session);
826        apr_table_setn(env, "SSL_SRP_USER", (tmp != NULL) ? tmp : "");
827#endif
828
829        if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
830                apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
831
832        unsigned int key_size =
833            8 *
834            gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
835        tmp = apr_psprintf(r->pool, "%u", key_size);
836
837        apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
838
839        apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
840
841        apr_table_setn(env, "SSL_CIPHER_EXPORT",
842                       (key_size <= 40) ? "true" : "false");
843
844        len = sizeof(sbuf);
845        gnutls_session_get_id(ctxt->session, sbuf, &len);
846        tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
847        apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
848
849        if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
850                mgs_add_common_cert_vars(r, ctxt->sc->certs_x509[0], 0,
851                                         ctxt->
852                                         sc->export_certificates_enabled);
853        else if (gnutls_certificate_type_get(ctxt->session) ==
854                 GNUTLS_CRT_OPENPGP)
855                mgs_add_common_pgpcert_vars(r, ctxt->sc->cert_pgp, 0,
856                                            ctxt->
857                                            sc->export_certificates_enabled);
858
859        return rv;
860}
861
862int mgs_hook_authz(request_rec * r)
863{
864        int rv;
865        mgs_handle_t *ctxt;
866        mgs_dirconf_rec *dc;
867
868        if (r == NULL)
869                return DECLINED;
870
871        dc = ap_get_module_config(r->per_dir_config, &gnutls_module);
872
873        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
874        ctxt =
875            ap_get_module_config(r->connection->conn_config,
876                                 &gnutls_module);
877
878        if (!ctxt || ctxt->session == NULL) {
879                return DECLINED;
880        }
881
882        if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
883                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
884                              "GnuTLS: Directory set to Ignore Client Certificate!");
885        } else {
886                if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
887                        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
888                                      "GnuTLS: Attempting to rehandshake with peer. %d %d",
889                                      ctxt->sc->client_verify_mode,
890                                      dc->client_verify_mode);
891
892                        /* If we already have a client certificate, there's no point in
893                         * re-handshaking... */
894                        rv = mgs_cert_verify(r, ctxt);
895                        if (rv != DECLINED && rv != HTTP_FORBIDDEN)
896                                return rv;
897
898                        gnutls_certificate_server_set_request
899                            (ctxt->session, dc->client_verify_mode);
900
901                        if (mgs_rehandshake(ctxt) != 0) {
902                                return HTTP_FORBIDDEN;
903                        }
904                } else if (ctxt->sc->client_verify_mode ==
905                           GNUTLS_CERT_IGNORE) {
906#if MOD_GNUTLS_DEBUG
907                        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
908                                      "GnuTLS: Peer is set to IGNORE");
909#endif
910                        return DECLINED;
911                }
912                rv = mgs_cert_verify(r, ctxt);
913                if (rv != DECLINED &&
914                    (rv != HTTP_FORBIDDEN ||
915                     dc->client_verify_mode == GNUTLS_CERT_REQUIRE)) {
916                        return rv;
917                }
918        }
919
920        return DECLINED;
921}
922
923/* variables that are not sent by default:
924 *
925 * SSL_CLIENT_CERT      string  PEM-encoded client certificate
926 * SSL_SERVER_CERT      string  PEM-encoded client certificate
927 */
928
929/* side is either 0 for SERVER or 1 for CLIENT
930 */
931#define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT")
932static void
933mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt_t cert, int side,
934                         int export_certificates_enabled)
935{
936        unsigned char sbuf[64]; /* buffer to hold serials */
937        char buf[AP_IOBUFSIZE];
938        const char *tmp;
939        char *tmp2;
940        size_t len;
941        int ret, i;
942
943        if (r == NULL)
944                return;
945
946        apr_table_t *env = r->subprocess_env;
947
948        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
949        if (export_certificates_enabled != 0) {
950                char cert_buf[10 * 1024];
951                len = sizeof(cert_buf);
952
953                if (gnutls_x509_crt_export
954                    (cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0)
955                        apr_table_setn(env,
956                                       apr_pstrcat(r->pool, MGS_SIDE,
957                                                   "_CERT", NULL),
958                                       apr_pstrmemdup(r->pool, cert_buf,
959                                                      len));
960
961        }
962
963        len = sizeof(buf);
964        gnutls_x509_crt_get_dn(cert, buf, &len);
965        apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_S_DN", NULL),
966                       apr_pstrmemdup(r->pool, buf, len));
967
968        len = sizeof(buf);
969        gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
970        apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_I_DN", NULL),
971                       apr_pstrmemdup(r->pool, buf, len));
972
973        len = sizeof(sbuf);
974        gnutls_x509_crt_get_serial(cert, sbuf, &len);
975        tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
976        apr_table_setn(env,
977                       apr_pstrcat(r->pool, MGS_SIDE, "_M_SERIAL", NULL),
978                       apr_pstrdup(r->pool, tmp));
979
980        ret = gnutls_x509_crt_get_version(cert);
981        if (ret > 0)
982                apr_table_setn(env,
983                               apr_pstrcat(r->pool, MGS_SIDE, "_M_VERSION",
984                                           NULL), apr_psprintf(r->pool,
985                                                               "%u", ret));
986
987        apr_table_setn(env,
988                       apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL),
989                       "X.509");
990
991        tmp =
992            mgs_time2sz(gnutls_x509_crt_get_expiration_time
993                        (cert), buf, sizeof(buf));
994        apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL),
995                       apr_pstrdup(r->pool, tmp));
996
997        tmp =
998            mgs_time2sz(gnutls_x509_crt_get_activation_time
999                        (cert), buf, sizeof(buf));
1000        apr_table_setn(env,
1001                       apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL),
1002                       apr_pstrdup(r->pool, tmp));
1003
1004        ret = gnutls_x509_crt_get_signature_algorithm(cert);
1005        if (ret >= 0) {
1006                apr_table_setn(env,
1007                               apr_pstrcat(r->pool, MGS_SIDE, "_A_SIG",
1008                                           NULL),
1009                               gnutls_sign_algorithm_get_name(ret));
1010        }
1011
1012        ret = gnutls_x509_crt_get_pk_algorithm(cert, NULL);
1013        if (ret >= 0) {
1014                apr_table_setn(env,
1015                               apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY",
1016                                           NULL),
1017                               gnutls_pk_algorithm_get_name(ret));
1018        }
1019
1020        /* export all the alternative names (DNS, RFC822 and URI) */
1021        for (i = 0; !(ret < 0); i++) {
1022                len = 0;
1023                ret = gnutls_x509_crt_get_subject_alt_name(cert, i,
1024                                                           NULL, &len,
1025                                                           NULL);
1026
1027                if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER && len > 1) {
1028                        tmp2 = apr_palloc(r->pool, len + 1);
1029
1030                        ret =
1031                            gnutls_x509_crt_get_subject_alt_name(cert, i,
1032                                                                 tmp2,
1033                                                                 &len,
1034                                                                 NULL);
1035                        tmp2[len] = 0;
1036
1037                        if (ret == GNUTLS_SAN_DNSNAME) {
1038                                apr_table_setn(env,
1039                                               apr_psprintf(r->pool,
1040                                                            "%s_S_AN%u",
1041                                                            MGS_SIDE, i),
1042                                               apr_psprintf(r->pool,
1043                                                            "DNSNAME:%s",
1044                                                            tmp2));
1045                        } else if (ret == GNUTLS_SAN_RFC822NAME) {
1046                                apr_table_setn(env,
1047                                               apr_psprintf(r->pool,
1048                                                            "%s_S_AN%u",
1049                                                            MGS_SIDE, i),
1050                                               apr_psprintf(r->pool,
1051                                                            "RFC822NAME:%s",
1052                                                            tmp2));
1053                        } else if (ret == GNUTLS_SAN_URI) {
1054                                apr_table_setn(env,
1055                                               apr_psprintf(r->pool,
1056                                                            "%s_S_AN%u",
1057                                                            MGS_SIDE, i),
1058                                               apr_psprintf(r->pool,
1059                                                            "URI:%s",
1060                                                            tmp2));
1061                        } else {
1062                                apr_table_setn(env,
1063                                               apr_psprintf(r->pool,
1064                                                            "%s_S_AN%u",
1065                                                            MGS_SIDE, i),
1066                                               "UNSUPPORTED");
1067                        }
1068                }
1069        }
1070}
1071
1072static void
1073mgs_add_common_pgpcert_vars(request_rec * r, gnutls_openpgp_crt_t cert,
1074                            int side, int export_certificates_enabled)
1075{
1076        unsigned char sbuf[64]; /* buffer to hold serials */
1077        char buf[AP_IOBUFSIZE];
1078        const char *tmp;
1079        size_t len;
1080        int ret;
1081
1082        if (r == NULL)
1083                return;
1084
1085        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1086        apr_table_t *env = r->subprocess_env;
1087
1088        if (export_certificates_enabled != 0) {
1089                char cert_buf[10 * 1024];
1090                len = sizeof(cert_buf);
1091
1092                if (gnutls_openpgp_crt_export
1093                    (cert, GNUTLS_OPENPGP_FMT_BASE64, cert_buf, &len) >= 0)
1094                        apr_table_setn(env,
1095                                       apr_pstrcat(r->pool, MGS_SIDE,
1096                                                   "_CERT", NULL),
1097                                       apr_pstrmemdup(r->pool, cert_buf,
1098                                                      len));
1099
1100        }
1101
1102        len = sizeof(buf);
1103        gnutls_openpgp_crt_get_name(cert, 0, buf, &len);
1104        apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_NAME", NULL),
1105                       apr_pstrmemdup(r->pool, buf, len));
1106
1107        len = sizeof(sbuf);
1108        gnutls_openpgp_crt_get_fingerprint(cert, sbuf, &len);
1109        tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
1110        apr_table_setn(env,
1111                       apr_pstrcat(r->pool, MGS_SIDE, "_FINGERPRINT",
1112                                   NULL), apr_pstrdup(r->pool, tmp));
1113
1114        ret = gnutls_openpgp_crt_get_version(cert);
1115        if (ret > 0)
1116                apr_table_setn(env,
1117                               apr_pstrcat(r->pool, MGS_SIDE, "_M_VERSION",
1118                                           NULL), apr_psprintf(r->pool,
1119                                                               "%u", ret));
1120
1121        apr_table_setn(env,
1122                       apr_pstrcat(r->pool, MGS_SIDE, "_CERT_TYPE", NULL),
1123                       "OPENPGP");
1124
1125        tmp =
1126            mgs_time2sz(gnutls_openpgp_crt_get_expiration_time
1127                        (cert), buf, sizeof(buf));
1128        apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL),
1129                       apr_pstrdup(r->pool, tmp));
1130
1131        tmp =
1132            mgs_time2sz(gnutls_openpgp_crt_get_creation_time
1133                        (cert), buf, sizeof(buf));
1134        apr_table_setn(env,
1135                       apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL),
1136                       apr_pstrdup(r->pool, tmp));
1137
1138        ret = gnutls_openpgp_crt_get_pk_algorithm(cert, NULL);
1139        if (ret >= 0) {
1140                apr_table_setn(env,
1141                               apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY",
1142                                           NULL),
1143                               gnutls_pk_algorithm_get_name(ret));
1144        }
1145
1146}
1147
1148/* TODO: Allow client sending a X.509 certificate chain */
1149static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt)
1150{
1151        const gnutls_datum_t *cert_list;
1152        unsigned int cert_list_size, status;
1153        int rv = GNUTLS_E_NO_CERTIFICATE_FOUND, ret;
1154        unsigned int ch_size = 0;
1155        union {
1156                gnutls_x509_crt_t x509[MAX_CHAIN_SIZE];
1157                gnutls_openpgp_crt_t pgp;
1158        } cert;
1159        apr_time_t expiration_time, cur_time;
1160
1161        if (r == NULL || ctxt == NULL || ctxt->session == NULL)
1162                return HTTP_FORBIDDEN;
1163
1164        _gnutls_log(debug_log_fp, "%s: %d\n", __func__, __LINE__);
1165        cert_list =
1166            gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
1167
1168        if (cert_list == NULL || cert_list_size == 0) {
1169                /* It is perfectly OK for a client not to send a certificate if on REQUEST mode
1170                 */
1171                if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1172                        return OK;
1173
1174                /* no certificate provided by the client, but one was required. */
1175                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1176                              "GnuTLS: Failed to Verify Peer: "
1177                              "Client did not submit a certificate");
1178                return HTTP_FORBIDDEN;
1179        }
1180
1181        if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1182                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1183                              "GnuTLS: A Chain of %d certificate(s) was provided for validation",
1184                              cert_list_size);
1185
1186                for (ch_size = 0; ch_size < cert_list_size; ch_size++) {
1187                        gnutls_x509_crt_init(&cert.x509[ch_size]);
1188                        rv = gnutls_x509_crt_import(cert.x509[ch_size],
1189                                                    &cert_list[ch_size],
1190                                                    GNUTLS_X509_FMT_DER);
1191                        // When failure to import, leave the loop
1192                        if (rv != GNUTLS_E_SUCCESS) {
1193                                if (ch_size < 1) {
1194                                        ap_log_rerror(APLOG_MARK,
1195                                                      APLOG_INFO, 0, r,
1196                                                      "GnuTLS: Failed to Verify Peer: "
1197                                                      "Failed to import peer certificates.");
1198                                        ret = HTTP_FORBIDDEN;
1199                                        goto exit;
1200                                }
1201                                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1202                                              "GnuTLS: Failed to import some peer certificates. Using %d certificates",
1203                                              ch_size);
1204                                rv = GNUTLS_E_SUCCESS;
1205                                break;
1206                        }
1207                }
1208        } else if (gnutls_certificate_type_get(ctxt->session) ==
1209                   GNUTLS_CRT_OPENPGP) {
1210                if (cert_list_size > 1) {
1211                        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1212                                      "GnuTLS: Failed to Verify Peer: "
1213                                      "Chained Client Certificates are not supported.");
1214                        return HTTP_FORBIDDEN;
1215                }
1216
1217                gnutls_openpgp_crt_init(&cert.pgp);
1218                rv = gnutls_openpgp_crt_import(cert.pgp, &cert_list[0],
1219                                               GNUTLS_OPENPGP_FMT_RAW);
1220
1221        } else
1222                return HTTP_FORBIDDEN;
1223
1224        if (rv < 0) {
1225                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1226                              "GnuTLS: Failed to Verify Peer: "
1227                              "Failed to import peer certificates.");
1228                ret = HTTP_FORBIDDEN;
1229                goto exit;
1230        }
1231
1232        if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1233                apr_time_ansi_put(&expiration_time,
1234                                  gnutls_x509_crt_get_expiration_time
1235                                  (cert.x509[0]));
1236
1237                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1238                              "GnuTLS: Verifying list of  %d certificate(s)",
1239                              ch_size);
1240                rv = gnutls_x509_crt_list_verify(cert.x509, ch_size,
1241                                                 ctxt->sc->ca_list,
1242                                                 ctxt->sc->ca_list_size,
1243                                                 NULL, 0, 0, &status);
1244        } else {
1245                apr_time_ansi_put(&expiration_time,
1246                                  gnutls_openpgp_crt_get_expiration_time
1247                                  (cert.pgp));
1248
1249                rv = gnutls_openpgp_crt_verify_ring(cert.pgp,
1250                                                    ctxt->sc->pgp_list, 0,
1251                                                    &status);
1252        }
1253
1254        if (rv < 0) {
1255                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1256                              "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
1257                              rv, gnutls_strerror(rv));
1258                if (rv == GNUTLS_E_NO_CERTIFICATE_FOUND)
1259                        ap_log_rerror(APLOG_MARK, APLOG_EMERG, 0, r,
1260                                      "GnuTLS: No certificate was found for verification. Did you set the GnuTLSX509CAFile or GnuTLSPGPKeyringFile directives?");
1261                ret = HTTP_FORBIDDEN;
1262                goto exit;
1263        }
1264
1265        /* TODO: X509 CRL Verification. */
1266        /* May add later if anyone needs it.
1267         */
1268        /* ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size); */
1269
1270        cur_time = apr_time_now();
1271
1272        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
1273                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1274                              "GnuTLS: Could not find Signer for Peer Certificate");
1275        }
1276
1277        if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
1278                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1279                              "GnuTLS: Peer's Certificate signer is not a CA");
1280        }
1281
1282        if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
1283                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1284                              "GnuTLS: Peer's Certificate is using insecure algorithms");
1285        }
1286
1287        if (status & GNUTLS_CERT_EXPIRED
1288            || status & GNUTLS_CERT_NOT_ACTIVATED) {
1289                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1290                              "GnuTLS: Peer's Certificate signer is expired or not yet activated");
1291        }
1292
1293        if (status & GNUTLS_CERT_INVALID) {
1294                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1295                              "GnuTLS: Peer Certificate is invalid.");
1296        } else if (status & GNUTLS_CERT_REVOKED) {
1297                ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
1298                              "GnuTLS: Peer Certificate is revoked.");
1299        }
1300
1301        if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509)
1302                mgs_add_common_cert_vars(r, cert.x509[0], 1,
1303                                         ctxt->
1304                                         sc->export_certificates_enabled);
1305        else if (gnutls_certificate_type_get(ctxt->session) ==
1306                 GNUTLS_CRT_OPENPGP)
1307                mgs_add_common_pgpcert_vars(r, cert.pgp, 1,
1308                                            ctxt->
1309                                            sc->export_certificates_enabled);
1310
1311        {
1312                /* days remaining */
1313                unsigned long remain =
1314                    (apr_time_sec(expiration_time) -
1315                     apr_time_sec(cur_time)) / 86400;
1316                apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
1317                               apr_psprintf(r->pool, "%lu", remain));
1318        }
1319
1320        if (status == 0) {
1321                apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1322                               "SUCCESS");
1323                ret = OK;
1324        } else {
1325                apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY",
1326                               "FAILED");
1327                if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
1328                        ret = OK;
1329                else
1330                        ret = HTTP_FORBIDDEN;
1331        }
1332
1333      exit:
1334        if (gnutls_certificate_type_get(ctxt->session) == GNUTLS_CRT_X509) {
1335                int i;
1336                for (i = 0; i < ch_size; i++) {
1337                        gnutls_x509_crt_deinit(cert.x509[i]);
1338                }
1339        } else if (gnutls_certificate_type_get(ctxt->session) ==
1340                   GNUTLS_CRT_OPENPGP)
1341                gnutls_openpgp_crt_deinit(cert.pgp);
1342        return ret;
1343
1344
1345}
Note: See TracBrowser for help on using the repository browser.