| /* |
| * |
| * 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 <string> |
| #include <windows.h> |
| #include "qpid/Msg.h" |
| #include "qpid/log/Logger.h" |
| #include "qpid/sys/windows/check.h" |
| #include "qpid/sys/windows/util.h" |
| #include "qpid/sys/windows/SslCredential.h" |
| |
| |
| namespace qpid { |
| namespace sys { |
| namespace windows { |
| |
| |
| SslCredential::SslCredential() : certStore(0), cert(0), hostnameVerification(true) |
| { |
| SecInvalidateHandle(&credHandle); |
| memset(&cred, 0, sizeof(cred)); |
| cred.dwVersion = SCHANNEL_CRED_VERSION; |
| cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS; |
| } |
| |
| SslCredential::~SslCredential() |
| { |
| if (SecIsValidHandle(&credHandle)) |
| ::FreeCredentialsHandle(&credHandle); |
| if (cert) |
| ::CertFreeCertificateContext(cert); |
| if (certStore) |
| ::CertCloseStore(certStore, CERT_CLOSE_STORE_FORCE_FLAG); |
| } |
| |
| bool SslCredential::load(const std::string& certName) |
| { |
| cert = findCertificate(certName); |
| if (cert != NULL) { |
| // assign the certificate into the credentials |
| cred.paCred = &cert; |
| cred.cCreds = 1; |
| } |
| if (!hostnameVerification) |
| cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; |
| |
| SECURITY_STATUS status = ::AcquireCredentialsHandle(NULL, |
| UNISP_NAME, |
| SECPKG_CRED_OUTBOUND, |
| NULL, |
| &cred, |
| NULL, |
| NULL, |
| &credHandle, |
| &credExpiry); |
| if (status != SEC_E_OK) |
| throw QPID_WINDOWS_ERROR(status); |
| |
| return (cert != NULL); |
| } |
| |
| CredHandle SslCredential::handle() |
| { |
| return credHandle; |
| } |
| |
| std::string SslCredential::error() |
| { |
| // Certificate needed after all. Return main error and log additional context |
| if (!loadError.logMessage.empty()) |
| QPID_LOG(warning, loadError.logMessage); |
| return loadError.error; |
| } |
| |
| void SslCredential::ignoreHostnameVerificationFailure(){ |
| hostnameVerification = false; |
| } |
| |
| void SslCredential::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 a system store, names are not case sensitive |
| std::string store = opts.certStore.empty() ? "my" : opts.certStore; |
| std::transform(store.begin(), store.end(), store.begin(), ::tolower); |
| // map confusing GUI name to actual registry store name |
| if (store == "personal") |
| store = "my"; |
| certStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, NULL, |
| CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG | |
| CERT_SYSTEM_STORE_CURRENT_USER, store.c_str()); |
| if (!certStore) { |
| HRESULT status = GetLastError(); |
| loadError.set(Msg() << "Could not open system certificate store: " << store, status); |
| return; |
| } |
| 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(); |
| loadError.set(Msg() << "Failed to open the file holding the private key: " << opts.certFilename, status); |
| return; |
| } |
| 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(); |
| CloseHandle(certFileHandle); |
| loadError.set(Msg() << "Reading the private key from file failed " << opts.certFilename, status); |
| return; |
| } |
| } |
| else { |
| HRESULT status = GetLastError(); |
| loadError.set(Msg() << "Unable to read the certificate file " << opts.certFilename, status); |
| return; |
| } |
| 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); |
| if (loadError.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(); |
| loadError.set("Error converting password from UTF8", status); |
| return; |
| } |
| |
| certStore = PFXImportCertStore(&blobData, &pwUCS2[0], 0); |
| if (certStore == NULL) { |
| HRESULT status = GetLastError(); |
| loadError.set("Failed to open the certificate store", status); |
| return; |
| } |
| QPID_LOG(debug, "SslConnector using certificate from pkcs#12 file: " << opts.certFilename); |
| } |
| } |
| |
| |
| PCCERT_CONTEXT SslCredential::findCertificate(const std::string& name) |
| { |
| loadPrivCertStore(); |
| if (loadError.pending()) |
| return NULL; |
| |
| // search for the certificate by Friendly Name |
| PCCERT_CONTEXT tmpctx = NULL; |
| while (tmpctx = CertEnumCertificatesInStore(certStore, 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) { |
| loadError.set(Msg() << "Client SSL/TLS certificate not found in the certificate store for name " << name, |
| "client certificate not found"); |
| } |
| return tmpctx; |
| } |
| |
| |
| std::string SslCredential::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(); |
| loadError.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); |
| loadError.set("", "Cannot read password file"); |
| return passwd; |
| } |
| |
| std::vector<char> pwbuf; |
| pwbuf.resize(fileSize); |
| DWORD nbytes = 0; |
| if (!ReadFile(pwfHandle, &pwbuf[0], fileSize, &nbytes, NULL)) { |
| HRESULT status = GetLastError(); |
| CloseHandle(pwfHandle); |
| loadError.set("Error reading password file", status); |
| return passwd; |
| } |
| 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; |
| } |
| |
| void SslCredential::SavedError::set(const std::string &lm, const std::string es) { |
| logMessage = lm; |
| error = es; |
| } |
| |
| void SslCredential::SavedError::set(const std::string &lm, int status) { |
| logMessage = lm; |
| error = qpid::sys::strError(status); |
| } |
| |
| void SslCredential::SavedError::clear() { |
| logMessage.clear(); |
| error.clear(); |
| } |
| |
| bool SslCredential::SavedError::pending() { |
| return !logMessage.empty() || !error.empty(); |
| } |
| |
| }}} |