diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index abef4b3..99e8610 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -138,6 +138,9 @@ static const command_rec ssl_config_cmds[] = { SSL_CMD_ALL(VerifyDepth, TAKE1, "SSL Client verify depth " "('N' - number of intermediate certificates)") + SSL_CMD_ALL(VerifyAcceptExpiredClient, TAKE1, + "SSL Accept expired Client certificate " + "('on' 'off')") SSL_CMD_SRV(SessionCacheTimeout, TAKE1, "SSL Session Cache object lifetime " "('N' - number of seconds)") diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index 7cc2113..995e569 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -128,6 +128,7 @@ static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p) mctx->auth.cipher_suite = NULL; mctx->auth.verify_depth = UNSET; mctx->auth.verify_mode = SSL_CVERIFY_UNSET; + mctx->auth.nAcceptExpired = SSL_CACCEPTEXPIRED_UNSET; mctx->ocsp_enabled = UNSET; mctx->ocsp_force_default = UNSET; @@ -262,6 +263,7 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p, cfgMergeString(auth.cipher_suite); cfgMergeInt(auth.verify_depth); cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET); + cfgMerge(auth.nAcceptExpired, SSL_CACCEPTEXPIRED_UNSET); cfgMergeBool(ocsp_enabled); cfgMergeBool(ocsp_force_default); @@ -377,6 +379,7 @@ void *ssl_config_perdir_create(apr_pool_t *p, char *dir) dc->szCipherSuite = NULL; dc->nVerifyClient = SSL_CVERIFY_UNSET; dc->nVerifyDepth = UNSET; + dc->nAcceptExpired = SSL_CACCEPTEXPIRED_UNSET; dc->szUserName = NULL; @@ -431,6 +434,7 @@ void *ssl_config_perdir_merge(apr_pool_t *p, void *basev, void *addv) cfgMergeString(szCipherSuite); cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET); cfgMergeInt(nVerifyDepth); + cfgMerge(nAcceptExpired, SSL_CACCEPTEXPIRED_UNSET); cfgMergeString(szUserName); @@ -1125,6 +1129,49 @@ const char *ssl_cmd_SSLVerifyDepth(cmd_parms *cmd, return NULL; } +static const char *ssl_cmd_accept_expired_client(cmd_parms *parms, + const char *arg, + ssl_accept_expired_t *nAcceptExpired) +{ + if (strcEQ(arg, "on")) { + *nAcceptExpired = SSL_CACCEPTEXPIRED_YES; + } + else if (strcEQ(arg, "off")) { + *nAcceptExpired = SSL_CACCEPTEXPIRED_NO; + } + else { + return apr_pstrcat(parms->temp_pool, parms->cmd->name, + ": Invalid argument '", arg, "'", + NULL); + } + + return NULL; +} + +const char *ssl_cmd_SSLVerifyAcceptExpiredClient(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + ssl_accept_expired_t nAcceptExpired = SSL_CACCEPTEXPIRED_NO; + + const char *err; + + if ((err = ssl_cmd_accept_expired_client(cmd, arg, &nAcceptExpired))) { + return err; + } + + if (cmd->path) { + dc->nAcceptExpired = nAcceptExpired; + } + else { + sc->server->auth.nAcceptExpired = nAcceptExpired; + } + + return NULL; +} + const char *ssl_cmd_SSLSessionCache(cmd_parms *cmd, void *dcfg, const char *arg) diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c index f11d057..6c21752 100644 --- a/modules/ssl/ssl_engine_io.c +++ b/modules/ssl/ssl_engine_io.c @@ -1403,6 +1403,16 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx) return inctx->rc; } sc = mySrvConfig(sslconn->server); + SSLDirConfigRec *dc; + dc = sslconn->dc; + ssl_accept_expired_t nAcceptExpired; + + if (dc && (dc->nAcceptExpired != SSL_CACCEPTEXPIRED_UNSET)) { + nAcceptExpired = dc->nAcceptExpired; + } + else { + nAcceptExpired = sc->server->auth.nAcceptExpired; + } /* * Check for failed client authentication @@ -1434,8 +1444,20 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx) * will not be called, therefore we have to set it here */ sslconn->verify_info = "GENEROUS"; - } - else { + } else if ((verify_result == X509_V_ERR_CERT_HAS_EXPIRED) && + (nAcceptExpired == SSL_CACCEPTEXPIRED_YES)) { + ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, APLOGNO(02009) + "SSL client authentication failed, " + "accepting certificate based on " + "\"SSLVerifyAcceptExpiredClient on\" " + "configuration"); + ssl_log_ssl_error(SSLLOG_MARK, APLOG_INFO, server); + + /* on session resumption ssl_callback_SSLVerify() + * will not be called, therefore we have to set it here + */ + sslconn->verify_info = "GENEROUS"; + } else { const char *error = sslconn->verify_error ? sslconn->verify_error : X509_verify_cert_error_string(verify_result); diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index af2ada7..113a890 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -1580,7 +1580,7 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx) int errnum = X509_STORE_CTX_get_error(ctx); int errdepth = X509_STORE_CTX_get_error_depth(ctx); int depth, verify; - + ssl_accept_expired_t nAcceptExpired; /* * Log verification information @@ -1625,6 +1625,21 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx) } /* + * Optionaly accept expired client certificate + */ + if (dc && (dc->nAcceptExpired != SSL_CACCEPTEXPIRED_UNSET)) { + nAcceptExpired = dc->nAcceptExpired; + } + else { + nAcceptExpired = mctx->auth.nAcceptExpired; + } + + if ((errnum == X509_V_ERR_CERT_HAS_EXPIRED) && + (nAcceptExpired == SSL_CACCEPTEXPIRED_YES)) { + ok = TRUE; + } + + /* * Expired certificates vs. "expired" CRLs: by default, OpenSSL * turns X509_V_ERR_CRL_HAS_EXPIRED into a "certificate_expired(45)" * SSL alert, but that's not really the message we should convey to the diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index 459b6b3..01cca37 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -376,6 +376,11 @@ typedef enum { || (errnum == X509_V_ERR_CERT_UNTRUSTED) \ || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) +typedef enum { + SSL_CACCEPTEXPIRED_UNSET = UNSET, + SSL_CACCEPTEXPIRED_NO = 0, + SSL_CACCEPTEXPIRED_YES = 1, +} ssl_accept_expired_t; /** * CRL checking mask (mode | flags) */ @@ -603,6 +608,7 @@ typedef struct { /** for client or downstream server authentication */ int verify_depth; ssl_verify_t verify_mode; + ssl_accept_expired_t nAcceptExpired; } modssl_auth_ctx_t; #ifdef HAVE_TLS_SESSION_TICKETS @@ -723,6 +729,7 @@ struct SSLDirConfigRec { const char *szCipherSuite; ssl_verify_t nVerifyClient; int nVerifyDepth; + ssl_accept_expired_t nAcceptExpired; const char *szUserName; apr_size_t nRenegBufferSize; @@ -768,6 +775,7 @@ const char *ssl_cmd_SSLCompression(cmd_parms *, void *, int flag); const char *ssl_cmd_SSLSessionTickets(cmd_parms *, void *, int flag); const char *ssl_cmd_SSLVerifyClient(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLVerifyDepth(cmd_parms *, void *, const char *); +const char *ssl_cmd_SSLVerifyAcceptExpiredClient(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLSessionCache(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProtocol(cmd_parms *, void *, const char *);