diff options
Diffstat (limited to 'qpid/cpp/src/qpid/sys/ssl')
-rw-r--r-- | qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp | 379 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/sys/ssl/SslSocket.h | 112 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/sys/ssl/check.cpp | 85 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/sys/ssl/check.h | 57 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/sys/ssl/util.cpp | 127 | ||||
-rw-r--r-- | qpid/cpp/src/qpid/sys/ssl/util.h | 50 |
6 files changed, 810 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp b/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp new file mode 100644 index 0000000000..32bc78d22d --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ssl/SslSocket.cpp @@ -0,0 +1,379 @@ +/* + * + * 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/ssl/SslSocket.h" +#include "qpid/sys/SocketAddress.h" +#include "qpid/sys/ssl/check.h" +#include "qpid/sys/ssl/util.h" +#include "qpid/Exception.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" +#include "qpid/log/Statement.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <poll.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <cstdlib> +#include <string.h> +#include <iostream> + +#include <private/pprio.h> +#include <nss.h> +#include <pk11pub.h> +#include <ssl.h> +#include <key.h> +#include <sslerr.h> + +#include <boost/format.hpp> + +namespace qpid { +namespace sys { +namespace ssl { + +namespace { +const std::string DOMAIN_SEPARATOR("@"); +const std::string DC_SEPARATOR("."); +const std::string DC("DC"); +const std::string DN_DELIMS(" ,="); + +std::string getDomainFromSubject(std::string subject) +{ + std::string::size_type last = subject.find_first_not_of(DN_DELIMS, 0); + std::string::size_type i = subject.find_first_of(DN_DELIMS, last); + + std::string domain; + bool nextTokenIsDC = false; + while (std::string::npos != i || std::string::npos != last) + { + std::string token = subject.substr(last, i - last); + if (nextTokenIsDC) { + if (domain.size()) domain += DC_SEPARATOR; + domain += token; + nextTokenIsDC = false; + } else if (token == DC) { + nextTokenIsDC = true; + } + last = subject.find_first_not_of(DN_DELIMS, i); + i = subject.find_first_of(DN_DELIMS, last); + } + return domain; +} +} + +SslSocket::SslSocket(const std::string& certName, bool clientAuth) : + nssSocket(0), certname(certName), prototype(0), hostnameVerification(true) +{ + //configure prototype socket: + prototype = SSL_ImportFD(0, PR_NewTCPSocket()); + + if (clientAuth) { + NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUEST_CERTIFICATE, PR_TRUE)); + NSS_CHECK(SSL_OptionSet(prototype, SSL_REQUIRE_CERTIFICATE, PR_TRUE)); + } +} + +/** + * This form of the constructor is used with the server-side sockets + * returned from accept. Because we use posix accept rather than + * PR_Accept, we have to reset the handshake. + */ +SslSocket::SslSocket(int fd, PRFileDesc* model) : BSDSocket(fd), nssSocket(0), prototype(0) +{ + nssSocket = SSL_ImportFD(model, PR_ImportTCPSocket(fd)); + NSS_CHECK(SSL_ResetHandshake(nssSocket, PR_TRUE)); +} + +void SslSocket::ignoreHostnameVerificationFailure() +{ + hostnameVerification = false; +} + +void SslSocket::setNonblocking() const +{ + if (!nssSocket) { + BSDSocket::setNonblocking(); + return; + } + PRSocketOptionData option; + option.option = PR_SockOpt_Nonblocking; + option.value.non_blocking = true; + PR_SetSocketOption(nssSocket, &option); +} + +void SslSocket::setTcpNoDelay() const +{ + if (!nssSocket) { + BSDSocket::setTcpNoDelay(); + return; + } + PRSocketOptionData option; + option.option = PR_SockOpt_NoDelay; + option.value.no_delay = true; + PR_SetSocketOption(nssSocket, &option); +} + +void SslSocket::connect(const SocketAddress& addr) const +{ + BSDSocket::connect(addr); +} + +namespace { +SECStatus bad_certificate(void* arg, PRFileDesc* /*fd*/) { + switch (PR_GetError()) { + case SSL_ERROR_BAD_CERT_DOMAIN: + QPID_LOG(info, "Ignoring hostname verification failure for " << (const char*) arg); + return SECSuccess; + default: + return SECFailure; + } +} +} + +void SslSocket::finishConnect(const SocketAddress& addr) const +{ + nssSocket = SSL_ImportFD(0, PR_ImportTCPSocket(fd)); + + void* arg; + // Use the connection's cert-name if it has one; else use global cert-name + if (certname != "") { + arg = const_cast<char*>(certname.c_str()); + } else if (SslOptions::global.certName.empty()) { + arg = 0; + } else { + arg = const_cast<char*>(SslOptions::global.certName.c_str()); + } + NSS_CHECK(SSL_GetClientAuthDataHook(nssSocket, NSS_GetClientAuthData, arg)); + + url = addr.getHost(); + if (!hostnameVerification) { + NSS_CHECK(SSL_BadCertHook(nssSocket, bad_certificate, const_cast<char*>(url.data()))); + } + NSS_CHECK(SSL_SetURL(nssSocket, url.data())); + + NSS_CHECK(SSL_ResetHandshake(nssSocket, PR_FALSE)); + NSS_CHECK(SSL_ForceHandshake(nssSocket)); +} + +void SslSocket::close() const +{ + if (!nssSocket) { + BSDSocket::close(); + return; + } + if (fd > 0) { + PR_Close(nssSocket); + fd = -1; + } +} + +int SslSocket::listen(const SocketAddress& sa, int backlog) const +{ + //get certificate and key (is this the correct way?) + std::string cName( (certname == "") ? "localhost.localdomain" : certname); + CERTCertificate *cert = PK11_FindCertFromNickname(const_cast<char*>(cName.c_str()), 0); + if (!cert) throw Exception(QPID_MSG("Failed to load certificate '" << cName << "'")); + SECKEYPrivateKey *key = PK11_FindKeyByAnyCert(cert, 0); + if (!key) throw Exception(QPID_MSG("Failed to retrieve private key from certificate")); + NSS_CHECK(SSL_ConfigSecureServer(prototype, cert, key, NSS_FindCertKEAType(cert))); + SECKEY_DestroyPrivateKey(key); + CERT_DestroyCertificate(cert); + + return BSDSocket::listen(sa, backlog); +} + +Socket* SslSocket::accept() const +{ + QPID_LOG(trace, "Accepting SSL connection."); + int afd = ::accept(fd, 0, 0); + if ( afd >= 0) { + return new SslSocket(afd, prototype); + } else if (errno == EAGAIN) { + return 0; + } else { + throw QPID_POSIX_ERROR(errno); + } +} + +#define SSL_STREAM_MAX_WAIT_ms 20 +#define SSL_STREAM_MAX_RETRIES 2 + +static bool isSslStream(int afd) { + int retries = SSL_STREAM_MAX_RETRIES; + unsigned char buf[5] = {}; + + do { + struct pollfd fd = {afd, POLLIN, 0}; + + /* + * Note that this is blocking the accept thread, so connections that + * send no data can limit the rate at which we can accept new + * connections. + */ + if (::poll(&fd, 1, SSL_STREAM_MAX_WAIT_ms) > 0) { + errno = 0; + int result = recv(afd, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT); + if (result == sizeof(buf)) { + break; + } + if (errno && errno != EAGAIN) { + int err = errno; + ::close(afd); + throw QPID_POSIX_ERROR(err); + } + } + } while (retries-- > 0); + + if (retries < 0) { + return false; + } + + /* + * SSLv2 Client Hello format + * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html + * + * Bytes 0-1: RECORD-LENGTH + * Byte 2: MSG-CLIENT-HELLO (1) + * Byte 3: CLIENT-VERSION-MSB + * Byte 4: CLIENT-VERSION-LSB + * + * Allowed versions: + * 2.0 - SSLv2 + * 3.0 - SSLv3 + * 3.1 - TLS 1.0 + * 3.2 - TLS 1.1 + * 3.3 - TLS 1.2 + * + * The version sent in the Client-Hello is the latest version supported by + * the client. NSS may send version 3.x in an SSLv2 header for + * maximum compatibility. + */ + bool isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO + ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3) + (buf[3] == 2 && buf[4] == 0)); // SSL 2 + + /* + * SSLv3/TLS Client Hello format + * RFC 2246 + * + * Byte 0: ContentType (handshake - 22) + * Bytes 1-2: ProtocolVersion {major, minor} + * + * Allowed versions: + * 3.0 - SSLv3 + * 3.1 - TLS 1.0 + * 3.2 - TLS 1.1 + * 3.3 - TLS 1.2 + */ + bool isSSL3Handshake = buf[0] == 22 && // handshake + (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3) + + return isSSL2Handshake || isSSL3Handshake; +} + +SslMuxSocket::SslMuxSocket(const std::string& certName, bool clientAuth) : + SslSocket(certName, clientAuth) +{ +} + +Socket* SslMuxSocket::accept() const +{ + int afd = ::accept(fd, 0, 0); + if (afd >= 0) { + QPID_LOG(trace, "Accepting connection with optional SSL wrapper."); + if (isSslStream(afd)) { + QPID_LOG(trace, "Accepted SSL connection."); + return new SslSocket(afd, prototype); + } else { + QPID_LOG(trace, "Accepted Plaintext connection."); + return new BSDSocket(afd); + } + } else if (errno == EAGAIN) { + return 0; + } else { + throw QPID_POSIX_ERROR(errno); + } +} + +int SslSocket::read(void *buf, size_t count) const +{ + return PR_Read(nssSocket, buf, count); +} + +int SslSocket::write(const void *buf, size_t count) const +{ + return PR_Write(nssSocket, buf, count); +} + +void SslSocket::setCertName(const std::string& name) +{ + certname = name; +} + + +/** get the bit length of the current cipher's key */ +int SslSocket::getKeyLen() const +{ + int enabled = 0; + int keySize = 0; + SECStatus rc; + + rc = SSL_SecurityStatus( nssSocket, + &enabled, + NULL, + NULL, + &keySize, + NULL, NULL ); + if (rc == SECSuccess && enabled) { + return keySize; + } + return 0; +} + +std::string SslSocket::getClientAuthId() const +{ + std::string authId; + CERTCertificate* cert = SSL_PeerCertificate(nssSocket); + if (cert) { + char *cn = CERT_GetCommonName(&(cert->subject)); + if (cn) { + authId = std::string(cn); + /* + * The NSS function CERT_GetDomainComponentName only returns + * the last component of the domain name, so we have to parse + * the subject manually to extract the full domain. + */ + std::string domain = getDomainFromSubject(cert->subjectName); + if (!domain.empty()) { + authId += DOMAIN_SEPARATOR; + authId += domain; + } + } + CERT_DestroyCertificate(cert); + } + return authId; +} + +}}} // namespace qpid::sys::ssl diff --git a/qpid/cpp/src/qpid/sys/ssl/SslSocket.h b/qpid/cpp/src/qpid/sys/ssl/SslSocket.h new file mode 100644 index 0000000000..2407a1bf4b --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ssl/SslSocket.h @@ -0,0 +1,112 @@ +#ifndef _sys_ssl_Socket_h +#define _sys_ssl_Socket_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/sys/IOHandle.h" +#include "qpid/sys/posix/BSDSocket.h" +#include <nspr.h> + +#include <string> + +struct sockaddr; + +namespace qpid { +namespace sys { + +class Duration; + +namespace ssl { + +class SslSocket : public qpid::sys::BSDSocket +{ +public: + /** Create a socket wrapper for descriptor. + *@param certName name of certificate to use to identify the socket + */ + SslSocket(const std::string& certName = "", bool clientAuth = false); + + /** Proceed with connect inspite of hostname verifcation failures*/ + void ignoreHostnameVerificationFailure(); + + /** Set socket non blocking */ + void setNonblocking() const; + + /** Set tcp-nodelay */ + void setTcpNoDelay() const; + + /** Set SSL cert-name. Allows the cert-name to be set per + * connection, overriding global cert-name settings from + * NSSInit().*/ + void setCertName(const std::string& certName); + + void connect(const SocketAddress&) const; + void finishConnect(const SocketAddress&) const; + + void close() const; + + /** Bind to a port and start listening. + *@param port 0 means choose an available port. + *@param backlog maximum number of pending connections. + *@return The bound port. + */ + int listen(const SocketAddress&, int backlog = 10) const; + + /** + * Accept a connection from a socket that is already listening + * and has an incoming connection + */ + virtual Socket* accept() const; + + // TODO The following are raw operations, maybe they need better wrapping? + int read(void *buf, size_t count) const; + int write(const void *buf, size_t count) const; + + int getKeyLen() const; + std::string getClientAuthId() const; + +protected: + mutable PRFileDesc* nssSocket; + std::string certname; + mutable std::string url; + + /** + * 'model' socket, with configuration to use when importing + * accepted sockets for use as ssl sockets. Set on listen(), used + * in accept to pass through to newly created socket instances. + */ + mutable PRFileDesc* prototype; + bool hostnameVerification; + + SslSocket(int fd, PRFileDesc* model); + friend class SslMuxSocket; // Needed for this constructor +}; + +class SslMuxSocket : public SslSocket +{ +public: + SslMuxSocket(const std::string& certName = "", bool clientAuth = false); + Socket* accept() const; +}; + +}}} +#endif /*!_sys_ssl_Socket_h*/ diff --git a/qpid/cpp/src/qpid/sys/ssl/check.cpp b/qpid/cpp/src/qpid/sys/ssl/check.cpp new file mode 100644 index 0000000000..72a2e265bd --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ssl/check.cpp @@ -0,0 +1,85 @@ +/* + * + * 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/ssl/check.h" +#include <secerr.h> +#include <sslerr.h> +#include <boost/format.hpp> + +using boost::format; +using boost::str; + +namespace qpid { +namespace sys { +namespace ssl { + +ErrorString::ErrorString() : code(PR_GetError()), buffer(new char[PR_GetErrorTextLength()]), used(PR_GetErrorText(buffer)) {} + +ErrorString::~ErrorString() +{ + delete[] buffer; +} + +std::string ErrorString::getString() const +{ + std::string msg = std::string(buffer, used); + if (!used) { + //seems most of the NSPR/NSS errors don't have text set for + //them, add a few specific ones in here. (TODO: more complete + //list?): + return getErrorString(code); + } else { + return str(format("%1% [%2%]") % msg % code); + } +} + +std::string getErrorString(int code) +{ + std::string msg; + switch (code) { + case SSL_ERROR_EXPORT_ONLY_SERVER: msg = "Unable to communicate securely. Peer does not support high-grade encryption."; break; + case SSL_ERROR_US_ONLY_SERVER: msg = "Unable to communicate securely. Peer requires high-grade encryption which is not supported."; break; + case SSL_ERROR_NO_CYPHER_OVERLAP: msg = "Cannot communicate securely with peer: no common encryption algorithm(s)."; break; + case SSL_ERROR_NO_CERTIFICATE: msg = "Unable to find the certificate or key necessary for authentication."; break; + case SSL_ERROR_BAD_CERTIFICATE: msg = "Unable to communicate securely with peer: peers's certificate was rejected."; break; + case SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE: msg = "Unsupported certificate type."; break; + case SSL_ERROR_WRONG_CERTIFICATE: msg = "Client authentication failed: private key in key database does not correspond to public key in certificate database."; break; + case SSL_ERROR_BAD_CERT_DOMAIN: msg = "Unable to communicate securely with peer: requested domain name does not match the server's certificate."; break; + case SSL_ERROR_BAD_CERT_ALERT: msg = "SSL peer cannot verify your certificate."; break; + case SSL_ERROR_REVOKED_CERT_ALERT: msg = "SSL peer rejected your certificate as revoked."; break; + case SSL_ERROR_EXPIRED_CERT_ALERT: msg = "SSL peer rejected your certificate as expired."; break; + + case PR_DIRECTORY_LOOKUP_ERROR: msg = "A directory lookup on a network address has failed"; break; + case PR_CONNECT_RESET_ERROR: msg = "TCP connection reset by peer"; break; + case PR_END_OF_FILE_ERROR: msg = "Encountered end of file"; break; + case SEC_ERROR_EXPIRED_CERTIFICATE: msg = "Peer's certificate has expired"; break; + default: msg = (code < -6000) ? "NSS error" : "NSPR error"; break; + } + return str(format("%1% [%2%]") % msg % code); +} + +std::ostream& operator<<(std::ostream& out, const ErrorString& err) +{ + out << err.getString(); + return out; +} + + +}}} // namespace qpid::sys::ssl diff --git a/qpid/cpp/src/qpid/sys/ssl/check.h b/qpid/cpp/src/qpid/sys/ssl/check.h new file mode 100644 index 0000000000..28d3c74ad0 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ssl/check.h @@ -0,0 +1,57 @@ +#ifndef QPID_SYS_SSL_CHECK_H +#define QPID_SYS_SSL_CHECK_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/Msg.h" + +#include <iostream> +#include <string> +#include <nspr.h> +#include <nss.h> + +namespace qpid { +namespace sys { +namespace ssl { + +std::string getErrorString(int code); + +class ErrorString +{ + public: + ErrorString(); + ~ErrorString(); + std::string getString() const; + private: + const int code; + char* const buffer; + const size_t used; +}; + +std::ostream& operator<<(std::ostream& out, const ErrorString& err); + +}}} // namespace qpid::sys::ssl + + +#define NSS_CHECK(value) if (value != SECSuccess) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); } +#define PR_CHECK(value) if (value != PR_SUCCESS) { throw Exception(QPID_MSG("Failed: " << qpid::sys::ssl::ErrorString())); } + +#endif /*!QPID_SYS_SSL_CHECK_H*/ diff --git a/qpid/cpp/src/qpid/sys/ssl/util.cpp b/qpid/cpp/src/qpid/sys/ssl/util.cpp new file mode 100644 index 0000000000..9f5493cbbf --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ssl/util.cpp @@ -0,0 +1,127 @@ +/* + * + * 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/ssl/util.h" +#include "qpid/sys/ssl/check.h" +#include "qpid/Exception.h" +#include "qpid/sys/SystemInfo.h" + +#include <unistd.h> +#include <nspr.h> +#include <nss.h> +#include <pk11pub.h> +#include <ssl.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()), + exportPolicy(false) +{ + addOptions() + ("ssl-use-export-policy", optValue(exportPolicy), "Use NSS export policy") + ("ssl-cert-password-file", optValue(certPasswordFile, "PATH"), "File containing password to use for accessing certificate database") + ("ssl-cert-db", optValue(certDbPath, "PATH"), "Path to directory containing certificate database") + ("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use"); +} + +SslOptions& SslOptions::operator=(const SslOptions& o) +{ + certDbPath = o.certDbPath; + certName = o.certName; + certPasswordFile = o.certPasswordFile; + exportPolicy = o.exportPolicy; + return *this; +} + +char* promptForPassword(PK11SlotInfo*, PRBool retry, void*) +{ + if (retry) return 0; + //TODO: something else? + return PL_strdup(getpass("Please enter the password for accessing the certificate database:")); +} + +SslOptions SslOptions::global; + +char* readPasswordFromFile(PK11SlotInfo*, PRBool retry, void*) +{ + const std::string& passwordFile = SslOptions::global.certPasswordFile; + if (retry || passwordFile.empty()) return 0; + std::ifstream file(passwordFile.c_str()); + if (!file) return 0; + + std::string password; + getline(file, password); + return PL_strdup(password.c_str()); +} + +void initNSS(const SslOptions& options, bool server) +{ + SslOptions::global = options; + if (options.certPasswordFile.empty()) { + PK11_SetPasswordFunc(promptForPassword); + } else { + PK11_SetPasswordFunc(readPasswordFromFile); + } + NSS_CHECK(NSS_Init(options.certDbPath.c_str())); + if (options.exportPolicy) { + NSS_CHECK(NSS_SetExportPolicy()); + } else { + NSS_CHECK(NSS_SetDomesticPolicy()); + } + if (server) { + //use defaults for all args, TODO: may want to make this configurable + SSL_ConfigServerSessionIDCache(0, 0, 0, 0); + } + + // disable SSLv2 and SSLv3 versions of the protocol - they are + // no longer considered secure + SSLVersionRange vrange; + const uint16_t tlsv1 = 0x0301; // Protocol version for TLSv1.0 + NSS_CHECK(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange)); + if (vrange.min < tlsv1) { + vrange.min = tlsv1; + NSS_CHECK(SSL_VersionRangeSetDefault(ssl_variant_stream, &vrange)); + } +} + +void shutdownNSS() +{ + NSS_Shutdown(); +} + +}}} // namespace qpid::sys::ssl diff --git a/qpid/cpp/src/qpid/sys/ssl/util.h b/qpid/cpp/src/qpid/sys/ssl/util.h new file mode 100644 index 0000000000..f34adab7be --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ssl/util.h @@ -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/Options.h" +#include <string> + +namespace qpid { +namespace sys { +namespace ssl { + +struct SslOptions : qpid::Options +{ + static SslOptions global; + + std::string certDbPath; + std::string certName; + std::string certPasswordFile; + bool exportPolicy; + + SslOptions(); + SslOptions& operator=(const SslOptions&); +}; + +void initNSS(const SslOptions& options, bool server = false); +void shutdownNSS(); + +}}} // namespace qpid::sys::ssl + +#endif /*!QPID_SYS_SSL_UTIL_H*/ |