LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File mod_nss-CVE-2013-4566-NSSVerifyClient.diff of Package apache2-mod_nss (Project home:draht:branches:openSUSE:13.1:Update)

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");