You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by cl...@apache.org on 2013/12/05 00:12:01 UTC

svn commit: r1547958 - /qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp

Author: cliffjansen
Date: Wed Dec  4 23:12:01 2013
New Revision: 1547958

URL: http://svn.apache.org/r1547958
Log:
QPID-5375: make Windows client certs more like their Posix counterpart, no longer restricted to SASL EXTERNAL

Modified:
    qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp

Modified: qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp?rev=1547958&r1=1547957&r2=1547958&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp Wed Dec  4 23:12:01 2013
@@ -43,6 +43,16 @@
 #undef SECURITY_WIN32
 #include <winsock2.h>
 
+/*
+ * Note on client certificates: The Posix/NSS implementation performs a lazy
+ * client certificate search part way through the ssl handshake if the server
+ * requests one.  Here, it is not known in advance if the server will
+ * request the certificate so the certificate is pre-loaded (even if never
+ * used).  To match the Linux behavior, client certificate load problems are
+ * remembered and reported later if appropriate, but do not prevent the
+ * connection attempt.
+ */
+
 namespace qpid {
 namespace client {
 namespace windows {
@@ -51,6 +61,15 @@ using qpid::sys::Socket;
 
 class SslConnector : public qpid::client::TCPConnector
 {
+    struct SavedError {
+        std::string logMessage;
+        std::string error;
+        void set(const std::string &lm, const std::string es);
+        void set(const std::string &lm, int status);
+        void clear();
+        bool pending();
+    };
+
     qpid::sys::windows::ClientSslAsynchIO *shim;
     boost::shared_ptr<qpid::sys::Poller> poller;
     std::string brokerHost;
@@ -59,13 +78,16 @@ class SslConnector : public qpid::client
     SCHANNEL_CRED cred;
     CredHandle credHandle;
     TimeStamp credExpiry;
+    SavedError clientCertError;
 
     virtual ~SslConnector();
     void negotiationDone(SECURITY_STATUS status);
 
     void connect(const std::string& host, const std::string& port);
     void connected(const Socket&);
+    PCCERT_CONTEXT findCertificate(const std::string& name);
     void loadPrivCertStore();
+    std::string getPasswd(const std::string& filename);
     void importHostCert(const ConnectionSettings&);
 
 public:
@@ -100,16 +122,24 @@ namespace {
         ~StaticInit() { }
     } init;
 
-    std::string getPasswd(const std::string& filename);
-    PCCERT_CONTEXT findCertificate(const std::string& name, HCERTSTORE& store);
 }
 
 void SslConnector::negotiationDone(SECURITY_STATUS status)
 {
-    if (status == SEC_E_OK)
+    if (status == SEC_E_OK) {
+        clientCertError.clear();
         initAmqp();
-    else
-        connectFailed(QPID_MSG(qpid::sys::strError(status)));
+    }
+    else {
+        if (status == SEC_E_INCOMPLETE_CREDENTIALS && clientCertError.pending()) {
+            // Server requested a client cert but we supplied none for the following reason:
+            if (!clientCertError.logMessage.empty())
+                QPID_LOG(warning, clientCertError.logMessage);
+            connectFailed(QPID_MSG(clientCertError.error));
+        }
+        else
+            connectFailed(QPID_MSG(qpid::sys::strError(status)));
+    }
 }
 
 SslConnector::SslConnector(boost::shared_ptr<qpid::sys::Poller> p,
@@ -123,13 +153,10 @@ SslConnector::SslConnector(boost::shared
     cred.dwVersion = SCHANNEL_CRED_VERSION;
     cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;
 
-    // In case EXTERNAL SASL mechanism has been selected, we need to find
-    // the client certificate with the private key which should be used
-    if (settings.mechanism == std::string("EXTERNAL"))  {
-        const std::string& name = (settings.sslCertName != "") ?
-            settings.sslCertName : qpid::sys::ssl::SslOptions::global.certName;
-        loadPrivCertStore();
-        cert = findCertificate(name, certStore);
+    const std::string& name = (settings.sslCertName != "") ?
+        settings.sslCertName : qpid::sys::ssl::SslOptions::global.certName;
+    cert = findCertificate(name);
+    if (cert != NULL) {
         // assign the certificate into the credentials
         cred.paCred = &cert;
         cred.cCreds = 1;
@@ -192,8 +219,8 @@ void SslConnector::loadPrivCertStore()
                           CERT_SYSTEM_STORE_CURRENT_USER, store);
         if (!certStore) {
             HRESULT status = GetLastError();
-            QPID_LOG(warning, "Could not open system certificate store: " << store);
-            throw QPID_WINDOWS_ERROR(status);
+            clientCertError.set(Msg() << "Could not open system certificate store: " << store, status);
+            return;
         }
         QPID_LOG(debug, "SslConnector using certifcates from system store: " << store);
     } else {
@@ -203,8 +230,8 @@ void SslConnector::loadPrivCertStore()
             FILE_ATTRIBUTE_NORMAL, NULL);
         if (INVALID_HANDLE_VALUE == certFileHandle) {
             HRESULT status = GetLastError();
-            QPID_LOG(warning, "Failed to open the file holding the private key: " << opts.certFilename);
-            throw QPID_WINDOWS_ERROR(status);
+            clientCertError.set(Msg() << "Failed to open the file holding the private key: " << opts.certFilename, status);
+            return;
         }
         std::vector<BYTE> certEncoded;
         DWORD certEncodedSize = 0L;
@@ -219,15 +246,15 @@ void SslConnector::loadPrivCertStore()
             if (!result) {
                 // the read failed, return the error as an HRESULT
                 HRESULT status = GetLastError();
-                QPID_LOG(warning, "Reading the private key from file failed " << opts.certFilename);
                 CloseHandle(certFileHandle);
-                throw QPID_WINDOWS_ERROR(status);
+                clientCertError.set(Msg() << "Reading the private key from file failed " << opts.certFilename, status);
+                return;
             }
         }
         else {
             HRESULT status = GetLastError();
-            QPID_LOG(warning, "Unable to read the certificate file " << opts.certFilename);
-            throw QPID_WINDOWS_ERROR(status);
+            clientCertError.set(Msg() << "Unable to read the certificate file " << opts.certFilename, status);
+            return;
         }
         CloseHandle(certFileHandle);
 
@@ -237,33 +264,37 @@ void SslConnector::loadPrivCertStore()
 
         // get passwd from file and convert to null terminated wchar_t (Windows UCS2)
         std::string passwd = getPasswd(opts.certPasswordFile);
+        if (clientCertError.pending())
+            return;
         int pwlen = passwd.length();
         std::vector<wchar_t> pwUCS2(pwlen + 1, L'\0');
         int nwc = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, passwd.data(), pwlen, &pwUCS2[0], pwlen);
         if (!nwc) {
             HRESULT status = GetLastError();
-            QPID_LOG(warning, "Error converting password from UTF8");
-            throw QPID_WINDOWS_ERROR(status);
+            clientCertError.set("Error converting password from UTF8", status);
+            return;
         }
 
         certStore = PFXImportCertStore(&blobData, &pwUCS2[0], 0);
         if (certStore == NULL) {
             HRESULT status = GetLastError();
-            QPID_LOG(warning, "Failed to open the certificate store");
-            throw QPID_WINDOWS_ERROR(status);
+            clientCertError.set("Failed to open the certificate store", status);
+            return;
         }
         QPID_LOG(debug, "SslConnector using certificate from pkcs#12 file: " << opts.certFilename);
     }
 }
 
 
-namespace {
-
-PCCERT_CONTEXT findCertificate(const std::string& name, HCERTSTORE& store)
+PCCERT_CONTEXT SslConnector::findCertificate(const std::string& name)
 {
+        loadPrivCertStore();
+        if (clientCertError.pending())
+            return NULL;
+
         // search for the certificate by Friendly Name
         PCCERT_CONTEXT tmpctx = NULL;
-        while (tmpctx = CertEnumCertificatesInStore(store, tmpctx)) {
+        while (tmpctx = CertEnumCertificatesInStore(certStore, tmpctx)) {
             DWORD len = CertGetNameString(tmpctx, CERT_NAME_FRIENDLY_DISPLAY_TYPE,
                                           0, NULL, NULL, 0);
             if (len == 1)
@@ -278,14 +309,14 @@ PCCERT_CONTEXT findCertificate(const std
 
         // verify whether some certificate has been found
         if (tmpctx == NULL) {
-            QPID_LOG(warning, "Certificate not found in the certificate store for name " << name);
-            throw qpid::Exception(QPID_MSG("client certificate not found"));
+            clientCertError.set(Msg() << "Client SSL/TLS certificate not found in the certificate store for name " << name,
+                                "client certificate not found");
         }
         return tmpctx;
 }
 
 
-std::string getPasswd(const std::string& filename)
+std::string SslConnector::getPasswd(const std::string& filename)
 {
     std::string passwd;
     if (filename == "")
@@ -296,14 +327,15 @@ std::string getPasswd(const std::string&
 
     if (INVALID_HANDLE_VALUE == pwfHandle) {
         HRESULT status = GetLastError();
-        QPID_LOG(warning, "Failed to open the password file: " << filename);
-        throw QPID_WINDOWS_ERROR(status);
+        clientCertError.set(Msg() << "Failed to open the password file: " << filename, status);
+        return passwd;
     }
 
     const DWORD fileSize = GetFileSize(pwfHandle, NULL);
     if (fileSize == INVALID_FILE_SIZE) {
         CloseHandle(pwfHandle);
-        throw qpid::Exception(QPID_MSG("Cannot read password file"));
+        clientCertError.set("", "Cannot read password file");
+        return passwd;
     }
 
     std::vector<char> pwbuf;
@@ -312,8 +344,8 @@ std::string getPasswd(const std::string&
     if (!ReadFile(pwfHandle, &pwbuf[0], fileSize, &nbytes, NULL)) {
         HRESULT status = GetLastError();
         CloseHandle(pwfHandle);
-        QPID_LOG(warning, "Error reading password file");
-        throw QPID_WINDOWS_ERROR(status);
+        clientCertError.set("Error reading password file", status);
+        return passwd;
     }
     CloseHandle(pwfHandle);
 
@@ -332,6 +364,24 @@ std::string getPasswd(const std::string&
 
     return passwd;
 }
-} // namespace
+
+void SslConnector::SavedError::set(const std::string &lm, const std::string es) {
+    logMessage = lm;
+    error = es;
+}
+
+void SslConnector::SavedError::set(const std::string &lm, int status) {
+    logMessage = lm;
+    error = qpid::sys::strError(status);
+}
+
+void SslConnector::SavedError::clear() {
+    logMessage.clear();
+    error.clear();
+}
+
+bool SslConnector::SavedError::pending() {
+    return !logMessage.empty() || !error.empty();
+}
 
 }}} // namespace qpid::client::windows



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org