Index: docs/manual/mod/mod_ssl.xml =================================================================== --- docs/manual/mod/mod_ssl.xml (revision 933374) +++ docs/manual/mod/mod_ssl.xml (working copy) @@ -81,7 +81,7 @@ SSL_CLIENT_A_KEY string Algorithm used for the public key of client's certificate SSL_CLIENT_CERT string PEM-encoded client certificate SSL_CLIENT_CERT_CHAIN_n string PEM-encoded certificates in client certificate chain -SSL_CLIENT_VERIFY string NONE, SUCCESS, GENEROUS or FAILED:reason +SSL_CLIENT_VERIFY string NONE, SUCCESS, GENEROUS:reason or FAILED:reason SSL_SERVER_M_VERSION string The version of the server certificate SSL_SERVER_M_SERIAL string The serial of the server certificate SSL_SERVER_S_DN string Subject DN in server's certificate @@ -987,31 +987,58 @@

-This directive sets the Certificate verification level for the Client +This directive sets the Certificate verification level for Client Certificate Authentication. Notice that this directive can be used both in per-server and per-directory context. In per-server context it applies to the client -authentication process used in the standard SSL handshake when a connection is +authentication process in the initial SSL handshake when a connection is established. In per-directory context it forces a SSL renegotation with the -reconfigured client verification level after the HTTP request was read but +reconfigured client verification level after the HTTP request is read but before the HTTP response is sent.

The following levels are available for level:

-

In practice only levels none and -require are really interesting, because level -optional doesn't work with all browsers and level -optional_no_ca is actually against the idea of -authentication (but can be used to establish SSL test pages, etc.)

+

In practice, most users will want either none or +require, as the other levels allow users to bypass the +authentication mechanism. However, the other levels may be useful for testing, +or if your web application does it's own Certificate verification.

+

See the SSLCACertificateFile and +SSLCACertificatePath directives to +configure the list of trusted CA Certificates.

+

If using one of the optional levels, note that if the +client does not have a suitable Certificate signed by one of the specified +trusted CAs, most browsers will attempt to continue without providing a +Certificate, but some browsers may give up and disconnect on their own.

+

If using optional_no_ca or +optional_no_verify, you can request any available Certificate +(signed by any CA) from most browsers by creating a file containing only a +single space character, and pointing SSLCACertificateFile at that file.

Example SSLVerifyClient require @@ -1448,14 +1475,14 @@

When a proxy is configured to forward requests to a remote SSL -server, this directive can be used to configure certificate -verification of the remote server. Notice that this directive can be +server, this directive can be used to configure verification of the +remote server's SSL Certificate. Notice that this directive can be used both in per-server and per-directory context. In per-server context it applies to the remote server authentication process used in -the standard SSL handshake when a connection is established by the +the initial SSL handshake when a connection is established by the proxy. In per-directory context it forces a SSL renegotation with the reconfigured remote server verification level after the HTTP request -was read but before the HTTP response is sent.

+is read but before the HTTP response is sent.

Note that even when certificate verification is enabled, @@ -1477,21 +1504,29 @@

The following levels are available for level:

    -
  • none: - no remote server Certificate is required at all
  • -
  • optional: - the remote server may present a valid Certificate
  • -
  • require: - the remote server has to present a valid Certificate
  • -
  • optional_no_ca: - the remote server may present a valid Certificate
    - but it need not to be (successfully) verifiable.
  • +
  • none +

    No remote server Certificate verification will be performed.

  • +
  • optional_no_verify +

    If the remote server provides a Certificate, it will be verified, but + the result will be ignored.

  • +
  • optional_no_ca +

    If the remote server provides a Certificate, it will be verified. The + connection will be accepted even if the Certificate is not signed by one of + the CAs listed in + SSLProxyCACertificateFile or + SSLProxyCACertificatePath, but will + be rejected if verification fails for any other reason.

  • +
  • optional +

    If the remote server provides a Certificate, it will be verified, and the + connection will be rejected if verification fails.

  • +
  • require +

    The connection will be rejected unless the remote server provides a + Certificate and that Certificate is successfully verified.

-

In practice only levels none and -require are really interesting, because level -optional doesn't work with all servers and level -optional_no_ca is actually against the idea of -authentication (but can be used to establish SSL test pages, etc.)

+

In practice, most users will want either none or +require, as the other levels allow a remote server to bypass +the authentication mechanism. However, the other levels may be useful for +testing.

Example SSLProxyVerify require Index: modules/ssl/ssl_private.h =================================================================== --- modules/ssl/ssl_private.h (revision 933374) +++ modules/ssl/ssl_private.h (working copy) @@ -229,7 +229,8 @@ SSL_CVERIFY_NONE = 0, SSL_CVERIFY_OPTIONAL = 1, SSL_CVERIFY_REQUIRE = 2, - SSL_CVERIFY_OPTIONAL_NO_CA = 3 + SSL_CVERIFY_OPTIONAL_NO_CA = 3, + SSL_CVERIFY_OPTIONAL_NO_VERIFY = 4 } ssl_verify_t; #define SSL_VERIFY_PEER_STRICT \ Index: modules/ssl/ssl_engine_init.c =================================================================== --- modules/ssl/ssl_engine_init.c (revision 933374) +++ modules/ssl/ssl_engine_init.c (working copy) @@ -534,7 +534,8 @@ } if ((mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL) || - (mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) + (mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA) || + (mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_VERIFY)) { verify |= SSL_VERIFY_PEER; } Index: modules/ssl/ssl_engine_config.c =================================================================== --- modules/ssl/ssl_engine_config.c (revision 933374) +++ modules/ssl/ssl_engine_config.c (working copy) @@ -938,6 +938,9 @@ else if (strcEQ(arg, "optional_no_ca")) { *id = SSL_CVERIFY_OPTIONAL_NO_CA; } + else if (strcEQ(arg, "optional_no_verify")) { + *id = SSL_CVERIFY_OPTIONAL_NO_VERIFY; + } else { return apr_pstrcat(parms->temp_pool, parms->cmd->name, ": Invalid argument '", arg, "'", Index: modules/ssl/ssl_engine_io.c =================================================================== --- modules/ssl/ssl_engine_io.c (revision 933374) +++ modules/ssl/ssl_engine_io.c (working copy) @@ -1169,8 +1169,13 @@ if ((verify_result != X509_V_OK) || sslconn->verify_error) { - if (ssl_verify_error_is_optional(verify_result) && - (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) + const char *error = sslconn->verify_error ? + sslconn->verify_error : + X509_verify_cert_error_string(verify_result); + + if ((ssl_verify_error_is_optional(verify_result) && + sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA) || + sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_VERIFY) { /* leaving this log message as an error for the moment, * according to the mod_ssl docs: @@ -1181,17 +1186,14 @@ * in 1.x */ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, - "SSL client authentication failed, " - "accepting certificate based on " - "\"SSLVerifyClient optional_no_ca\" " - "configuration"); + "SSL client authentication failed, accepting " + "certificate based on \"SSLVerifyClient " + "optional_no_ca\" or \"SSLVerifyClient " + "optional_no_verify\" configuration: %s", + error ? error : "unknown"); ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, server); } else { - const char *error = sslconn->verify_error ? - sslconn->verify_error : - X509_verify_cert_error_string(verify_result); - ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL client authentication failed: %s", error ? error : "unknown"); Index: modules/ssl/ssl_engine_vars.c =================================================================== --- modules/ssl/ssl_engine_vars.c (revision 933374) +++ modules/ssl/ssl_engine_vars.c (working copy) @@ -611,9 +611,12 @@ else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL) /* client verification done successful */ result = "SUCCESS"; - else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS")) + else if (vinfo != NULL && strEQ(vinfo, "GENEROUS")) /* client verification done in generous way */ - result = "GENEROUS"; + if (verr) + result = apr_psprintf(p, "GENEROUS:%s", verr); + else + result = "GENEROUS"; else /* client verification failed */ result = apr_psprintf(p, "FAILED:%s", verr); Index: modules/ssl/ssl_engine_kernel.c =================================================================== --- modules/ssl/ssl_engine_kernel.c (revision 933374) +++ modules/ssl/ssl_engine_kernel.c (working copy) @@ -454,7 +454,7 @@ * We force a renegotiation if the reconfigured/new verify type is * stronger than the currently active verify type. * - * The order is: none << optional_no_ca << optional << require + * The order is: none << optional_no_verify << optional_no_ca << optional << require * * Additionally the following optimization is possible here: When the * currently active verify type is "none" but a client certificate is @@ -476,8 +476,10 @@ if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) || (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) || + (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_VERIFY) || (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) || - (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) + (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA) || + (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_VERIFY)) { verify |= SSL_VERIFY_PEER; } @@ -1354,14 +1356,17 @@ } if (ssl_verify_error_is_optional(errnum) && - (verify == SSL_CVERIFY_OPTIONAL_NO_CA)) + ((verify == SSL_CVERIFY_OPTIONAL_NO_CA) || + (verify == SSL_CVERIFY_OPTIONAL_NO_VERIFY))) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn, - "Certificate Verification: Verifiable Issuer is " - "configured as optional, therefore we're accepting " - "the certificate"); + "Certificate Verification: Error (%d) but verifiable " + "Issuer is configured as optional, therefore we're " + "accepting the certificate: %s", errnum, + X509_verify_cert_error_string(errnum)); sslconn->verify_info = "GENEROUS"; + sslconn->verify_error = X509_verify_cert_error_string(errnum); ok = TRUE; } @@ -1378,16 +1383,31 @@ * If we already know it's not ok, log the real reason */ if (!ok) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, - "Certificate Verification: Error (%d): %s", - errnum, X509_verify_cert_error_string(errnum)); + if (verify == SSL_CVERIFY_OPTIONAL_NO_VERIFY) { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn, + "Certificate Verification: Error (%d) but " + "verification is configured as optional, therefore " + "we're accepting the certificate: %s", errnum, + X509_verify_cert_error_string(errnum)); - if (sslconn->client_cert) { - X509_free(sslconn->client_cert); - sslconn->client_cert = NULL; + sslconn->verify_info = "GENEROUS"; + if (!sslconn->verify_error) { + sslconn->verify_error = X509_verify_cert_error_string(errnum); + } + ok = TRUE; } - sslconn->client_dn = NULL; - sslconn->verify_error = X509_verify_cert_error_string(errnum); + else { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, + "Certificate Verification: Error (%d): %s", + errnum, X509_verify_cert_error_string(errnum)); + + if (sslconn->client_cert) { + X509_free(sslconn->client_cert); + sslconn->client_cert = NULL; + } + sslconn->client_dn = NULL; + sslconn->verify_error = X509_verify_cert_error_string(errnum); + } } /* @@ -1401,16 +1421,32 @@ } if (errdepth > depth) { - ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, - "Certificate Verification: Certificate Chain too long " - "(chain has %d certificates, but maximum allowed are " - "only %d)", - errdepth, depth); + if (verify == SSL_CVERIFY_OPTIONAL_NO_VERIFY) { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, + "Certificate Verification: Certificate Chain too long " + "(chain has %d certificates, but maximum allowed are " + "only %d) but verification is configured as optional, " + "therefore we're accepting the certificate", + errdepth, depth); - errnum = X509_V_ERR_CERT_CHAIN_TOO_LONG; - sslconn->verify_error = X509_verify_cert_error_string(errnum); + sslconn->verify_info = "GENEROUS"; + if (!sslconn->verify_error) { + errnum = X509_V_ERR_CERT_CHAIN_TOO_LONG; + sslconn->verify_error = X509_verify_cert_error_string(errnum); + } + } + else { + ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, conn, + "Certificate Verification: Certificate Chain too long " + "(chain has %d certificates, but maximum allowed are " + "only %d)", + errdepth, depth); - ok = FALSE; + errnum = X509_V_ERR_CERT_CHAIN_TOO_LONG; + sslconn->verify_error = X509_verify_cert_error_string(errnum); + + ok = FALSE; + } } /* Index: modules/ssl/mod_ssl.c =================================================================== --- modules/ssl/mod_ssl.c (revision 933374) +++ modules/ssl/mod_ssl.c (working copy) @@ -131,7 +131,7 @@ "(`/path/to/file' - PEM encoded)") SSL_CMD_ALL(VerifyClient, TAKE1, "SSL Client verify type " - "(`none', `optional', `require', `optional_no_ca')") + "(`none', `optional_no_verify', `optional_no_ca', `optional', `require')") SSL_CMD_ALL(VerifyDepth, TAKE1, "SSL Client verify depth " "(`N' - number of intermediate certificates)") @@ -164,7 +164,7 @@ "(`XXX:...:XXX' - see manual)") SSL_CMD_SRV(ProxyVerify, TAKE1, "SSL Proxy: whether to verify the remote certificate " - "(`on' or `off')") + "(`none', `optional_no_verify', `optional_no_ca', `optional', `require')") SSL_CMD_SRV(ProxyVerifyDepth, TAKE1, "SSL Proxy: maximum certificate verification depth " "(`N' - number of intermediate certificates)")