bug 1003448 - HTTP/2 Alternate Service and Opportunistic Security [1/2 PSM] r=keeler
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 20 Aug 2014 16:30:16 -0400
changeset 233178 eb92720d6cd21e5cf1cfffee5f2d2e22677abb6d
parent 233177 f2003d572d536d1ff8d2d9f74f6eb059ae8e0a5a
child 233179 fcf6d37ae66cf5e3dfecc5d57d935763a19af25d
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1003448
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1003448 - HTTP/2 Alternate Service and Opportunistic Security [1/2 PSM] r=keeler
netwerk/socket/nsISSLSocketControl.idl
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsNSSIOLayer.h
--- a/netwerk/socket/nsISSLSocketControl.idl
+++ b/netwerk/socket/nsISSLSocketControl.idl
@@ -10,17 +10,17 @@ interface nsIInterfaceRequestor;
 interface nsIX509Cert;
 
 %{C++
 template<class T> class nsTArray;
 class nsCString;
 %}
 [ref] native nsCStringTArrayRef(nsTArray<nsCString>);
 
-[scriptable, builtinclass, uuid(89b819dc-31b0-4d09-915a-66f8a3703483)]
+[scriptable, builtinclass, uuid(f160ec31-01f3-47f2-b542-0e12a647b07f)]
 interface nsISSLSocketControl : nsISupports {
     attribute nsIInterfaceRequestor     notificationCallbacks;
 
     void proxyStartSSL();
     void StartTLS();
 
     /* NPN (Next Protocol Negotiation) is a mechanism for
        negotiating the protocol to be spoken inside the SSL
@@ -48,16 +48,21 @@ interface nsISSLSocketControl : nsISuppo
      * a desired NPN negotiated protocol of npnProtocol can use the socket
      * associated with this object instead of making a new one.
      */
     boolean joinConnection(
       in ACString npnProtocol, /* e.g. "spdy/2" */
       in ACString hostname,
       in long port);
 
+    /* Determine if existing connection should be trusted to convey information about
+     * a hostname.
+     */
+    boolean isAcceptableForHost(in ACString hostname);
+
     /* The Key Exchange Algorithm is used when determining whether or
        not to do false start and whether or not HTTP/2 can be used.
 
        After a handshake is complete it can be read from KEAUsed,
        before a handshake is started it may be set through KEAExpected.
        The values correspond to the SSLKEAType enum in NSS or the
        KEY_EXCHANGE_UNKNOWN constant defined below.
 
@@ -98,10 +103,31 @@ interface nsISSLSocketControl : nsISuppo
     [infallible] readonly attribute short MACAlgorithmUsed;
 
     /**
      * If set before the server requests a client cert (assuming it does so at
      * all), then this cert will be presented to the server, instead of asking
      * the user or searching the set of rememebered user cert decisions.
      */
     attribute nsIX509Cert clientCert;
+
+    /**
+     * If you wish to verify the host certificate using a different name than
+     * was used for the tcp connection, but without using proxy semantics, you
+     * can set authenticationName and authenticationPort
+     */
+    attribute ACString authenticationName;
+    [infallible] attribute long authenticationPort;
+
+    /**
+     * set bypassAuthentication to true if the server certificate checks should
+     * not be enforced. This is to enable non-secure transport over TLS.
+     */
+    [infallible] attribute boolean bypassAuthentication;
+
+    /*
+     * failedVerification is true if any enforced certificate checks have failed.
+     * Connections that have not yet tried to verify, have verifications bypassed,
+     * or are using acceptable exceptions will all return false.
+     */
+    [infallible] readonly attribute boolean failedVerification;
 };
 
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -395,16 +395,26 @@ CertErrorRunnable::CheckCertOverrides()
   unused << mFdForLogging;
 
   if (!NS_IsMainThread()) {
     NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
     return new SSLServerCertVerificationResult(mInfoObject,
                                                mDefaultErrorCodeToReport);
   }
 
+  nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
+    NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
+  if (sslSocketControl &&
+      sslSocketControl->GetBypassAuthentication()) {
+    PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+           ("[%p][%p] Bypass Auth in CheckCertOverrides\n",
+            mFdForLogging, this));
+    return new SSLServerCertVerificationResult(mInfoObject, 0);
+  }
+
   int32_t port;
   mInfoObject->GetPort(&port);
 
   nsAutoCString hostWithPortString(mInfoObject->GetHostName());
   hostWithPortString.Append(':');
   hostWithPortString.AppendInt(port);
 
   uint32_t remaining_display_errors = mCollectedErrors;
@@ -484,18 +494,16 @@ CertErrorRunnable::CheckCertOverrides()
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
          ("[%p][%p] Certificate error was not overridden\n",
          mFdForLogging, this));
 
   // Ok, this is a full stop.
   // First, deliver the technical details of the broken SSL status.
 
   // Try to get a nsIBadCertListener2 implementation from the socket consumer.
-  nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
-    NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
   if (sslSocketControl) {
     nsCOMPtr<nsIInterfaceRequestor> cb;
     sslSocketControl->GetNotificationCallbacks(getter_AddRefs(cb));
     if (cb) {
       nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
       if (bcl) {
         nsIInterfaceRequestor* csi
           = static_cast<nsIInterfaceRequestor*>(mInfoObject);
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -127,21 +127,23 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedS
     mNPNCompleted(false),
     mFalseStartCallbackCalled(false),
     mFalseStarted(false),
     mIsFullHandshake(false),
     mHandshakeCompleted(false),
     mJoined(false),
     mSentClientCert(false),
     mNotedTimeUntilReady(false),
+    mFailedVerification(false),
     mKEAUsed(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
     mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN),
     mKEAKeyBits(0),
     mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
     mMACAlgorithmUsed(nsISSLSocketControl::SSL_MAC_UNKNOWN),
+    mBypassAuthentication(false),
     mProviderFlags(providerFlags),
     mSocketCreationTimestamp(TimeStamp::Now()),
     mPlaintextBytesRead(0),
     mClientCert(nullptr)
 {
   mTLSVersionRange.min = 0;
   mTLSVersionRange.max = 0;
 }
@@ -222,16 +224,62 @@ nsNSSSocketInfo::GetClientCert(nsIX509Ce
 NS_IMETHODIMP
 nsNSSSocketInfo::SetClientCert(nsIX509Cert* aClientCert)
 {
   mClientCert = aClientCert;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNSSSocketInfo::GetBypassAuthentication(bool* arg)
+{
+  *arg = mBypassAuthentication;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::SetBypassAuthentication(bool arg)
+{
+  mBypassAuthentication = arg;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetFailedVerification(bool* arg)
+{
+  *arg = mFailedVerification;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetAuthenticationName(nsACString& aAuthenticationName)
+{
+  aAuthenticationName = GetHostName();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::SetAuthenticationName(const nsACString& aAuthenticationName)
+{
+  return SetHostName(PromiseFlatCString(aAuthenticationName).get());
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::GetAuthenticationPort(int32_t* aAuthenticationPort)
+{
+  return GetPort(aAuthenticationPort);
+}
+
+NS_IMETHODIMP
+nsNSSSocketInfo::SetAuthenticationPort(int32_t aAuthenticationPort)
+{
+  return SetPort(aAuthenticationPort);
+}
+
+NS_IMETHODIMP
 nsNSSSocketInfo::GetRememberClientAuthCertificate(bool* aRemember)
 {
   NS_ENSURE_ARG_POINTER(aRemember);
   *aRemember = mRememberClientAuthCertificate;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -373,31 +421,18 @@ nsNSSSocketInfo::GetNegotiatedNPN(nsACSt
   if (!mNPNCompleted)
     return NS_ERROR_NOT_CONNECTED;
 
   aNegotiatedNPN = mNegotiatedNPN;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
-                                const nsACString& hostname,
-                                int32_t port,
-                                bool* _retval)
+nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
 {
-  *_retval = false;
-
-  // Different ports may not be joined together
-  if (port != GetPort())
-    return NS_OK;
-
-  // Make sure NPN has been completed and matches requested npnProtocol
-  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
-    return NS_OK;
-
   // If this is the same hostname then the certicate status does not
   // need to be considered. They are joinable.
   if (hostname.Equals(GetHostName())) {
     *_retval = true;
     return NS_OK;
   }
 
   // Before checking the server certificate we need to make sure the
@@ -457,22 +492,46 @@ nsNSSSocketInfo::JoinConnection(const ns
                                                    mozilla::pkix::Now(),
                                                    nullptr, hostnameFlat.get(),
                                                    false, flags, nullptr,
                                                    nullptr);
   if (rv != SECSuccess) {
     return NS_OK;
   }
 
-  // All tests pass - this is joinable
-  mJoined = true;
+  // All tests pass
   *_retval = true;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsNSSSocketInfo::JoinConnection(const nsACString& npnProtocol,
+                                const nsACString& hostname,
+                                int32_t port,
+                                bool* _retval)
+{
+  *_retval = false;
+
+  // Different ports may not be joined together
+  if (port != GetPort())
+    return NS_OK;
+
+  // Make sure NPN has been completed and matches requested npnProtocol
+  if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol))
+    return NS_OK;
+
+  IsAcceptableForHost(hostname, _retval);
+
+  if (*_retval) {
+    // All tests pass - this is joinable
+    mJoined = true;
+  }
+  return NS_OK;
+}
+
 bool
 nsNSSSocketInfo::GetForSTARTTLS()
 {
   return mForSTARTTLS;
 }
 
 void
 nsNSSSocketInfo::SetForSTARTTLS(bool aForSTARTTLS)
@@ -627,16 +686,17 @@ nsNSSSocketInfo::SetCertVerificationResu
       if (errorCode == 0) {
         NS_ERROR("SSL_AuthCertificateComplete didn't set error code");
         errorCode = PR_INVALID_STATE_ERROR;
       }
     }
   }
 
   if (errorCode) {
+    mFailedVerification = true;
     SetCanceled(errorCode, errorMessageType);
   }
 
   if (mPlaintextBytesRead && !errorCode) {
     Telemetry::Accumulate(Telemetry::SSL_BYTES_BEFORE_CERT_CALLBACK,
                           AssertedCast<uint32_t>(mPlaintextBytesRead));
   }
 
--- a/security/manager/ssl/src/nsNSSIOLayer.h
+++ b/security/manager/ssl/src/nsNSSIOLayer.h
@@ -108,16 +108,32 @@ public:
 
   void SetSSLVersionUsed(int16_t version)
   {
     mSSLVersionUsed = version;
   }
 
   void SetMACAlgorithmUsed(int16_t mac) { mMACAlgorithmUsed = mac; }
 
+  inline bool GetBypassAuthentication()
+  {
+    bool result = false;
+    mozilla::DebugOnly<nsresult> rv = GetBypassAuthentication(&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result;
+  }
+
+  inline int32_t GetAuthenticationPort()
+  {
+    int32_t result = -1;
+    mozilla::DebugOnly<nsresult> rv = GetAuthenticationPort(&result);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return result;
+  }
+
 protected:
   virtual ~nsNSSSocketInfo();
 
 private:
   PRFileDesc* mFd;
 
   CertVerificationState mCertVerificationState;
 
@@ -134,24 +150,26 @@ private:
   bool      mNPNCompleted;
   bool      mFalseStartCallbackCalled;
   bool      mFalseStarted;
   bool      mIsFullHandshake;
   bool      mHandshakeCompleted;
   bool      mJoined;
   bool      mSentClientCert;
   bool      mNotedTimeUntilReady;
+  bool      mFailedVerification;
 
   // mKEA* are used in false start and http/2 detetermination
   // Values are from nsISSLSocketControl
   int16_t mKEAUsed;
   int16_t mKEAExpected;
   uint32_t mKEAKeyBits;
   int16_t mSSLVersionUsed;
   int16_t mMACAlgorithmUsed;
+  bool    mBypassAuthentication;
 
   uint32_t mProviderFlags;
   mozilla::TimeStamp mSocketCreationTimestamp;
   uint64_t mPlaintextBytesRead;
 
   nsCOMPtr<nsIX509Cert> mClientCert;
 };