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