summaryrefslogtreecommitdiffstatsabout
path: root/src/gnutls_hooks.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gnutls_hooks.c')
-rw-r--r--src/gnutls_hooks.c990
1 files changed, 598 insertions, 392 deletions
diff --git a/src/gnutls_hooks.c b/src/gnutls_hooks.c
index 8452d36..f776def 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,16 @@ 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 cert,
40 int side, int export_certificates_enabled);
41
36static apr_status_t mgs_cleanup_pre_config(void *data) 42static apr_status_t mgs_cleanup_pre_config(void *data)
37{ 43{
38 gnutls_global_deinit(); 44 gnutls_global_deinit();
@@ -40,20 +46,21 @@ static apr_status_t mgs_cleanup_pre_config(void *data)
40} 46}
41 47
42#if MOD_GNUTLS_DEBUG 48#if MOD_GNUTLS_DEBUG
43static void gnutls_debug_log_all( int level, const char* str) 49static void gnutls_debug_log_all(int level, const char *str)
44{ 50{
45 apr_file_printf(debug_log_fp, "<%d> %s\n", level, str); 51 apr_file_printf(debug_log_fp, "<%d> %s\n", level, str);
46} 52}
47#endif 53#endif
48 54
49int mgs_hook_pre_config(apr_pool_t * pconf, 55int
50 apr_pool_t * plog, apr_pool_t * ptemp) 56mgs_hook_pre_config(apr_pool_t * pconf,
57 apr_pool_t * plog, apr_pool_t * ptemp)
51{ 58{
52 59
53#if APR_HAS_THREADS 60#if APR_HAS_THREADS
54 ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_is_threaded); 61 ap_mpm_query(AP_MPMQ_IS_THREADED, &mpm_is_threaded);
55 if (mpm_is_threaded) { 62 if (mpm_is_threaded) {
56 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); 63 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
57 } 64 }
58#else 65#else
59 mpm_is_threaded = 0; 66 mpm_is_threaded = 0;
@@ -62,11 +69,12 @@ int mgs_hook_pre_config(apr_pool_t * pconf,
62 gnutls_global_init(); 69 gnutls_global_init();
63 70
64 apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config, 71 apr_pool_cleanup_register(pconf, NULL, mgs_cleanup_pre_config,
65 apr_pool_cleanup_null); 72 apr_pool_cleanup_null);
66 73
67#if MOD_GNUTLS_DEBUG 74#if MOD_GNUTLS_DEBUG
68 apr_file_open(&debug_log_fp, "/tmp/gnutls_debug", 75 apr_file_open(&debug_log_fp, "/tmp/gnutls_debug",
69 APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, pconf); 76 APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
77 pconf);
70 78
71 gnutls_global_set_log_level(9); 79 gnutls_global_set_log_level(9);
72 gnutls_global_set_log_function(gnutls_debug_log_all); 80 gnutls_global_set_log_function(gnutls_debug_log_all);
@@ -76,38 +84,38 @@ int mgs_hook_pre_config(apr_pool_t * pconf,
76} 84}
77 85
78 86
79static gnutls_datum load_params(const char* file, server_rec* s, 87static gnutls_datum
80 apr_pool_t* pool) 88load_params(const char *file, server_rec * s, apr_pool_t * pool)
81{ 89{
82 gnutls_datum ret = { NULL, 0 }; 90 gnutls_datum ret = { NULL, 0 };
83 apr_file_t* fp; 91 apr_file_t *fp;
84 apr_finfo_t finfo; 92 apr_finfo_t finfo;
85 apr_status_t rv; 93 apr_status_t rv;
86 apr_size_t br = 0; 94 apr_size_t br = 0;
87 95
88 rv = apr_file_open(&fp, file, APR_READ|APR_BINARY, APR_OS_DEFAULT, 96 rv = apr_file_open(&fp, file, APR_READ | APR_BINARY, APR_OS_DEFAULT,
89 pool); 97 pool);
90 if (rv != APR_SUCCESS) { 98 if (rv != APR_SUCCESS) {
91 ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, 99 ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
92 "GnuTLS failed to load params file at: %s", file); 100 "GnuTLS failed to load params file at: %s. Will use internal params.", file);
93 return ret; 101 return ret;
94 } 102 }
95 103
96 rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp); 104 rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, fp);
97 105
98 if (rv != APR_SUCCESS) { 106 if (rv != APR_SUCCESS) {
99 ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, 107 ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
100 "GnuTLS failed to stat params file at: %s", file); 108 "GnuTLS failed to stat params file at: %s", file);
101 return ret; 109 return ret;
102 } 110 }
103 111
104 ret.data = apr_palloc(pool, finfo.size+1); 112 ret.data = apr_palloc(pool, finfo.size + 1);
105 rv = apr_file_read_full(fp, ret.data, finfo.size, &br); 113 rv = apr_file_read_full(fp, ret.data, finfo.size, &br);
106 114
107 if (rv != APR_SUCCESS) { 115 if (rv != APR_SUCCESS) {
108 ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s, 116 ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
109 "GnuTLS failed to read params file at: %s", file); 117 "GnuTLS failed to read params file at: %s", file);
110 return ret; 118 return ret;
111 } 119 }
112 apr_file_close(fp); 120 apr_file_close(fp);
113 ret.data[br] = '\0'; 121 ret.data[br] = '\0';
@@ -116,12 +124,89 @@ static gnutls_datum load_params(const char* file, server_rec* s,
116 return ret; 124 return ret;
117} 125}
118 126
119int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog, 127/* We don't support openpgp certificates, yet */
120 apr_pool_t * ptemp, 128const static int cert_type_prio[2] = { GNUTLS_CRT_X509, 0 };
121 server_rec * base_server) 129
130static int mgs_select_virtual_server_cb( gnutls_session_t session)
131{
132 mgs_handle_t *ctxt;
133 mgs_srvconf_rec *tsc;
134 int ret;
135
136 ctxt = gnutls_transport_get_ptr(session);
137
138 /* find the virtual server */
139 tsc = mgs_find_sni_server(session);
140
141 if (tsc != NULL)
142 ctxt->sc = tsc;
143
144 gnutls_certificate_server_set_request(session,
145 ctxt->sc->client_verify_mode);
146
147 /* set the new server credentials
148 */
149
150 gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
151 ctxt->sc->certs);
152
153 gnutls_credentials_set(session, GNUTLS_CRD_ANON,
154 ctxt->sc->anon_creds);
155
156 if ( ctxt->sc->srp_tpasswd_conf_file != NULL && ctxt->sc->srp_tpasswd_file != NULL) {
157 gnutls_credentials_set(session, GNUTLS_CRD_SRP,
158 ctxt->sc->srp_creds);
159 }
160
161 /* update the priorities - to avoid negotiating a ciphersuite that is not
162 * enabled on this virtual server. Note that here we ignore the version
163 * negotiation.
164 */
165 ret = gnutls_priority_set( session, ctxt->sc->priorities);
166 gnutls_certificate_type_set_priority( session, cert_type_prio);
167
168
169 /* actually it shouldn't fail since we have checked at startup */
170 if (ret < 0) return ret;
171
172 /* allow separate caches per virtual host. Actually allowing the same is not
173 * a good idea, especially if they have different security requirements.
174 */
175 mgs_cache_session_init(ctxt);
176
177 return 0;
178}
179
180static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st * ret)
181{
182 mgs_handle_t *ctxt;
183
184 ctxt = gnutls_transport_get_ptr(session);
185
186 ret->type = GNUTLS_CRT_X509;
187 ret->ncerts = 1;
188 ret->deinit_all = 0;
189
190 ret->cert.x509 = &ctxt->sc->cert_x509;
191 ret->key.x509 = ctxt->sc->privkey_x509;
192 return 0;
193}
194
195const char static_dh_params[] = "-----BEGIN DH PARAMETERS-----\n"
196"MIIBBwKCAQCsa9tBMkqam/Fm3l4TiVgvr3K2ZRmH7gf8MZKUPbVgUKNzKcu0oJnt\n"
197"gZPgdXdnoT3VIxKrSwMxDc1/SKnaBP1Q6Ag5ae23Z7DPYJUXmhY6s2YaBfvV+qro\n"
198"KRipli8Lk7hV+XmT7Jde6qgNdArb9P90c1nQQdXDPqcdKB5EaxR3O8qXtDoj+4AW\n"
199"dr0gekNsZIHx0rkHhxdGGludMuaI+HdIVEUjtSSw1X1ep3onddLs+gMs+9v1L7N4\n"
200"YWAnkATleuavh05zA85TKZzMBBx7wwjYKlaY86jQw4JxrjX46dv7tpS1yAPYn3rk\n"
201"Nd4jbVJfVHWbZeNy/NaO8g+nER+eSv9zAgEC\n"
202"-----END DH PARAMETERS-----\n";
203
204int
205mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
206 apr_pool_t * ptemp, server_rec * base_server)
122{ 207{
123 int rv; 208 int rv;
124 int data_len; 209 size_t data_len;
125 server_rec *s; 210 server_rec *s;
126 gnutls_dh_params_t dh_params; 211 gnutls_dh_params_t dh_params;
127 gnutls_rsa_params_t rsa_params; 212 gnutls_rsa_params_t rsa_params;
@@ -130,118 +215,140 @@ int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
130 void *data = NULL; 215 void *data = NULL;
131 int first_run = 0; 216 int first_run = 0;
132 const char *userdata_key = "mgs_init"; 217 const char *userdata_key = "mgs_init";
133 218
134 apr_pool_userdata_get(&data, userdata_key, base_server->process->pool); 219 apr_pool_userdata_get(&data, userdata_key, base_server->process->pool);
135 if (data == NULL) { 220 if (data == NULL) {
136 first_run = 1; 221 first_run = 1;
137 apr_pool_userdata_set((const void *)1, userdata_key, 222 apr_pool_userdata_set((const void *) 1, userdata_key,
138 apr_pool_cleanup_null, 223 apr_pool_cleanup_null,
139 base_server->process->pool); 224 base_server->process->pool);
140 } 225 }
141 226
142 227
143 { 228 {
144 gnutls_datum pdata; 229 gnutls_datum pdata;
145 apr_pool_t* tpool; 230 apr_pool_t *tpool;
146 s = base_server; 231 s = base_server;
147 sc_base = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, 232 sc_base =
148 &gnutls_module); 233 (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
149 234 &gnutls_module);
150 apr_pool_create(&tpool, p); 235
151 236 apr_pool_create(&tpool, p);
152 gnutls_dh_params_init(&dh_params); 237
153 238 gnutls_dh_params_init(&dh_params);
154 pdata = load_params(sc_base->dh_params_file, s, tpool); 239
155 240 pdata = load_params(sc_base->dh_params_file, s, tpool);
156 if (pdata.size != 0) { 241
157 rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata, 242 if (pdata.size != 0) {
158 GNUTLS_X509_FMT_PEM); 243 rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata,
159 if (rv != 0) { 244 GNUTLS_X509_FMT_PEM);
160 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, 245 if (rv != 0) {
161 "GnuTLS: Unable to load DH Params: (%d) %s", 246 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
162 rv, gnutls_strerror(rv)); 247 "GnuTLS: Unable to load DH Params: (%d) %s",
163 exit(rv); 248 rv, gnutls_strerror(rv));
164 } 249 exit(rv);
165 } 250 }
166 else { 251 } else {
167 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s, 252 /* If the file does not exist use internal parameters
168 "GnuTLS: Unable to load DH Params." 253 */
169 " Shutting Down."); 254 pdata.data = (void*)static_dh_params;
170 exit(-1); 255 pdata.size = sizeof( static_dh_params);
171 } 256 rv = gnutls_dh_params_import_pkcs3(dh_params, &pdata,
172 apr_pool_clear(tpool); 257 GNUTLS_X509_FMT_PEM);
173 258
174 gnutls_rsa_params_init(&rsa_params); 259 if (rv < 0) {
175 260 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
176 pdata = load_params(sc_base->rsa_params_file, s, tpool); 261 "GnuTLS: Unable to load internal DH Params."
177 262 " Shutting down.");
178 if (pdata.size != 0) { 263 exit(-1);
179 rv = gnutls_rsa_params_import_pkcs1(rsa_params, &pdata,
180 GNUTLS_X509_FMT_PEM);
181 if (rv != 0) {
182 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
183 "GnuTLS: Unable to load RSA Params: (%d) %s",
184 rv, gnutls_strerror(rv));
185 exit(rv);
186 }
187 }
188 else {
189 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
190 "GnuTLS: Unable to load RSA Params."
191 " Shutting Down.");
192 exit(-1);
193 }
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 } 264 }
221 265 }
222 if (sc->privkey_x509 == NULL && sc->enabled == GNUTLS_ENABLED_TRUE) { 266 apr_pool_clear(tpool);
223 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, 267
224 "[GnuTLS] - Host '%s:%d' is missing a " 268 rsa_params = NULL;
225 "Private Key File!", 269
226 s->server_hostname, s->port); 270 pdata = load_params(sc_base->rsa_params_file, s, tpool);
227 exit(-1); 271
272 if (pdata.size != 0) {
273 gnutls_rsa_params_init(&rsa_params);
274 rv = gnutls_rsa_params_import_pkcs1(rsa_params, &pdata,
275 GNUTLS_X509_FMT_PEM);
276 if (rv != 0) {
277 ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, s,
278 "GnuTLS: Unable to load RSA Params: (%d) %s",
279 rv, gnutls_strerror(rv));
280 exit(rv);
281 }
282 }
283 /* not an error but RSA-EXPORT ciphersuites are not available
284 */
285
286 apr_pool_destroy(tpool);
287 rv = mgs_cache_post_config(p, s, sc_base);
288 if (rv != 0) {
289 ap_log_error(APLOG_MARK, APLOG_STARTUP, rv, s,
290 "GnuTLS: Post Config for GnuTLSCache Failed."
291 " Shutting Down.");
292 exit(-1);
293 }
294
295 for (s = base_server; s; s = s->next) {
296 sc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
297 &gnutls_module);
298 sc->cache_type = sc_base->cache_type;
299 sc->cache_config = sc_base->cache_config;
300
301 if (rsa_params != NULL)
302 gnutls_certificate_set_rsa_export_params(sc->certs,
303 rsa_params);
304 gnutls_certificate_set_dh_params(sc->certs, dh_params);
305
306 gnutls_anon_set_server_dh_params( sc->anon_creds, dh_params);
307
308 gnutls_certificate_server_set_retrieve_function(sc->certs,
309 cert_retrieve_fn);
310
311 if ( sc->srp_tpasswd_conf_file != NULL && sc->srp_tpasswd_file != NULL) {
312 gnutls_srp_set_server_credentials_file( sc->srp_creds,
313 sc->srp_tpasswd_file, sc->srp_tpasswd_conf_file);
228 } 314 }
229 315
230 rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509, 316 if (sc->cert_x509 == NULL
231 GNUTLS_OID_X520_COMMON_NAME, 0, 0, 317 && sc->enabled == GNUTLS_ENABLED_TRUE) {
232 NULL, &data_len); 318 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
233 319 "[GnuTLS] - Host '%s:%d' is missing a "
234 if (data_len < 1) { 320 "Certificate File!", s->server_hostname,
235 sc->enabled = GNUTLS_ENABLED_FALSE; 321 s->port);
236 sc->cert_cn = NULL; 322 exit(-1);
237 continue; 323 }
324
325 if (sc->privkey_x509 == NULL
326 && sc->enabled == GNUTLS_ENABLED_TRUE) {
327 ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s,
328 "[GnuTLS] - Host '%s:%d' is missing a "
329 "Private Key File!",
330 s->server_hostname, s->port);
331 exit(-1);
332 }
333
334 if (sc->enabled == GNUTLS_ENABLED_TRUE) {
335 rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509,
336 GNUTLS_OID_X520_COMMON_NAME,
337 0, 0, NULL, &data_len);
338
339 if (data_len < 1) {
340 sc->enabled = GNUTLS_ENABLED_FALSE;
341 sc->cert_cn = NULL;
342 continue;
343 }
344
345 sc->cert_cn = apr_palloc(p, data_len);
346 rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509,
347 GNUTLS_OID_X520_COMMON_NAME,
348 0, 0, sc->cert_cn,
349 &data_len);
238 } 350 }
239 351 }
240 sc->cert_cn = apr_palloc(p, data_len);
241 rv = gnutls_x509_crt_get_dn_by_oid(sc->cert_x509,
242 GNUTLS_OID_X520_COMMON_NAME, 0, 0,
243 sc->cert_cn, &data_len);
244 }
245 } 352 }
246 353
247 ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION); 354 ap_add_version_component(p, "mod_gnutls/" MOD_GNUTLS_VERSION);
@@ -249,34 +356,32 @@ int mgs_hook_post_config(apr_pool_t * p, apr_pool_t * plog,
249 return OK; 356 return OK;
250} 357}
251 358
252void mgs_hook_child_init(apr_pool_t *p, server_rec *s) 359void mgs_hook_child_init(apr_pool_t * p, server_rec * s)
253{ 360{
254 apr_status_t rv = APR_SUCCESS; 361 apr_status_t rv = APR_SUCCESS;
255 mgs_srvconf_rec *sc = ap_get_module_config(s->module_config, 362 mgs_srvconf_rec *sc = ap_get_module_config(s->module_config,
256 &gnutls_module); 363 &gnutls_module);
257 364
258 if (sc->cache_type != mgs_cache_none) { 365 if (sc->cache_type != mgs_cache_none) {
259 rv = mgs_cache_child_init(p, s, sc); 366 rv = mgs_cache_child_init(p, s, sc);
260 if(rv != APR_SUCCESS) { 367 if (rv != APR_SUCCESS) {
261 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, 368 ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
262 "[GnuTLS] - Failed to run Cache Init"); 369 "[GnuTLS] - Failed to run Cache Init");
263 } 370 }
264 } 371 } else {
265 else { 372 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
266 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s, 373 "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache");
267 "[GnuTLS] - No Cache Configured. Hint: GnuTLSCache");
268 } 374 }
269} 375}
270 376
271const char *mgs_hook_http_scheme(const request_rec * r) 377const char *mgs_hook_http_scheme(const request_rec * r)
272{ 378{
273 mgs_srvconf_rec *sc = 379 mgs_srvconf_rec *sc =
274 (mgs_srvconf_rec *) ap_get_module_config(r->server-> 380 (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config,
275 module_config, 381 &gnutls_module);
276 &gnutls_module);
277 382
278 if (sc->enabled == GNUTLS_ENABLED_FALSE) { 383 if (sc->enabled == GNUTLS_ENABLED_FALSE) {
279 return NULL; 384 return NULL;
280 } 385 }
281 386
282 return "https"; 387 return "https";
@@ -285,12 +390,11 @@ const char *mgs_hook_http_scheme(const request_rec * r)
285apr_port_t mgs_hook_default_port(const request_rec * r) 390apr_port_t mgs_hook_default_port(const request_rec * r)
286{ 391{
287 mgs_srvconf_rec *sc = 392 mgs_srvconf_rec *sc =
288 (mgs_srvconf_rec *) ap_get_module_config(r->server-> 393 (mgs_srvconf_rec *) ap_get_module_config(r->server->module_config,
289 module_config, 394 &gnutls_module);
290 &gnutls_module);
291 395
292 if (sc->enabled == GNUTLS_ENABLED_FALSE) { 396 if (sc->enabled == GNUTLS_ENABLED_FALSE) {
293 return 0; 397 return 0;
294 } 398 }
295 399
296 return 443; 400 return 443;
@@ -299,88 +403,87 @@ apr_port_t mgs_hook_default_port(const request_rec * r)
299#define MAX_HOST_LEN 255 403#define MAX_HOST_LEN 255
300 404
301#if USING_2_1_RECENT 405#if USING_2_1_RECENT
302typedef struct 406typedef struct {
303{
304 mgs_handle_t *ctxt; 407 mgs_handle_t *ctxt;
305 mgs_srvconf_rec *sc; 408 mgs_srvconf_rec *sc;
306 const char* sni_name; 409 const char *sni_name;
307} vhost_cb_rec; 410} vhost_cb_rec;
308 411
309static int vhost_cb (void* baton, conn_rec* conn, server_rec* s) 412static int vhost_cb(void *baton, conn_rec * conn, server_rec * s)
310{ 413{
311 mgs_srvconf_rec *tsc; 414 mgs_srvconf_rec *tsc;
312 vhost_cb_rec* x = baton; 415 vhost_cb_rec *x = baton;
313 416
314 tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, 417 tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
315 &gnutls_module); 418 &gnutls_module);
316 419
317 if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) { 420 if (tsc->enabled != GNUTLS_ENABLED_TRUE || tsc->cert_cn == NULL) {
318 return 0; 421 return 0;
319 } 422 }
320 423
321 /* The CN can contain a * -- this will match those too. */ 424 /* The CN can contain a * -- this will match those too. */
322 if (ap_strcasecmp_match(x->sni_name, tsc->cert_cn) == 0) { 425 if (ap_strcasecmp_match(x->sni_name, tsc->cert_cn) == 0) {
323 /* found a match */ 426 /* found a match */
324#if MOD_GNUTLS_DEBUG 427#if MOD_GNUTLS_DEBUG
325 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 428 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
326 x->ctxt->c->base_server, 429 x->ctxt->c->base_server,
327 "GnuTLS: Virtual Host CB: " 430 "GnuTLS: Virtual Host CB: "
328 "'%s' == '%s'", tsc->cert_cn, x->sni_name); 431 "'%s' == '%s'", tsc->cert_cn, x->sni_name);
329#endif 432#endif
330 /* Because we actually change the server used here, we need to reset 433 /* Because we actually change the server used here, we need to reset
331 * things like ClientVerify. 434 * things like ClientVerify.
332 */ 435 */
333 x->sc = tsc; 436 x->sc = tsc;
334 /* Shit. Crap. Dammit. We *really* should rehandshake here, as our 437 /* Shit. Crap. Dammit. We *really* should rehandshake here, as our
335 * certificate structure *should* change when the server changes. 438 * certificate structure *should* change when the server changes.
336 * acccckkkkkk. 439 * acccckkkkkk.
337 */ 440 */
338 return 1; 441 return 1;
339 } 442 }
340 return 0; 443 return 0;
341} 444}
342#endif 445#endif
343 446
344mgs_srvconf_rec* mgs_find_sni_server(gnutls_session_t session) 447mgs_srvconf_rec *mgs_find_sni_server(gnutls_session_t session)
345{ 448{
346 int rv; 449 int rv;
347 int sni_type; 450 unsigned int sni_type;
348 int data_len = MAX_HOST_LEN; 451 size_t data_len = MAX_HOST_LEN;
349 char sni_name[MAX_HOST_LEN]; 452 char sni_name[MAX_HOST_LEN];
350 mgs_handle_t *ctxt; 453 mgs_handle_t *ctxt;
351#if USING_2_1_RECENT 454#if USING_2_1_RECENT
352 vhost_cb_rec cbx; 455 vhost_cb_rec cbx;
353#else 456#else
354 server_rec* s; 457 server_rec *s;
355 mgs_srvconf_rec *tsc; 458 mgs_srvconf_rec *tsc;
356#endif 459#endif
357 460
358 ctxt = gnutls_transport_get_ptr(session); 461 ctxt = gnutls_transport_get_ptr(session);
359 462
360 sni_type = gnutls_certificate_type_get(session); 463 sni_type = gnutls_certificate_type_get(session);
361 if (sni_type != GNUTLS_CRT_X509) { 464 if (sni_type != GNUTLS_CRT_X509) {
362 /* In theory, we could support OpenPGP Certificates. Theory != code. */ 465 /* In theory, we could support OpenPGP Certificates. Theory != code. */
363 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, 466 ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
364 ctxt->c->base_server, 467 ctxt->c->base_server,
365 "GnuTLS: Only x509 Certificates are currently supported."); 468 "GnuTLS: Only x509 Certificates are currently supported.");
366 return NULL; 469 return NULL;
367 } 470 }
368 471
369 rv = gnutls_server_name_get(ctxt->session, sni_name, 472 rv = gnutls_server_name_get(ctxt->session, sni_name,
370 &data_len, &sni_type, 0); 473 &data_len, &sni_type, 0);
371 474
372 if (rv != 0) { 475 if (rv != 0) {
373 return NULL; 476 return NULL;
374 } 477 }
375 478
376 if (sni_type != GNUTLS_NAME_DNS) { 479 if (sni_type != GNUTLS_NAME_DNS) {
377 ap_log_error(APLOG_MARK, APLOG_CRIT, 0, 480 ap_log_error(APLOG_MARK, APLOG_CRIT, 0,
378 ctxt->c->base_server, 481 ctxt->c->base_server,
379 "GnuTLS: Unknown type '%d' for SNI: " 482 "GnuTLS: Unknown type '%d' for SNI: "
380 "'%s'", sni_type, sni_name); 483 "'%s'", sni_type, sni_name);
381 return NULL; 484 return NULL;
382 } 485 }
383 486
384 /** 487 /**
385 * Code in the Core already sets up the c->base_server as the base 488 * 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. 489 * for this IP/Port combo. Trust that the core did the 'right' thing.
@@ -389,72 +492,56 @@ mgs_srvconf_rec* mgs_find_sni_server(gnutls_session_t session)
389 cbx.ctxt = ctxt; 492 cbx.ctxt = ctxt;
390 cbx.sc = NULL; 493 cbx.sc = NULL;
391 cbx.sni_name = sni_name; 494 cbx.sni_name = sni_name;
392 495
393 rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx); 496 rv = ap_vhost_iterate_given_conn(ctxt->c, vhost_cb, &cbx);
394 if (rv == 1) { 497 if (rv == 1) {
395 return cbx.sc; 498 return cbx.sc;
396 } 499 }
397#else 500#else
398 for (s = ap_server_conf; s; s = s->next) { 501 for (s = ap_server_conf; s; s = s->next) {
399 502
400 tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config, 503 tsc = (mgs_srvconf_rec *) ap_get_module_config(s->module_config,
401 &gnutls_module); 504 &gnutls_module);
402 if (tsc->enabled != GNUTLS_ENABLED_TRUE) { 505 if (tsc->enabled != GNUTLS_ENABLED_TRUE) {
403 continue; 506 continue;
404 } 507 }
405#if MOD_GNUTLS_DEBUG 508#if MOD_GNUTLS_DEBUG
406 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 509 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
407 ctxt->c->base_server, 510 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, 511 "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)), 512 tsc->cert_cn, rv,
410 (unsigned int)s, (unsigned int)s->next, (unsigned int)tsc); 513 gnutls_pk_algorithm_get_name
411#endif 514 (gnutls_x509_privkey_get_pk_algorithm
412 /* The CN can contain a * -- this will match those too. */ 515 (ctxt->sc->privkey_x509)), (unsigned int) s,
413 if (ap_strcasecmp_match(sni_name, tsc->cert_cn) == 0) { 516 (unsigned int) s->next, (unsigned int) tsc);
517#endif
518 /* The CN can contain a * -- this will match those too. */
519 if (ap_strcasecmp_match(sni_name, tsc->cert_cn) == 0) {
414#if MOD_GNUTLS_DEBUG 520#if MOD_GNUTLS_DEBUG
415 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, 521 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
416 ctxt->c->base_server, 522 ctxt->c->base_server,
417 "GnuTLS: Virtual Host: " 523 "GnuTLS: Virtual Host: "
418 "'%s' == '%s'", tsc->cert_cn, sni_name); 524 "'%s' == '%s'", tsc->cert_cn, sni_name);
419#endif 525#endif
420 return tsc; 526 return tsc;
421 } 527 }
422 } 528 }
423#endif 529#endif
424 return NULL; 530 return NULL;
425} 531}
426 532
427 533
428static int cert_retrieve_fn(gnutls_session_t session, gnutls_retr_st* ret) 534static const int protocol_priority[] = {
429{ 535 GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
430 mgs_handle_t *ctxt; 536
431 mgs_srvconf_rec *tsc;
432
433 ctxt = gnutls_transport_get_ptr(session);
434 537
435 ret->type = GNUTLS_CRT_X509; 538static mgs_handle_t *create_gnutls_handle(apr_pool_t * pool, conn_rec * c)
436 ret->ncerts = 1;
437 ret->deinit_all = 0;
438
439 tsc = mgs_find_sni_server(session);
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{ 539{
453 mgs_handle_t *ctxt; 540 mgs_handle_t *ctxt;
454 mgs_srvconf_rec *sc = 541 mgs_srvconf_rec *sc =
455 (mgs_srvconf_rec *) ap_get_module_config(c->base_server-> 542 (mgs_srvconf_rec *) ap_get_module_config(c->base_server->
456 module_config, 543 module_config,
457 &gnutls_module); 544 &gnutls_module);
458 545
459 ctxt = apr_pcalloc(pool, sizeof(*ctxt)); 546 ctxt = apr_pcalloc(pool, sizeof(*ctxt));
460 ctxt->c = c; 547 ctxt->c = c;
@@ -471,20 +558,20 @@ static mgs_handle_t* create_gnutls_handle(apr_pool_t* pool, conn_rec * c)
471 ctxt->output_length = 0; 558 ctxt->output_length = 0;
472 559
473 gnutls_init(&ctxt->session, GNUTLS_SERVER); 560 gnutls_init(&ctxt->session, GNUTLS_SERVER);
474
475 gnutls_protocol_set_priority(ctxt->session, sc->protocol);
476 gnutls_cipher_set_priority(ctxt->session, sc->ciphers);
477 gnutls_compression_set_priority(ctxt->session, sc->compression);
478 gnutls_kx_set_priority(ctxt->session, sc->key_exchange);
479 gnutls_mac_set_priority(ctxt->session, sc->macs);
480 gnutls_certificate_type_set_priority(ctxt->session, sc->cert_types);
481
482 mgs_cache_session_init(ctxt);
483 561
484 gnutls_credentials_set(ctxt->session, GNUTLS_CRD_CERTIFICATE, ctxt->sc->certs); 562 /* This is not very good as it trades security for compatibility,
563 * but it is the only way to be ultra-portable.
564 */
565 gnutls_session_enable_compatibility_mode( ctxt->session);
566
567 /* because we don't set any default priorities here (we set later at
568 * the user hello callback) we need to at least set this in order for
569 * gnutls to be able to read packets.
570 */
571 gnutls_protocol_set_priority( ctxt->session, protocol_priority);
572
573 gnutls_handshake_set_post_client_hello_function( ctxt->session, mgs_select_virtual_server_cb);
485 574
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; 575 return ctxt;
489} 576}
490 577
@@ -492,218 +579,337 @@ int mgs_hook_pre_connection(conn_rec * c, void *csd)
492{ 579{
493 mgs_handle_t *ctxt; 580 mgs_handle_t *ctxt;
494 mgs_srvconf_rec *sc = 581 mgs_srvconf_rec *sc =
495 (mgs_srvconf_rec *) ap_get_module_config(c->base_server-> 582 (mgs_srvconf_rec *) ap_get_module_config(c->base_server->
496 module_config, 583 module_config,
497 &gnutls_module); 584 &gnutls_module);
498 585
499 if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) { 586 if (!(sc && (sc->enabled == GNUTLS_ENABLED_TRUE))) {
500 return DECLINED; 587 return DECLINED;
501 } 588 }
502 589
503 ctxt = create_gnutls_handle(c->pool, c); 590 ctxt = create_gnutls_handle(c->pool, c);
504 591
505 ap_set_module_config(c->conn_config, &gnutls_module, ctxt); 592 ap_set_module_config(c->conn_config, &gnutls_module, ctxt);
506 593
507 gnutls_transport_set_pull_function(ctxt->session, 594 gnutls_transport_set_pull_function(ctxt->session, mgs_transport_read);
508 mgs_transport_read); 595 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); 596 gnutls_transport_set_ptr(ctxt->session, ctxt);
512 597
513 ctxt->input_filter = ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, 598 ctxt->input_filter =
514 NULL, c); 599 ap_add_input_filter(GNUTLS_INPUT_FILTER_NAME, ctxt, NULL, c);
515 ctxt->output_filter = ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, 600 ctxt->output_filter =
516 NULL, c); 601 ap_add_output_filter(GNUTLS_OUTPUT_FILTER_NAME, ctxt, NULL, c);
517 602
518 return OK; 603 return OK;
519} 604}
520 605
521int mgs_hook_fixups(request_rec *r) 606int mgs_hook_fixups(request_rec * r)
522{ 607{
523 unsigned char sbuf[GNUTLS_MAX_SESSION_ID]; 608 unsigned char sbuf[GNUTLS_MAX_SESSION_ID];
524 char buf[AP_IOBUFSIZE]; 609 char buf[AP_IOBUFSIZE];
525 const char* tmp; 610 const char *tmp;
526 int len; 611 size_t len;
527 mgs_handle_t *ctxt; 612 mgs_handle_t *ctxt;
528 int rv = OK; 613 int rv = OK;
529 614
530 apr_table_t *env = r->subprocess_env; 615 apr_table_t *env = r->subprocess_env;
531 616
532 ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module); 617 ctxt =
618 ap_get_module_config(r->connection->conn_config, &gnutls_module);
533 619
534 if(!ctxt) { 620 if (!ctxt) {
535 return DECLINED; 621 return DECLINED;
536 } 622 }
537 623
538 apr_table_setn(env, "HTTPS", "on"); 624 apr_table_setn(env, "HTTPS", "on");
539 625
540 apr_table_setn(env, "GNUTLS_VERSION_INTERFACE", MOD_GNUTLS_VERSION); 626 apr_table_setn(env, "SSL_VERSION_LIBRARY", "GnuTLS/"LIBGNUTLS_VERSION);
541 apr_table_setn(env, "GNUTLS_VERSION_LIBRARY", LIBGNUTLS_VERSION); 627 apr_table_setn(env, "SSL_VERSION_INTERFACE", "mod_gnutls/"MOD_GNUTLS_VERSION);
542 628
543 apr_table_setn(env, "SSL_PROTOCOL", 629 apr_table_setn(env, "SSL_PROTOCOL",
544 gnutls_protocol_get_name(gnutls_protocol_get_version(ctxt->session))); 630 gnutls_protocol_get_name(gnutls_protocol_get_version
631 (ctxt->session)));
545 632
633 /* should have been called SSL_CIPHERSUITE instead */
546 apr_table_setn(env, "SSL_CIPHER", 634 apr_table_setn(env, "SSL_CIPHER",
547 gnutls_cipher_get_name(gnutls_cipher_get(ctxt->session))); 635 gnutls_cipher_suite_get_name(
636 gnutls_kx_get(ctxt->session), gnutls_cipher_get(ctxt->session),
637 gnutls_mac_get(ctxt->session)));
638
639 apr_table_setn(env, "SSL_COMPRESS_METHOD",
640 gnutls_compression_get_name(gnutls_compression_get
641 (ctxt->session)));
642
643 apr_table_setn(env, "SSL_SRP_USER",
644 gnutls_srp_server_get_username( ctxt->session));
548 645
549 apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE"); 646 if (apr_table_get(env, "SSL_CLIENT_VERIFY") == NULL)
647 apr_table_setn(env, "SSL_CLIENT_VERIFY", "NONE");
550 648
551 tmp = apr_psprintf(r->pool, "%d", 649 unsigned int key_size =
552 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session))); 650 8 * gnutls_cipher_get_key_size(gnutls_cipher_get(ctxt->session));
651 tmp = apr_psprintf(r->pool, "%u", key_size);
553 652
554 apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp); 653 apr_table_setn(env, "SSL_CIPHER_USEKEYSIZE", tmp);
555 654
556 apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp); 655 apr_table_setn(env, "SSL_CIPHER_ALGKEYSIZE", tmp);
557 656
657 apr_table_setn(env, "SSL_CIPHER_EXPORT",
658 (key_size <= 40) ? "true" : "false");
659
558 len = sizeof(sbuf); 660 len = sizeof(sbuf);
559 gnutls_session_get_id(ctxt->session, sbuf, &len); 661 gnutls_session_get_id(ctxt->session, sbuf, &len);
560 tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf)); 662 tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
561 apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp)); 663 apr_table_setn(env, "SSL_SESSION_ID", apr_pstrdup(r->pool, tmp));
562 664
563 /* TODO: There are many other env vars that we need to add */ 665 mgs_add_common_cert_vars(r, ctxt->sc->cert_x509, 0, ctxt->sc->export_certificates_enabled);
564 { 666
565 len = sizeof(buf);
566 gnutls_x509_crt_get_dn(ctxt->sc->cert_x509, buf, &len);
567 apr_table_setn(env, "SSL_SERVER_S_DN", apr_pstrmemdup(r->pool, buf, len));
568
569 len = sizeof(buf);
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; 667 return rv;
574} 668}
575 669
576int mgs_hook_authz(request_rec *r) 670int mgs_hook_authz(request_rec * r)
577{ 671{
578 int rv; 672 int rv;
579 int status;
580 mgs_handle_t *ctxt; 673 mgs_handle_t *ctxt;
581 mgs_dirconf_rec *dc = ap_get_module_config(r->per_dir_config, 674 mgs_dirconf_rec *dc = ap_get_module_config(r->per_dir_config,
582 &gnutls_module); 675 &gnutls_module);
583 676
584 ctxt = ap_get_module_config(r->connection->conn_config, &gnutls_module); 677 ctxt =
585 678 ap_get_module_config(r->connection->conn_config, &gnutls_module);
679
586 if (!ctxt) { 680 if (!ctxt) {
587 return DECLINED; 681 return DECLINED;
588 } 682 }
589 683
590 if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) { 684 if (dc->client_verify_mode == GNUTLS_CERT_IGNORE) {
591 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 685 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
592 "GnuTLS: Directory set to Ignore Client Certificate!"); 686 "GnuTLS: Directory set to Ignore Client Certificate!");
593 } 687 } else {
594 else { 688 if (ctxt->sc->client_verify_mode < dc->client_verify_mode) {
595 if (ctxt->sc->client_verify_mode < dc->client_verify_mode) { 689 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
596 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 690 "GnuTLS: Attempting to rehandshake with peer. %d %d",
597 "GnuTLS: Attempting to rehandshake with peer. %d %d", 691 ctxt->sc->client_verify_mode,
598 ctxt->sc->client_verify_mode, dc->client_verify_mode); 692 dc->client_verify_mode);
599 693
600 gnutls_certificate_server_set_request(ctxt->session, 694 gnutls_certificate_server_set_request(ctxt->session,
601 dc->client_verify_mode); 695 dc->client_verify_mode);
602 696
603 if (mgs_rehandshake(ctxt) != 0) { 697 if (mgs_rehandshake(ctxt) != 0) {
604 return HTTP_FORBIDDEN; 698 return HTTP_FORBIDDEN;
605 } 699 }
606 } 700 } 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 701#if MOD_GNUTLS_DEBUG
609 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 702 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
610 "GnuTLS: Peer is set to IGNORE"); 703 "GnuTLS: Peer is set to IGNORE");
611#endif 704#endif
612 } 705 } else {
613 else { 706 rv = mgs_cert_verify(r, ctxt);
614 rv = mgs_cert_verify(r, ctxt); 707 if (rv != DECLINED) {
615 if (rv != DECLINED) { 708 return rv;
616 return rv; 709 }
617 } 710 }
618 } 711 }
619 712
713 return DECLINED;
714}
715
716/* variables that are not sent by default:
717 *
718 * SSL_CLIENT_CERT string PEM-encoded client certificate
719 * SSL_SERVER_CERT string PEM-encoded client certificate
720 */
620 721
621static int mgs_cert_verify(request_rec *r, mgs_handle_t *ctxt) 722/* side is either 0 for SERVER or 1 for CLIENT
723 */
724#define MGS_SIDE ((side==0)?"SSL_SERVER":"SSL_CLIENT")
725static void
726mgs_add_common_cert_vars(request_rec * r, gnutls_x509_crt cert, int side, int export_certificates_enabled)
622{ 727{
623 const gnutls_datum_t* cert_list; 728 unsigned char sbuf[64]; /* buffer to hold serials */
624 int cert_list_size; 729 char buf[AP_IOBUFSIZE];
625 gnutls_x509_crt_t cert; 730 const char *tmp;
731 size_t len;
732 int alg;
733
734 apr_table_t *env = r->subprocess_env;
626 735
736 if (export_certificates_enabled != 0) {
737 char cert_buf[10*1024];
738 len = sizeof(cert_buf);
739
740 if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_PEM, cert_buf, &len) >= 0)
741 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_CERT", NULL),
742 apr_pstrmemdup(r->pool, cert_buf, len));
743
744 }
627 745
628 cert_list = gnutls_certificate_get_peers(ctxt->session, &cert_list_size); 746 len = sizeof(buf);
747 gnutls_x509_crt_get_dn(cert, buf, &len);
748 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_S_DN", NULL),
749 apr_pstrmemdup(r->pool, buf, len));
750
751 len = sizeof(buf);
752 gnutls_x509_crt_get_issuer_dn(cert, buf, &len);
753 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_I_DN", NULL),
754 apr_pstrmemdup(r->pool, buf, len));
755
756 len = sizeof(sbuf);
757 gnutls_x509_crt_get_serial(cert, sbuf, &len);
758 tmp = mgs_session_id2sz(sbuf, len, buf, sizeof(buf));
759 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_M_SERIAL", NULL),
760 apr_pstrdup(r->pool, tmp));
761
762 tmp =
763 mgs_time2sz(gnutls_x509_crt_get_expiration_time
764 (cert), buf, sizeof(buf));
765 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_END", NULL),
766 apr_pstrdup(r->pool, tmp));
767
768 tmp =
769 mgs_time2sz(gnutls_x509_crt_get_activation_time
770 (cert), buf, sizeof(buf));
771 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_V_START", NULL),
772 apr_pstrdup(r->pool, tmp));
773
774 alg = gnutls_x509_crt_get_signature_algorithm( cert);
775 if (alg >= 0) {
776 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_SIG", NULL),
777 gnutls_sign_algorithm_get_name( alg));
778 }
779
780 alg = gnutls_x509_crt_get_pk_algorithm( cert, NULL);
781 if (alg >= 0) {
782 apr_table_setn(env, apr_pstrcat(r->pool, MGS_SIDE, "_A_KEY", NULL),
783 gnutls_pk_algorithm_get_name( alg));
784 }
785
786
787}
788
789
790static int mgs_cert_verify(request_rec * r, mgs_handle_t * ctxt)
791{
792 const gnutls_datum_t *cert_list;
793 unsigned int cert_list_size, status, expired;
794 int rv, ret;
795 gnutls_x509_crt_t cert;
796 apr_time_t activation_time, expiration_time, cur_time;
797
798 cert_list =
799 gnutls_certificate_get_peers(ctxt->session, &cert_list_size);
629 800
630 if (cert_list == NULL || cert_list_size == 0) { 801 if (cert_list == NULL || cert_list_size == 0) {
631 /* no certificate provided by the client, but one was required. */ 802 /* 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, 803 */
633 "GnuTLS: Failed to Verify Peer: " 804 if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
634 "Client did not submit a certificate"); 805 return OK;
635 return HTTP_FORBIDDEN; 806
807 /* no certificate provided by the client, but one was required. */
808 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
809 "GnuTLS: Failed to Verify Peer: "
810 "Client did not submit a certificate");
811 return HTTP_FORBIDDEN;
636 } 812 }
637 813
638 if (cert_list_size > 1) { 814 if (cert_list_size > 1) {
639 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 815 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
640 "GnuTLS: Failed to Verify Peer: " 816 "GnuTLS: Failed to Verify Peer: "
641 "Chained Client Certificates are not supported."); 817 "Chained Client Certificates are not supported.");
642 return HTTP_FORBIDDEN; 818 return HTTP_FORBIDDEN;
643 } 819 }
644 820
645 gnutls_x509_crt_init(&cert); 821 gnutls_x509_crt_init(&cert);
646 gnutls_x509_crt_import(cert, &cert_chain[0], GNUTLS_X509_FMT_DER); 822 rv = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
823 if (rv < 0) {
824 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
825 "GnuTLS: Failed to Verify Peer: "
826 "Failed to import peer certificates.");
827 ret = HTTP_FORBIDDEN;
828 goto exit;
829 }
647 830
648 rv = gnutls_x509_crt_verify(cert, ctxt->sc->ca_list, ctxt->sc->ca_list_size, 0, &status); 831 apr_time_ansi_put(&expiration_time,
832 gnutls_x509_crt_get_expiration_time(cert));
833 apr_time_ansi_put(&activation_time,
834 gnutls_x509_crt_get_activation_time(cert));
835
836 rv = gnutls_x509_crt_verify(cert, ctxt->sc->ca_list,
837 ctxt->sc->ca_list_size, 0, &status);
649 838
650 if (rv < 0) { 839 if (rv < 0) {
651 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 840 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
652 "GnuTLS: Failed to Verify Peer: (%d) %s", 841 "GnuTLS: Failed to Verify Peer certificate: (%d) %s",
653 rv, gnutls_strerror(rv)); 842 rv, gnutls_strerror(rv));
654 return HTTP_FORBIDDEN; 843 ret = HTTP_FORBIDDEN;
844 goto exit;
655 } 845 }
656 846
657 if (status < 0) { 847 expired = 0;
658 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 848 cur_time = apr_time_now();
659 "GnuTLS: Peer Status is invalid."); 849 if (activation_time > cur_time) {
660 return HTTP_FORBIDDEN; 850 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
661 } 851 "GnuTLS: Failed to Verify Peer: "
662 852 "Peer Certificate is not yet activated.");
663 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { 853 expired = 1;
664 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
665 "GnuTLS: Could not find Signer for Peer Certificate");
666 }
667
668 if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
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 } 854 }
697 855
698 ap_add_common_vars(r); 856 if (expiration_time < cur_time) {
699 mgs_hook_fixups(r); 857 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
700 status = mgs_authz_lua(r); 858 "GnuTLS: Failed to Verify Peer: "
859 "Peer Certificate is expired.");
860 expired = 1;
861 }
701 862
702 if (status != 0) { 863 if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
703 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, 864 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
704 "GnuTLS: FAILED Authorization Test"); 865 "GnuTLS: Could not find Signer for Peer Certificate");
705 return HTTP_FORBIDDEN;
706 } 866 }
707 867
708} 868 if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
869 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
870 "GnuTLS: Peer's Certificate signer is not a CA");
871 }
872
873 if (status & GNUTLS_CERT_INVALID) {
874 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
875 "GnuTLS: Peer Certificate is invalid.");
876 } else if (status & GNUTLS_CERT_REVOKED) {
877 ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
878 "GnuTLS: Peer Certificate is revoked.");
879 }
880
881 /* TODO: Further Verification. */
882 /* Revocation is X.509 non workable paradigm, I really doubt implementation
883 * is worth doing --nmav
884 */
885/// ret = gnutls_x509_crt_check_revocation(crt, crl_list, crl_list_size);
709 886
887// mgs_hook_fixups(r);
888// rv = mgs_authz_lua(r);
889
890 mgs_add_common_cert_vars(r, cert, 1, ctxt->sc->export_certificates_enabled);
891
892 {
893 /* days remaining */
894 unsigned long remain = (apr_time_sec(expiration_time) - apr_time_sec(cur_time))/86400;
895 apr_table_setn(r->subprocess_env, "SSL_CLIENT_V_REMAIN",
896 apr_psprintf(r->pool, "%lu", remain));
897 }
898
899 if (status == 0 && expired == 0) {
900 apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY", "SUCCESS");
901 ret = OK;
902 } else {
903 apr_table_setn(r->subprocess_env, "SSL_CLIENT_VERIFY", "FAILED");
904 if (ctxt->sc->client_verify_mode == GNUTLS_CERT_REQUEST)
905 ret = OK;
906 else
907 ret = HTTP_FORBIDDEN;
908 }
909
910 exit:
911 gnutls_x509_crt_deinit(cert);
912 return ret;
913
914
915}