1d3e419a19
- mod_nss-cipherlist_update_for_tls12-doc.diff mod_nss-cipherlist_update_for_tls12.diff GCM mode and Camellia ciphers added to the supported ciphers list. The additional ciphers are: rsa_aes_128_gcm_sha == TLS_RSA_WITH_AES_128_GCM_SHA256 rsa_camellia_128_sha == TLS_RSA_WITH_CAMELLIA_128_CBC_SHA rsa_camellia_256_sha == TLS_RSA_WITH_CAMELLIA_256_CBC_SHA ecdh_ecdsa_aes_128_gcm_sha == TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 ecdhe_ecdsa_aes_128_gcm_sha == TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ecdh_rsa_aes_128_gcm_sha == TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 ecdhe_rsa_aes_128_gcm_sha == TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 [bnc#863035] - mod_nss-CVE-2013-4566-NSSVerifyClient.diff fixes CVE-2013-4566: If 'NSSVerifyClient none' is set in the server / vhost context (i.e. when server is configured to not request or require client certificate authentication on the initial connection), and client certificate authentication is expected to be required for a specific directory via 'NSSVerifyClient require' setting, mod_nss fails to properly require certificate authentication. Remote attacker can use this to access content of the restricted directories. [bnc#853039] - glue documentation added to /etc/apache2/conf.d/mod_nss.conf: * simultaneaous usage of mod_ssl and mod_nss * SNI concurrency * SUSE framework for apache configuration, Listen directive * module initialization - mod_nss-conf.patch obsoleted by scratch-version of nss.conf.in or mod_nss.conf, respectively. This also leads to the removal of OBS-URL: https://build.opensuse.org/request/show/222758 OBS-URL: https://build.opensuse.org/package/show/mozilla:Factory/apache2-mod_nss?expand=0&rev=8
320 lines
13 KiB
Diff
320 lines
13 KiB
Diff
This is CVE-2013-4566:
|
|
The flaw is in the NSSVerifyClient (which is equivalent to mod_ssl's
|
|
SSLVerifyClient) setting enforcement. If 'NSSVerifyClient none' is set
|
|
in the server / vhost context (i.e. when server is configured to not
|
|
request or require client certificate authentication on the initial
|
|
connection), and client certificate authentication is expected to be
|
|
required for a specific directory via 'NSSVerifyClient require'
|
|
setting, mod_nss fails to properly require certificate authentication.
|
|
Remote attacker can use this to access content of the restricted
|
|
directories.
|
|
|
|
Reported by Thomas Hoger <thoger@redhat.com>.
|
|
|
|
diff -rNU 150 ../mod_nss-1.0.8-o/nss_engine_kernel.c ./nss_engine_kernel.c
|
|
--- ../mod_nss-1.0.8-o/nss_engine_kernel.c 2013-11-29 16:09:37.000000000 +0100
|
|
+++ ./nss_engine_kernel.c 2013-11-29 16:12:20.000000000 +0100
|
|
@@ -133,301 +133,301 @@
|
|
/*
|
|
* Check to see if SSL protocol is enabled. If it's not then
|
|
* no further access control checks are relevant. The test for
|
|
* sc->enabled is probably strictly unnecessary
|
|
*/
|
|
if (!((sc->enabled == TRUE) || !ssl)) {
|
|
return DECLINED;
|
|
}
|
|
|
|
/*
|
|
* Support for per-directory reconfigured SSL connection parameters.
|
|
*
|
|
* This is implemented by forcing an SSL renegotiation with the
|
|
* reconfigured parameter suite. But Apache's internal API processing
|
|
* makes our life very hard here, because when internal sub-requests occur
|
|
* we nevertheless should avoid multiple unnecessary SSL handshakes (they
|
|
* require extra network I/O and especially time to perform).
|
|
*
|
|
* But the optimization for filtering out the unnecessary handshakes isn't
|
|
* obvious and trivial. Especially because while Apache is in its
|
|
* sub-request processing the client could force additional handshakes,
|
|
* too. And these take place perhaps without our notice. So the only
|
|
* possibility is to explicitly _ask_ OpenSSL whether the renegotiation
|
|
* has to be performed or not. It has to performed when some parameters
|
|
* which were previously known (by us) are not those we've now
|
|
* reconfigured (as known by OpenSSL) or (in optimized way) at least when
|
|
* the reconfigured parameter suite is stronger (more restrictions) than
|
|
* the currently active one.
|
|
*/
|
|
|
|
/*
|
|
* Override of NSSCipherSuite
|
|
*
|
|
* We provide two options here:
|
|
*
|
|
* o The paranoid and default approach where we force a renegotiation when
|
|
* the cipher suite changed in _any_ way (which is straight-forward but
|
|
* often forces renegotiations too often and is perhaps not what the
|
|
* user actually wanted).
|
|
*
|
|
* o The optimized and still secure way where we force a renegotiation
|
|
* only if the currently active cipher is no longer contained in the
|
|
* reconfigured/new cipher suite. Any other changes are not important
|
|
* because it's the servers choice to select a cipher from the ones the
|
|
* client supports. So as long as the current cipher is still in the new
|
|
* cipher suite we're happy. Because we can assume we would have
|
|
* selected it again even when other (better) ciphers exists now in the
|
|
* new cipher suite. This approach is fine because the user explicitly
|
|
* has to enable this via ``NSSOptions +OptRenegotiate''. So we do no
|
|
* implicit optimizations.
|
|
*/
|
|
if (dc->szCipherSuite) {
|
|
/* remember old state */
|
|
for (i=0; i < ciphernum; i++) {
|
|
SSL_CipherPrefGet(ssl, ciphers_def[i].num, &ciphers_old[i]);
|
|
}
|
|
|
|
if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
|
|
int on, keySize, secretKeySize;
|
|
char *issuer, *subject;
|
|
|
|
SSL_SecurityStatus(ssl, &on, &cipher,
|
|
&keySize, &secretKeySize, &issuer,
|
|
&subject);
|
|
}
|
|
|
|
/* configure new state */
|
|
|
|
ciphers = strdup(dc->szCipherSuite);
|
|
if (nss_parse_ciphers(r->server, ciphers, ciphers_new) < 0) {
|
|
ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
|
|
r->server,
|
|
"Unable to reconfigure (per-directory) "
|
|
"permitted SSL ciphers");
|
|
nss_log_nss_error(APLOG_MARK, APLOG_ERR, r->server);
|
|
free(ciphers);
|
|
|
|
return HTTP_FORBIDDEN;
|
|
}
|
|
free(ciphers);
|
|
|
|
/* Actually enable the selected ciphers. Also check to
|
|
see if the existing cipher is in the new list for
|
|
a possible optimization later. */
|
|
|
|
for (i=0; i<ciphernum;i++) {
|
|
if (cipher && !strcasecmp(cipher, ciphers_def[i].name)) {
|
|
if (ciphers_new[i] == PR_TRUE)
|
|
cipher_in_list = PR_TRUE;
|
|
}
|
|
SSL_CipherPrefSet(ssl, ciphers_def[i].num, ciphers_new[i]);
|
|
}
|
|
|
|
/* determine whether a renegotiation has to be forced */
|
|
|
|
if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) {
|
|
if (cipher_in_list != PR_TRUE)
|
|
renegotiate = TRUE;
|
|
}
|
|
else {
|
|
/* paranoid way */
|
|
for (i=0; i<ciphernum;i++) {
|
|
if (ciphers_new[i] != ciphers_old[i]) {
|
|
renegotiate = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* tracing */
|
|
if (renegotiate) {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
|
"Reconfigured cipher suite will force renegotiation");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* override of SSLVerifyClient
|
|
*
|
|
* 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
|
|
*
|
|
* Additionally the following optimization is possible here: When the
|
|
* currently active verify type is "none" but a client certificate is
|
|
* already known/present, it's enough to manually force a client
|
|
* verification but at least skip the I/O-intensive renegotation
|
|
* handshake.
|
|
*/
|
|
if (dc->nVerifyClient != SSL_CVERIFY_UNSET) {
|
|
PRInt32 on;
|
|
|
|
/* remember old state */
|
|
SSL_OptionGet(ssl, SSL_REQUIRE_CERTIFICATE, &on);
|
|
if (on == PR_TRUE) {
|
|
verify_old = SSL_CVERIFY_REQUIRE;
|
|
} else {
|
|
SSL_OptionGet(ssl, SSL_REQUEST_CERTIFICATE, &on);
|
|
if (on == PR_TRUE)
|
|
verify_old = SSL_CVERIFY_OPTIONAL;
|
|
else
|
|
verify_old = SSL_CVERIFY_NONE;
|
|
}
|
|
|
|
/* configure new state */
|
|
verify = dc->nVerifyClient;
|
|
|
|
if (verify == SSL_CVERIFY_REQUIRE) {
|
|
SSL_OptionSet(ssl, SSL_REQUEST_CERTIFICATE, PR_TRUE);
|
|
- SSL_OptionSet(ssl, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NO_ERROR);
|
|
+ SSL_OptionSet(ssl, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_ALWAYS);
|
|
} else if (verify == SSL_CVERIFY_OPTIONAL) {
|
|
SSL_OptionSet(ssl, SSL_REQUEST_CERTIFICATE, PR_TRUE);
|
|
SSL_OptionSet(ssl, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER);
|
|
} else {
|
|
SSL_OptionSet(ssl, SSL_REQUEST_CERTIFICATE, PR_FALSE);
|
|
SSL_OptionSet(ssl, SSL_REQUIRE_CERTIFICATE, SSL_REQUIRE_NEVER);
|
|
}
|
|
|
|
/* determine whether we've to force a renegotiation */
|
|
if (!renegotiate && verify != verify_old) {
|
|
if (((verify_old == SSL_CVERIFY_NONE) &&
|
|
(verify != SSL_CVERIFY_NONE)) ||
|
|
|
|
(!(verify_old & SSL_CVERIFY_OPTIONAL) &&
|
|
(verify & SSL_CVERIFY_OPTIONAL)) ||
|
|
|
|
(!(verify_old & SSL_CVERIFY_REQUIRE) &&
|
|
(verify & SSL_CVERIFY_REQUIRE)))
|
|
{
|
|
renegotiate = TRUE;
|
|
/* optimization */
|
|
|
|
if ((dc->nOptions & SSL_OPT_OPTRENEGOTIATE) &&
|
|
(verify_old == SSL_CVERIFY_NONE) &&
|
|
((peercert = SSL_PeerCertificate(ssl)) != NULL))
|
|
{
|
|
renegotiate_quick = TRUE;
|
|
CERT_DestroyCertificate(peercert);
|
|
}
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0,
|
|
r->server,
|
|
"Changed client verification type will force "
|
|
"%srenegotiation",
|
|
renegotiate_quick ? "quick " : "");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If a renegotiation is now required for this location, and the
|
|
* request includes a message body (and the client has not
|
|
* requested a "100 Continue" response), then the client will be
|
|
* streaming the request body over the wire already. In that
|
|
* case, it is not possible to stop and perform a new SSL
|
|
* handshake immediately; once the SSL library moves to the
|
|
* "accept" state, it will reject the SSL packets which the client
|
|
* is sending for the request body.
|
|
*
|
|
* To allow authentication to complete in this auth hook, the
|
|
* solution used here is to fill a (bounded) buffer with the
|
|
* request body, and then to reinject that request body later.
|
|
*/
|
|
if (renegotiate && !renegotiate_quick
|
|
&& (apr_table_get(r->headers_in, "transfer-encoding")
|
|
|| (apr_table_get(r->headers_in, "content-length")
|
|
&& strcmp(apr_table_get(r->headers_in, "content-length"), "0")))
|
|
&& !r->expecting_100) {
|
|
int rv;
|
|
|
|
/* Fill the I/O buffer with the request body if possible. */
|
|
rv = nss_io_buffer_fill(r);
|
|
|
|
if (rv) {
|
|
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
|
"could not buffer message body to allow "
|
|
"SSL renegotiation to proceed");
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* now do the renegotiation if anything was actually reconfigured
|
|
*/
|
|
if (renegotiate) {
|
|
/*
|
|
* Now we force the SSL renegotation by sending the Hello Request
|
|
* message to the client. Here we have to do a workaround: Actually
|
|
* OpenSSL returns immediately after sending the Hello Request (the
|
|
* intent AFAIK is because the SSL/TLS protocol says it's not a must
|
|
* that the client replies to a Hello Request). But because we insist
|
|
* on a reply (anything else is an error for us) we have to go to the
|
|
* ACCEPT state manually. Using SSL_set_accept_state() doesn't work
|
|
* here because it resets too much of the connection. So we set the
|
|
* state explicitly and continue the handshake manually.
|
|
*/
|
|
ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
|
|
"Requesting connection re-negotiation");
|
|
|
|
if (renegotiate_quick) {
|
|
SECStatus rv;
|
|
CERTCertificate *peerCert;
|
|
void *pinArg;
|
|
|
|
/* perform just a manual re-verification of the peer */
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
|
"Performing quick renegotiation: "
|
|
"just re-verifying the peer");
|
|
|
|
peerCert = SSL_PeerCertificate(sslconn->ssl);
|
|
|
|
pinArg = SSL_RevealPinArg(sslconn->ssl);
|
|
|
|
rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(),
|
|
peerCert,
|
|
PR_TRUE,
|
|
certUsageSSLClient,
|
|
pinArg);
|
|
|
|
CERT_DestroyCertificate(peerCert);
|
|
|
|
if (rv != SECSuccess) {
|
|
ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
|
|
"Re-negotiation handshake failed: "
|
|
"Client verification failed");
|
|
|
|
return HTTP_FORBIDDEN;
|
|
}
|
|
|
|
/* The cert is ok, fall through to the check SSLRequires */
|
|
}
|
|
else {
|
|
int handshake_done = 0;
|
|
int result = 0;
|
|
|
|
/* do a full renegotiation */
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
|
"Performing full renegotiation: "
|
|
"complete handshake protocol");
|
|
|
|
/* Do NOT call SSL_ResetHandshake as this will tear down the
|
|
* existing connection.
|
|
*/
|
|
if (SSL_HandshakeCallback(ssl, HandshakeDone, (void *)&handshake_done) || SSL_ReHandshake(ssl, PR_TRUE)) {
|
|
int errCode = PR_GetError();
|
|
if (errCode == SEC_ERROR_INVALID_ARGS) {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
|
"Re-negotation request failed: "
|
|
"trying to do client authentication on a non-SSL3 connection");
|
|
} else {
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
|
"Re-negotation request failed: "
|
|
"returned error %d", errCode);
|
|
}
|
|
r->connection->aborted = 1;
|
|
return HTTP_FORBIDDEN;
|
|
}
|
|
|
|
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
|
|
"Awaiting re-negotiation handshake");
|
|
|