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/10/22 20:10:49 UTC
svn commit: r1534714 - in /qpid/trunk/qpid/cpp: SSL src/CMakeLists.txt
src/qpid/client/windows/SaslFactory.cpp
src/qpid/client/windows/SslConnector.cpp src/qpid/sys/windows/util.cpp
src/qpid/sys/windows/util.h
Author: cliffjansen
Date: Tue Oct 22 18:10:49 2013
New Revision: 1534714
URL: http://svn.apache.org/r1534714
Log:
QPID-3914: Windows C++ SSL client certificate authentication support
Added:
qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.cpp
qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.h
Modified:
qpid/trunk/qpid/cpp/SSL
qpid/trunk/qpid/cpp/src/CMakeLists.txt
qpid/trunk/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp
qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp
Modified: qpid/trunk/qpid/cpp/SSL
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/SSL?rev=1534714&r1=1534713&r2=1534714&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/SSL (original)
+++ qpid/trunk/qpid/cpp/SSL Tue Oct 22 18:10:49 2013
@@ -83,10 +83,9 @@ Windows
SSL support for Qpid-C++ on Windows is implemented using the Microsoft
Secure Channel (Schannel) package. Currently, only registry based
-certificates scoped to the local machine are supported, however
-Schannel also supports file based and user scoped certificates, so
-additional support could be added as required. Client certificate
-authentication is not supported at this time.
+certificates scoped to the local machine are supported on the broker.
+The client may specify client certificates in a user scoped store or in
+a pkcs#12 file.
For testing purposes, a self signed certificate can be created as
follows (requiring Administrator privilege on more recent versions of
@@ -109,7 +108,7 @@ that will be using qpid, you must import
as a trusted root. This can be done from the MMC certificate snapin
or directly using certmgr.exe. From the main window:
- select "Third-Party Root Certification Authorities"
+ select "Trusted Root Certification Authorities"
select "Action" -> "Import..."
then direct the Certificate Import Wizard to the "myhost.cer" file
@@ -124,3 +123,28 @@ clients if they support the DER format.
be converted to PEM format using OpenSSL
openssl x509 -in myhost.cer -inform DER -out myhost.pem -outform PEM
+
+Client certificates operate much the same as for Linux, except for
+identifying the certificate storage. Process environment variables
+are used but the certificate name may be set or overridden by its Qpid
+Messaging connection option. For Windows registry stores, you specify
+the store:
+
+ QPID_SSL_CERT_STORE=teststore
+
+If you omit the certificate store name, it defaults to the "Personal" or
+"MY" store. For a certificate stored in a pkcs#12 format file, you must
+supply the filename and a file containing the password for the
+certificate's private key:
+
+ QPID_SSL_CERT_FILENAME=wg444.pfx
+ QPID_SSL_CERT_PASSWORD_FILE=pw_wg444.txt
+
+The certificate is specified by its "friendly name", i.e.
+
+ QPID_SSL_CERT_NAME=guest123
+
+as an environment variable, or in the case of a Qpid Messaging
+connection option:
+
+ {transport:ssl,sasl-mechanism:EXTERNAL,ssl-cert-name:guest789}
Modified: qpid/trunk/qpid/cpp/src/CMakeLists.txt
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/CMakeLists.txt?rev=1534714&r1=1534713&r2=1534714&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/CMakeLists.txt (original)
+++ qpid/trunk/qpid/cpp/src/CMakeLists.txt Tue Oct 22 18:10:49 2013
@@ -636,6 +636,8 @@ if (BUILD_SSL)
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
set (sslcommon_SOURCES
qpid/sys/windows/SslAsynchIO.cpp
+ qpid/sys/windows/util.cpp
+ qpid/sys/windows/util.h
)
set (ssl_SOURCES
@@ -647,7 +649,7 @@ if (BUILD_SSL)
)
set (ssl_INCLUDES "")
set (ssl_LIBDIRS "")
- set (ssl_LIBS Secur32.lib)
+ set (ssl_LIBS Crypt32.lib Secur32.lib)
set (ssl_server_LIBS Crypt32.lib Secur32.lib)
else (CMAKE_SYSTEM_NAME STREQUAL Windows)
if (NOT NSS_FOUND)
Modified: qpid/trunk/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp?rev=1534714&r1=1534713&r2=1534714&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/client/windows/SaslFactory.cpp Tue Oct 22 18:10:49 2013
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -117,14 +117,15 @@ std::auto_ptr<SaslServer> SaslFactory::c
namespace {
const std::string ANONYMOUS = "ANONYMOUS";
const std::string PLAIN = "PLAIN";
+ const std::string EXTERNAL = "EXTERNAL";
}
WindowsSasl::WindowsSasl( const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf )
- : settings(username, password, serviceName, hostName, minSsf, maxSsf)
+ : settings(username, password, serviceName, hostName, minSsf, maxSsf)
{
}
-WindowsSasl::~WindowsSasl()
+WindowsSasl::~WindowsSasl()
{
}
@@ -135,21 +136,28 @@ bool WindowsSasl::start(const std::strin
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep(" ");
+ bool haveExt = false;
bool havePlain = false;
bool haveAnon = false;
tokenizer mechs(mechanisms, sep);
for (tokenizer::iterator mech = mechs.begin();
mech != mechs.end();
++mech) {
- if (*mech == ANONYMOUS)
+ if (*mech == EXTERNAL)
+ haveExt = true;
+ else if (*mech == ANONYMOUS)
haveAnon = true;
else if (*mech == PLAIN)
havePlain = true;
}
- if (!haveAnon && !havePlain)
+ if (!haveAnon && !havePlain && !haveExt)
throw InternalErrorException(QPID_MSG("Sasl error: no common mechanism"));
- if (havePlain) {
+ if (haveExt) {
+ mechanism = EXTERNAL;
+ response = ((char)0) + settings.username.c_str();
+ }
+ else if (havePlain) {
mechanism = PLAIN;
response = ((char)0) + settings.username + ((char)0) + settings.password;
}
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=1534714&r1=1534713&r2=1534714&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/client/windows/SslConnector.cpp Tue Oct 22 18:10:49 2013
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -30,6 +30,7 @@
#include "qpid/sys/Poller.h"
#include "qpid/sys/Time.h"
#include "qpid/sys/windows/check.h"
+#include "qpid/sys/windows/util.h"
#include "qpid/sys/windows/SslAsynchIO.h"
#include <boost/bind.hpp>
@@ -51,8 +52,10 @@ using qpid::sys::Socket;
class SslConnector : public qpid::client::TCPConnector
{
qpid::sys::windows::ClientSslAsynchIO *shim;
- boost::shared_ptr<qpid::sys::Poller> poller;
+ boost::shared_ptr<qpid::sys::Poller> poller;
std::string brokerHost;
+ HCERTSTORE certStore;
+ PCCERT_CONTEXT cert;
SCHANNEL_CRED cred;
CredHandle credHandle;
TimeStamp credExpiry;
@@ -62,11 +65,13 @@ class SslConnector : public qpid::client
void connect(const std::string& host, const std::string& port);
void connected(const Socket&);
+ void loadPrivCertStore();
+ void importHostCert(const ConnectionSettings&);
public:
SslConnector(boost::shared_ptr<qpid::sys::Poller>,
framing::ProtocolVersion pVersion,
- const ConnectionSettings&,
+ const ConnectionSettings&,
ConnectionImpl*);
};
@@ -82,13 +87,21 @@ namespace {
struct StaticInit {
StaticInit() {
try {
+ CommonOptions common("", "", QPIDC_CONF_FILE);
+ qpid::sys::ssl::SslOptions options;
+ common.parse(0, 0, common.clientConfig, true);
+ options.parse (0, 0, common.clientConfig, true);
Connector::registerFactory("ssl", &create);
+ initWinSsl(options);
} catch (const std::exception& e) {
QPID_LOG(error, "Failed to initialise SSL connector: " << e.what());
}
};
~StaticInit() { }
} init;
+
+ std::string getPasswd(const std::string& filename);
+ PCCERT_CONTEXT findCertificate(const std::string& name, HCERTSTORE& store);
}
void SslConnector::negotiationDone(SECURITY_STATUS status)
@@ -107,6 +120,19 @@ SslConnector::SslConnector(boost::shared
{
memset(&cred, 0, sizeof(cred));
cred.dwVersion = SCHANNEL_CRED_VERSION;
+
+ // 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);
+ // assign the certificate into the credentials
+ cred.paCred = &cert;
+ cred.cCreds = 1;
+ }
+
SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL,
UNISP_NAME,
SECPKG_CRED_OUTBOUND,
@@ -123,6 +149,9 @@ SslConnector::SslConnector(boost::shared
SslConnector::~SslConnector()
{
+ if (cert)
+ ::CertFreeCertificateContext(cert);
+ ::CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG);
::FreeCredentialsHandle(&credHandle);
}
@@ -146,4 +175,159 @@ void SslConnector::connected(const Socke
shim->start(poller);
}
+
+void SslConnector::loadPrivCertStore()
+{
+ // Get a handle to the system store or pkcs#12 file
+ qpid::sys::ssl::SslOptions& opts = qpid::sys::ssl::SslOptions::global;
+ if (opts.certFilename.empty()) {
+ // opening the system store
+ const char *store = opts.certStore.empty() ? "MY" : opts.certStore.c_str();
+ certStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, NULL,
+ CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG |
+ 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);
+ }
+ QPID_LOG(debug, "SslConnector using certifcates from system store: " << store);
+ } else {
+ // opening the store from file and populating it with a private key
+ HANDLE certFileHandle = NULL;
+ certFileHandle = CreateFile(opts.certFilename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ 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);
+ }
+ std::vector<BYTE> certEncoded;
+ DWORD certEncodedSize = 0L;
+ const DWORD fileSize = GetFileSize(certFileHandle, NULL);
+ if (INVALID_FILE_SIZE != fileSize) {
+ certEncoded.resize(fileSize);
+ bool result = false;
+ result = ReadFile(certFileHandle, &certEncoded[0],
+ fileSize,
+ &certEncodedSize,
+ NULL);
+ 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);
+ }
+ }
+ else {
+ HRESULT status = GetLastError();
+ QPID_LOG(warning, "Unable to read the certificate file " << opts.certFilename);
+ throw QPID_WINDOWS_ERROR(status);
+ }
+ CloseHandle(certFileHandle);
+
+ CRYPT_DATA_BLOB blobData;
+ blobData.cbData = certEncodedSize;
+ blobData.pbData = &certEncoded[0];
+
+ // get passwd from file and convert to null terminated wchar_t (Windows UCS2)
+ std::string passwd = getPasswd(opts.certPasswordFile);
+ 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);
+ }
+
+ 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);
+ }
+ QPID_LOG(debug, "SslConnector using certificate from pkcs#12 file: " << opts.certFilename);
+ }
+}
+
+
+namespace {
+
+PCCERT_CONTEXT findCertificate(const std::string& name, HCERTSTORE& store)
+{
+ // search for the certificate by Friendly Name
+ PCCERT_CONTEXT tmpctx = NULL;
+ while (tmpctx = CertEnumCertificatesInStore(store, tmpctx)) {
+ DWORD len = CertGetNameString(tmpctx, CERT_NAME_FRIENDLY_DISPLAY_TYPE,
+ 0, NULL, NULL, 0);
+ if (len == 1)
+ continue;
+ std::vector<char> ctxname(len);
+ CertGetNameString(tmpctx, CERT_NAME_FRIENDLY_DISPLAY_TYPE,
+ 0, NULL, &ctxname[0], len);
+ bool found = !name.compare(&ctxname[0]);
+ if (found)
+ break;
+ }
+
+ // 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"));
+ }
+ return tmpctx;
+}
+
+
+std::string getPasswd(const std::string& filename)
+{
+ std::string passwd;
+ if (filename == "")
+ return passwd;
+
+ HANDLE pwfHandle = CreateFile(filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (INVALID_HANDLE_VALUE == pwfHandle) {
+ HRESULT status = GetLastError();
+ QPID_LOG(warning, "Failed to open the password file: " << filename);
+ throw QPID_WINDOWS_ERROR(status);
+ }
+
+ const DWORD fileSize = GetFileSize(pwfHandle, NULL);
+ if (fileSize == INVALID_FILE_SIZE) {
+ CloseHandle(pwfHandle);
+ throw qpid::Exception(QPID_MSG("Cannot read password file"));
+ }
+
+ std::vector<char> pwbuf;
+ pwbuf.resize(fileSize);
+ DWORD nbytes = 0;
+ 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);
+ }
+ CloseHandle(pwfHandle);
+
+ if (nbytes == 0)
+ return passwd;
+
+ while (nbytes) {
+ if ((pwbuf[nbytes-1] == 012) || (pwbuf[nbytes-1] == 015))
+ nbytes--;
+ else
+ break;
+ }
+
+ if (nbytes)
+ passwd.assign(&pwbuf[0], nbytes);
+
+ return passwd;
+}
+} // namespace
+
}}} // namespace qpid::client::windows
Added: qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.cpp?rev=1534714&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.cpp (added)
+++ qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.cpp Tue Oct 22 18:10:49 2013
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/sys/windows/util.h"
+#include "qpid/Exception.h"
+#include "qpid/sys/SystemInfo.h"
+
+#include <iostream>
+#include <fstream>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+static const std::string LOCALHOST("127.0.0.1");
+
+std::string defaultCertName()
+{
+ Address address;
+ if (SystemInfo::getLocalHostname(address)) {
+ return address.host;
+ } else {
+ return LOCALHOST;
+ }
+}
+
+SslOptions::SslOptions() : qpid::Options("SSL Settings"),
+ certName(defaultCertName())
+{
+ addOptions()
+ ("ssl-cert-password-file", optValue(certPasswordFile, "PATH"), "File containing password to use for accessing certificates")
+ ("ssl-cert-store", optValue(certStore, "NAME"), "Windows certificate store containing the certificate")
+ ("ssl-cert-Filename", optValue(certFilename, "PATH"), "Path to PKCS#12 file containing the certificate")
+ ("ssl-cert-name", optValue(certName, "NAME"), "Friendly Name of the certificate to use");
+}
+
+SslOptions& SslOptions::operator=(const SslOptions& o)
+{
+ certStore = o.certStore;
+ certName = o.certName;
+ certPasswordFile = o.certPasswordFile;
+ certFilename = o.certFilename;
+
+ return *this;
+}
+
+SslOptions SslOptions::global;
+
+void initWinSsl(const SslOptions& options, bool)
+{
+ SslOptions::global = options;
+}
+}}} // namespace qpid::sys::ssl
Added: qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.h?rev=1534714&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.h (added)
+++ qpid/trunk/qpid/cpp/src/qpid/sys/windows/util.h Tue Oct 22 18:10:49 2013
@@ -0,0 +1,50 @@
+#ifndef QPID_SYS_SSL_UTIL_H
+#define QPID_SYS_SSL_UTIL_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/CommonImportExport.h"
+#include "qpid/Options.h"
+#include <string>
+
+namespace qpid {
+namespace sys {
+namespace ssl {
+
+struct SslOptions : qpid::Options
+{
+ QPID_COMMON_EXTERN static SslOptions global;
+
+ std::string certStore;
+ std::string certName;
+ std::string certPasswordFile;
+ std::string certFilename;
+
+ QPID_COMMON_EXTERN SslOptions();
+ QPID_COMMON_EXTERN SslOptions& operator=(const SslOptions&);
+};
+
+QPID_COMMON_EXTERN void initWinSsl(const SslOptions& options, bool server = false);
+
+}}} // namespace qpid::sys::ssl
+
+#endif /*!QPID_SYS_SSL_UTIL_H*/
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org