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