diff options
author | rcritten <> | 2005-05-17 14:50:16 +0000 |
---|---|---|
committer | rcritten <> | 2005-05-17 14:50:16 +0000 |
commit | f6ecd9df97f9bb1be9b69ae97a78eb54b6599463 (patch) | |
tree | c4c8e184503e99362268f59dfa8d3ce114c18013 /nss_engine_init.c | |
download | mod_nss-f6ecd9df97f9bb1be9b69ae97a78eb54b6599463.tar.gz mod_nss-f6ecd9df97f9bb1be9b69ae97a78eb54b6599463.tar.xz mod_nss-f6ecd9df97f9bb1be9b69ae97a78eb54b6599463.zip |
Initial import of mod_nss
Diffstat (limited to 'nss_engine_init.c')
-rw-r--r-- | nss_engine_init.c | 984 |
1 files changed, 984 insertions, 0 deletions
diff --git a/nss_engine_init.c b/nss_engine_init.c new file mode 100644 index 0000000..8bcb93d --- /dev/null +++ b/nss_engine_init.c @@ -0,0 +1,984 @@ +/* Copyright 2001-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mod_nss.h" +#include "apr_thread_proc.h" + +static SECStatus ownBadCertHandler(void *arg, PRFileDesc * socket); +static SECStatus ownHandshakeCallback(PRFileDesc * socket, void *arg); +static SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg); +static CERTCertificate* FindServerCertFromNickname(const char* name); +SECStatus ssl_AuthCertificate(void *arg, PRFileDesc *socket, PRBool checksig, PRBool isServer); + +/* + * Global variables defined in this file. + */ +char* INTERNAL_TOKEN_NAME = "internal "; + +cipher_properties ciphers_def[ciphernum] = +{ + /* SSL2 cipher suites */ + {"rc4", SSL_EN_RC4_128_WITH_MD5, 0, SSL2}, + {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5, 0, SSL2}, + {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5, 0, SSL2}, + {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, 0, SSL2}, + {"des", SSL_EN_DES_64_CBC_WITH_MD5, 0, SSL2}, + {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5, 0, SSL2}, + /* SSL3/TLS cipher suites */ + {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5, 0, SSL3 | TLS}, + {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA, 0, SSL3 | TLS}, + {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA, 0, SSL3 | TLS}, + {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA, 0, SSL3 | TLS}, + {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5, 0, SSL3 | TLS}, + {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, 0, SSL3 | TLS}, + {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5, 0, SSL3 | TLS}, + {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, 0, SSL3 | TLS}, + {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA, 0, SSL3 | TLS}, + {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, 1, SSL3 | TLS}, + {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, 1, SSL3 | TLS}, + {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA, 1, SSL3 | TLS}, + /* TLS 1.0: Exportable 56-bit Cipher Suites. */ + {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, 0, SSL3 | TLS}, + {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, 0, SSL3 | TLS}, + /* AES ciphers.*/ + {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA, 0, SSL3 | TLS}, + {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA, 0, SSL3 | TLS}, +}; + +static char *version_components[] = { + "SSL_VERSION_PRODUCT", + "SSL_VERSION_INTERFACE", + "SSL_VERSION_LIBRARY", + NULL +}; + +static char *ssl_add_version_component(apr_pool_t *p, + server_rec *s, + char *name) +{ + char *val = ssl_var_lookup(p, s, NULL, NULL, name); + + if (val && *val) { + ap_add_version_component(p, val); + } + + return val; +} + +static void ssl_add_version_components(apr_pool_t *p, + server_rec *s) +{ + char *vals[sizeof(version_components)/sizeof(char *)]; + int i; + + for (i=0; version_components[i]; i++) { + vals[i] = ssl_add_version_component(p, s, + version_components[i]); + } + + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Server: %s, Interface: %s, Library: %s", + AP_SERVER_BASEVERSION, + vals[1], /* SSL_VERSION_INTERFACE */ + vals[2]); /* SSL_VERSION_LIBRARY */ +} + +/* + * Initialize SSL library + */ +static void ssl_init_SSLLibrary(server_rec *s) +{ + SECStatus rv; + SSLModConfigRec *mc = myModConfig(s); + + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Init: %snitializing NSS library", mc->nInitCount == 1 ? "I" : "Re-i"); + + /* Do we need to fire up our password helper? */ + if (mc->nInitCount == 1) { + const char * child_argv[2]; + apr_status_t rv; + + if (mc->pphrase_dialog_helper == NULL && + mc->pphrase_dialog_path == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Neither SSLPassPhraseHelper nor SSLPassPhraseDialog is not set. One or the other is required."); + ssl_die(); + } + + child_argv[0] = mc->pphrase_dialog_helper; + child_argv[1] = NULL; + + rv = apr_procattr_create(&mc->procattr, mc->pPool); + + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "apr_procattr_create() failed APR err: %d.", rv); + ssl_die(); + } + +#if 0 + apr_procattr_io_set(mc->procattr, APR_PARENT_BLOCK, APR_FULL_NONBLOCK, + APR_FULL_NONBLOCK); +#endif + apr_procattr_io_set(mc->procattr, APR_PARENT_BLOCK, APR_PARENT_BLOCK, + APR_FULL_NONBLOCK); + apr_procattr_error_check_set(mc->procattr, 1); + + /* the process inherits our environment, which should allow the + * dynamic loader to find NSPR and NSS. + */ + apr_procattr_cmdtype_set(mc->procattr, APR_PROGRAM_ENV); + + /* We've now spawned our helper process, the actual communication + * with it occurs in nss_engine_pphrase.c. + */ + rv = apr_proc_create(&mc->proc, child_argv[0], child_argv, NULL, mc->procattr, mc->pPool); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "apr_proc_create failed to launch %s APR err: %d.", child_argv[0], rv); + ssl_die(); + } + /* Set a 30-second read/write timeout */ + apr_file_pipe_timeout_set(mc->proc.in, apr_time_from_sec(30)); + apr_file_pipe_timeout_set(mc->proc.out, apr_time_from_sec(30)); + } + + /* Initialize NSPR */ + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); + + /* Set the PKCS #11 strings for the internal token. */ + PK11_ConfigurePKCS11(NULL,NULL,NULL, INTERNAL_TOKEN_NAME, NULL, NULL,NULL,NULL,8,1); + + /* Initialize NSS and open the certificate database read-only. */ + rv = NSS_Initialize(mc->pCertificateDatabase, NULL, NULL, "secmod.db", NSS_INIT_READONLY); + + /* Assuming everything is ok so far, check the cert database password(s). */ + if (rv != SECSuccess || ssl_Init_Tokens(s) != SECSuccess) { + NSS_Shutdown(); + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "NSS initialization failed. Certificate database: %s.", mc->pCertificateDatabase != NULL ? mc->pCertificateDatabase : "not set in configuration"); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + + if (NSS_SetDomesticPolicy() != SECSuccess) { + NSS_Shutdown(); + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "NSS set domestic policy failed on certificate database %s.", mc->pCertificateDatabase); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Initializing SSL Session Cache of size %d. SSL2 timeout = %d, SSL3/TLS timeout = %d.", mc->session_cache_size, mc->session_cache_timeout, mc->ssl3_session_cache_timeout); + SSL_ConfigServerSessionIDCache(mc->session_cache_size, (PRUint32) mc->session_cache_timeout, (PRUint32) mc->ssl3_session_cache_timeout, NULL); + +} + +int ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, + server_rec *base_server) +{ + SSLModConfigRec *mc = myModConfig(base_server); + SSLSrvConfigRec *sc; + server_rec *s; + + mc->nInitCount++; + + /* + * Let us cleanup on restarts and exists + */ + apr_pool_cleanup_register(p, base_server, + ssl_init_ModuleKill, + apr_pool_cleanup_null); + + /* + * Any init round fixes the global config + */ + ssl_config_global_create(base_server); /* just to avoid problems */ + + /* + * Fix up any global settings that aren't in the configuration + */ + if (mc->session_cache_timeout == UNSET) { + mc->session_cache_timeout = SSL_SESSION_CACHE_TIMEOUT; + } + + if (mc->ssl3_session_cache_timeout == UNSET) { + mc->ssl3_session_cache_timeout = SSL3_SESSION_CACHE_TIMEOUT; + } + + if (mc->session_cache_size == UNSET) { + mc->session_cache_size = SSL_SESSION_CACHE_SIZE; + } + + if (mc->pphrase_dialog_type == SSL_PPTYPE_UNSET) { + mc->pphrase_dialog_type = SSL_PPTYPE_BUILTIN; + } + + /* + * try to fix the configuration and open the dedicated SSL + * logfile as early as possible + */ + for (s = base_server; s; s = s->next) { + sc = mySrvConfig(s); + + if (sc->server) { + sc->server->sc = sc; + } + +#ifdef PROXY + if (sc->proxy) { + sc->proxy->sc = sc; + } +#endif + + /* + * Create the server host:port string because we need it a lot + */ + sc->vhost_id = ssl_util_vhostid(p, s); + sc->vhost_id_len = strlen(sc->vhost_id); + + /* Fix up stuff that may not have been set */ + if (sc->enabled == UNSET) { + sc->enabled = FALSE; + } + + if (sc->proxy_enabled == UNSET) { + sc->proxy_enabled = FALSE; + } + } + + ssl_init_SSLLibrary(base_server); + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "done Init: Initializing NSS library"); + + /* Load our layer */ + ssl_io_layer_init(); + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "done layer"); + + /* + * initialize servers + */ + ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server, + "Init: Initializing (virtual) servers for SSL"); + + + for (s = base_server; s; s = s->next) { + sc = mySrvConfig(s); + /* + * Either now skip this server when SSL is disabled for + * it or give out some information about what we're + * configuring. + */ + + /* + * Read the server certificate and key + */ + ssl_init_ConfigureServer(s, p, ptemp, sc); + } + + /* + * Announce mod_ssl and SSL library in HTTP Server field + * as ``mod_ssl/X.X.X OpenSSL/X.X.X'' + */ + ssl_add_version_components(p, base_server); + + return OK; +} + +static void ssl_init_ctx_socket(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ + /* Setup a socket in the context that will be used to model all + * client connections. */ + mctx->model = ssl_io_new_fd(); + mctx->model = SSL_ImportFD(NULL, mctx->model); + + if (SSL_OptionSet(mctx->model, SSL_SECURITY, PR_TRUE) != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "Unable to enable security."); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + + if (SSL_OptionSet(mctx->model, SSL_HANDSHAKE_AS_SERVER, PR_TRUE) + != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "Unable to set SSL server handshake mode."); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + if (SSL_OptionSet(mctx->model, SSL_HANDSHAKE_AS_CLIENT, PR_FALSE) + != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "Unable to disable handshake as client"); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } +} + +static void ssl_init_ctx_protocol(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ + int ssl2, ssl3, tls; + char *lprotocols = NULL; + SECStatus stat; + + ssl2 = ssl3 = tls = 0; + + if (mctx->auth.protocols == NULL) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, + "SSLProtocols not set; using all protocols: SSLv2, SSLv3 and TLSv1"); + ssl2 = ssl3 = tls = 1; + } else { + lprotocols = strdup(mctx->auth.protocols); + ap_str_tolower(lprotocols); + + if (strstr(lprotocols, "all") != NULL) { + ssl2 = ssl3 = tls = 1; + } else { + if (strstr(lprotocols, "sslv2") != NULL) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Enabling SSL2"); + ssl2 = 1; + } + + if (strstr(lprotocols, "sslv3") != NULL) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Enabling SSL3"); + ssl3 = 1; + } + + if (strstr(lprotocols, "tlsv1") != NULL) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Enabling TLS"); + tls = 1; + } + } + free(lprotocols); + } + + stat = SECSuccess; + + if (ssl2 == 1) { + stat = SSL_OptionSet(mctx->model, SSL_ENABLE_SSL2, PR_TRUE); + } else { + stat = SSL_OptionSet(mctx->model, SSL_ENABLE_SSL2, PR_FALSE); + } + + if (stat == SECSuccess) { + if (ssl3 == 1) { + stat = SSL_OptionSet(mctx->model, SSL_ENABLE_SSL3, PR_TRUE); + } else { + stat = SSL_OptionSet(mctx->model, SSL_ENABLE_SSL3, PR_FALSE); + } + } + if (stat == SECSuccess) { + if (tls == 1) { + stat = SSL_OptionSet(mctx->model, SSL_ENABLE_TLS, PR_TRUE); + } else { + stat = SSL_OptionSet(mctx->model, SSL_ENABLE_TLS, PR_FALSE); + } + } + + if (stat != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "SSL protocol initialization failed."); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + + mctx->ssl2 = ssl2; + mctx->ssl3 = ssl3; + mctx->tls = tls; +} + +static void ssl_init_ctx_session_cache(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ +} + +static void ssl_init_ctx_callbacks(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ + if (SSL_AuthCertificateHook(mctx->model, ssl_AuthCertificate, (void *)CERT_GetDefaultCertDB()) != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "SSL_AuthCertificateHook failed."); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + if (SSL_BadCertHook(mctx->model, (SSLBadCertHandler) ownBadCertHandler, NULL) != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "SSL_BadCertHook failed"); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + if (SSL_HandshakeCallback(mctx->model, (SSLHandshakeCallback) ownHandshakeCallback, NULL) != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "SSL_HandshakeCallback failed"); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + if (SSL_GetClientAuthDataHook(mctx->model, NSS_GetClientAuthData, (void *)mctx->nickname) != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "SSL_GetClientAuthDataHook failed"); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } +} + +static void ssl_init_ctx_verify(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ + if (mctx->auth.verify_mode == SSL_CVERIFY_REQUIRE) { + SSL_OptionSet(mctx->model, SSL_REQUEST_CERTIFICATE, PR_TRUE); + SSL_OptionSet(mctx->model, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NO_ERROR); + } else if (mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL) { + SSL_OptionSet(mctx->model, SSL_REQUEST_CERTIFICATE, PR_TRUE); + SSL_OptionSet(mctx->model, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER); + } else { + SSL_OptionSet(mctx->model, SSL_REQUEST_CERTIFICATE, PR_FALSE); + SSL_OptionSet(mctx->model, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER); + } +} + +static int countciphers(PRBool cipher_state[ciphernum], int version) { + int ciphercount = 0; + int i; + + for (i = 0; i < ciphernum; i++) + { + if ((cipher_state[i] == PR_TRUE) && + (ciphers_def[i].version & version)) { + ciphercount++; + } + } + + return ciphercount; +} + +static void ssl_init_ctx_cipher_suite(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ + PRBool cipher_state[ciphernum]; + const char *suite = mctx->auth.cipher_suite; + char * ciphers; + int i; + + /* + * Configure SSL Cipher Suite + */ + if (!suite) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Required value SSLCipherSuite not set."); + ssl_die(); + } + ciphers = strdup(suite); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "Configuring permitted SSL ciphers [%s]", + suite); + + /* Disable all NSS supported cipher suites. This is to prevent any new + * NSS cipher suites from getting automatically and unintentionally + * enabled as a result of the NSS_SetDomesticPolicy() call. This way, + * only the ciphers explicitly specified in the server configuration can + * ever be enabled. + */ + + for (i = 0; i < SSL_NumImplementedCiphers; i++) + { + SSL_CipherPrefSet(mctx->model, SSL_ImplementedCiphers[i], SSL_NOT_ALLOWED); + } + + /* initialize all known ciphers to false */ + for (i=0; i<ciphernum; i++) + { + cipher_state[i] = PR_FALSE; + } + + if (ssl_parse_ciphers(s, ciphers, cipher_state) == -1) { + ssl_die(); + } + + free(ciphers); + + /* See if any ciphers have been enabled for a given protocol */ + if (mctx->ssl2 && countciphers(cipher_state, SSL2) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "SSL2 is enabled but no SSL2 ciphers are enabled."); + ssl_die(); + } + + if (mctx->ssl3 && countciphers(cipher_state, SSL3) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "SSL3 is enabled but no SSL3 ciphers are enabled."); + ssl_die(); + } + + if (mctx->tls && countciphers(cipher_state, TLS) == 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "TLS is enabled but no TLS ciphers are enabled."); + ssl_die(); + } + + /* Finally actually enable the selected ciphers */ + for (i=0; i<ciphernum;i++) { + SSL_CipherPrefSet(mctx->model, ciphers_def[i].num, cipher_state[i]); + } +} + +static void ssl_init_ctx(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ + + ssl_init_ctx_socket(s, p, ptemp, mctx); + + ssl_init_ctx_protocol(s, p, ptemp, mctx); + + ssl_init_ctx_session_cache(s, p, ptemp, mctx); + + ssl_init_ctx_callbacks(s, p, ptemp, mctx); + + ssl_init_ctx_verify(s, p, ptemp, mctx); + + ssl_init_ctx_cipher_suite(s, p, ptemp, mctx); +} + +static void ssl_init_server_certs(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + modnss_ctx_t *mctx) +{ + SECCertTimeValidity certtimestatus; + SECStatus secstatus; + int enforce = 0; // not currently used + + PK11SlotInfo* slot = NULL; + + /* + * Get own certificate and private key. + */ + + if (mctx->nickname == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "No certificate nickname provided."); + ssl_die(); + } + mctx->servercert = FindServerCertFromNickname(mctx->nickname); + + /* Verify the certificate chain. */ + if (mctx->servercert != NULL) { + SECCertificateUsage usage = certificateUsageSSLServer; + + if (CERT_VerifyCertificateNow(CERT_GetDefaultCertDB(), mctx->servercert, PR_TRUE, usage, NULL, NULL) != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Certificate not verified '%s'", mctx->nickname); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + if (enforce) { + ssl_die(); + } + } + } + + if (NULL == mctx->servercert) + { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Certificate not found '%s'", mctx->nickname); + ssl_die(); + } + + if (strchr(mctx->nickname, ':')) + { + char* token = strdup(mctx->nickname); + char* colon = strchr(token, ':'); + if (colon) { + *colon = 0; + slot = PK11_FindSlotByName(token); + if (!slot) { + /* + * Slot not found. This should never happen because we + * already found the cert. + */ + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Slot not found"); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + free(token); + ssl_die(); + } + } + free(token); + } + else { + slot = PK11_GetInternalKeySlot(); + } + + mctx->serverkey = PK11_FindPrivateKeyFromCert(slot, mctx->servercert, NULL); + + if (mctx->serverkey == NULL) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Key not found %s", mctx->nickname); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + + mctx->serverKEAType = NSS_FindCertKEAType(mctx->servercert); + + /* + * Check for certs that are expired or not yet valid and WARN about it + * no need to refuse working - the client gets a warning, but can work + * with the server we could also verify if the certificate is made out + * for the correct hostname but that would require a reverse DNS lookup + * for every virtual server - too expensive? + */ + + certtimestatus = CERT_CheckCertValidTimes(mctx->servercert, PR_Now(), PR_FALSE); + switch (certtimestatus) + { + case secCertTimeValid: + // ok + break; + case secCertTimeExpired: + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Server certificate is expired %s", mctx->nickname); + break; + case secCertTimeNotValidYet: + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Certificate is not valid yet %s", mctx->nickname); + default: + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Unhandled Certificate time type %d for %s", certtimestatus, mctx->nickname); + break; + } + + secstatus = (SECStatus)SSL_SetPKCS11PinArg(mctx->model, NULL); + if (secstatus != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Error setting PKCS11 pin argument: %s", mctx->nickname); + ssl_die(); + } + + secstatus = SSL_ConfigSecureServer(mctx->model, mctx->servercert, mctx->serverkey, mctx->serverKEAType); + if (secstatus != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "SSL error configuring server %s", mctx->nickname); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } + + secstatus = (SECStatus)SSL_HandshakeCallback(mctx->model, (SSLHandshakeCallback)NSSHandshakeCallback, NULL); + if (secstatus != SECSuccess) + { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "SSL error configuring handshake callback %s", mctx->nickname); + ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); + ssl_die(); + } +} + +static void ssl_init_server_ctx(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + SSLSrvConfigRec *sc) +{ + ssl_init_ctx(s, p, ptemp, sc->server); + + ssl_init_server_certs(s, p, ptemp, sc->server); +} + +/* + * Configure a particular server + */ +void ssl_init_ConfigureServer(server_rec *s, + apr_pool_t *p, + apr_pool_t *ptemp, + SSLSrvConfigRec *sc) +{ + if (sc->enabled) { + ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, + "Configuring server for SSL protocol"); + ssl_init_server_ctx(s, p, ptemp, sc); + } + +#ifdef PROXY + if (sc->proxy_enabled) { + ssl_init_proxy_ctx(s, p, ptemp, sc); + } +#endif +} + +void ssl_init_Child(apr_pool_t *p, server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(s); + mc->pid = getpid(); /* only call getpid() once per-process */ +} + +apr_status_t ssl_init_ModuleKill(void *data) +{ + /* + * There is nothing stored at the server level to kill at the moment. + */ + + NSS_Shutdown(); + + return APR_SUCCESS; +} + +/* + * This callback is used when the incoming cert is not valid. + * It should return SECSuccess to accept the cert anyway, SECFailure + * to reject. In this case we always reject. + */ +SECStatus ownBadCertHandler(void *arg, PRFileDesc * socket) +{ + PRErrorCode err = PR_GetError(); + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Bad remote server certificate: %d", err); + + return SECFailure; +} + +/* + * Called by SSL to inform application that the handshake is + * complete. This function is mostly used on the server side of an SSL + * connection, although it is provided for a client as well. + * We don't do anything special. + */ +SECStatus ownHandshakeCallback(PRFileDesc * socket, void *arg) +{ + return SECSuccess; +} + +/* + * Duplicated, non-exported function from NSS that compares 2 certificate + * times. + */ +static PRBool +cert_IsNewer(CERTCertificate *certa, CERTCertificate *certb) +{ + PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now; + SECStatus rv; + PRBool newerbefore, newerafter; + + newerbefore = newerafter = PR_FALSE; + + rv = CERT_GetCertTimes(certa, ¬BeforeA, ¬AfterA); + if ( rv != SECSuccess ) { + return(PR_FALSE); + } + + rv = CERT_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + if ( rv != SECSuccess ) { + return(PR_TRUE); + } + + if ( LL_CMP(notBeforeA, >, notBeforeB) ) { + newerbefore = PR_TRUE; + } + + if ( LL_CMP(notAfterA, >, notAfterB) ) { + newerafter = PR_TRUE; + } + + if ( newerbefore && newerafter ) { + return(PR_TRUE); + } + + if ( ( !newerbefore ) && ( !newerafter ) ) { + return(PR_FALSE); + } + + /* get current UTC time */ + now = PR_Now(); + + if ( newerbefore ) { + /* cert A was issued after cert B, but expires sooner */ + /* if A is expired, then pick B */ + if ( LL_CMP(notAfterA, <, now ) ) { + return(PR_FALSE); + } + return(PR_TRUE); + } else { + /* cert B was issued after cert A, but expires sooner */ + /* if B is expired, then pick A */ + if ( LL_CMP(notAfterB, <, now ) ) { + return(PR_TRUE); + } + return(PR_FALSE); + } +} + +/* + * Given a nickname, find the "best" certificate available for that + * certificate (for the case of multiple CN's with different usages, a + * renewed cert that is not yet valid, etc). The best is defined as the + * newest, valid server certificate. + */ +static CERTCertificate* +FindServerCertFromNickname(const char* name) +{ + CERTCertList* clist; + CERTCertificate* bestcert = NULL; + + CERTCertListNode *cln; + PRUint32 bestCertMatchedUsage = 0; + PRBool bestCertIsValid = PR_FALSE; + + clist = PK11_ListCerts(PK11CertListUser, NULL); + + for (cln = CERT_LIST_HEAD(clist); !CERT_LIST_END(cln,clist); + cln = CERT_LIST_NEXT(cln)) { + CERTCertificate* cert = cln->cert; + const char* nickname = (const char*) cln->appData; + if (!nickname) { + nickname = cert->nickname; + } + if (strcmp(name, nickname) == 0) { + PRUint32 matchedUsage = 0; + PRBool isValid = PR_FALSE; + PRBool swapcert = PR_FALSE; + // We still need to check key usage. Dual-key certs appear + // as 2 certs in the list with different usages. We want to pick + // the "best" one, preferrably the one with certUsageSSLServer. + // Otherwise just return the cert if the nickname matches. + if (CERT_CheckCertUsage(cert, certUsageSSLServer) == SECSuccess) { + matchedUsage = 2; + } else { + if (CERT_CheckCertUsage(cert, certUsageEmailRecipient) == SECSuccess) + { + matchedUsage = 1; + } + } + + if (secCertTimeValid == CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE)) + { + // This is a valid certificate. + isValid = PR_TRUE; + } + if (!bestcert) { + // We didn't have a cert picked yet, automatically choose this + // one. + swapcert = PR_TRUE; + } else { + if (matchedUsage > bestCertMatchedUsage) { + // The cert previously picked didn't have the correct + // usage, but this one does. Choose this one. + swapcert = PR_TRUE; + } else { + if ( (bestCertMatchedUsage == matchedUsage) && + (((PR_FALSE == bestCertIsValid) && (PR_TRUE == isValid)) || + ((PR_TRUE == bestCertIsValid == isValid) && (PR_TRUE == cert_IsNewer(cert, bestcert))))) { + // The cert previously picked was invalid but this one + // is. Or they were both valid but this one is newer. + swapcert = PR_TRUE; + } + } + } + + if (swapcert == PR_TRUE) + { + bestcert = cert; + bestCertMatchedUsage = matchedUsage; + bestCertIsValid = isValid; + } + } + } + if (bestcert) { + bestcert = CERT_DupCertificate(bestcert); + } + if (clist) { + CERT_DestroyCertList(clist); + } + return bestcert; +} + +/* + * Executed automatically when the SSL handshake is completed. + * We don't do anything special here. + */ +SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg) +{ + return SECSuccess; +} + +int ssl_parse_ciphers(server_rec *s, char *ciphers, PRBool cipher_list[ciphernum]) +{ + char * cipher; + PRBool found, active; + int i; + + cipher = ciphers; + + while (ciphers && (strlen(ciphers))) + { + while ((*cipher) && (isspace(*cipher))) + ++cipher; + + switch(*cipher++) + { + case '+': + active = PR_TRUE; + break; + case '-': + active = PR_FALSE; + break; + default: + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "invalid cipher string %s. Format is +cipher1,-cipher2...", cipher - 1); + return -1; + } + + if ((ciphers = strchr(cipher, ','))) { + *ciphers++ = '\0'; + } + + found = PR_FALSE; + + for (i = 0; i < ciphernum; i++) + { + if (!strcasecmp(cipher, ciphers_def[i].name)) { + cipher_list[i] = active; + found = PR_TRUE; + break; + } + } + + if (found == PR_FALSE) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "Unknown cipher %s", cipher); + } + + if (ciphers) { + cipher = ciphers; + } + } + + return 0; +} |