37#include "blocxx/BLOCXX_config.h"
38#ifdef BLOCXX_HAVE_OPENSSL
54#include <openssl/rand.h>
55#include <openssl/err.h>
61#include <sys/resource.h>
65#ifdef BLOCXX_HAVE_SYS_TYPES_H
69#ifdef BLOCXX_HAVE_SYS_STAT_H
73#ifdef BLOCXX_HAVE_UNISTD_H
86struct CRYPTO_dynlock_value
103static struct CRYPTO_dynlock_value * dyn_create_function(
const char *,
int)
105 return new CRYPTO_dynlock_value;
109static void dyn_lock_function(
int mode,
struct CRYPTO_dynlock_value *l,
112 if (mode & CRYPTO_LOCK)
122static void dyn_destroy_function(
struct CRYPTO_dynlock_value *l,
128static unsigned long id_function()
133static void locking_function(
int mode,
int n,
const char*,
int)
135 if (mode & CRYPTO_LOCK)
149 X509Freer(X509* x509)
164enum SSLLibraryInit_t {
165 BLOCXX_SSL_LIBRARY_NOT_INITIALIZED,
166 BLOCXX_SSL_LIBRARY_INITIALIZED,
167 BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED
169SSLLibraryInit_t m_initState = BLOCXX_SSL_LIBRARY_NOT_INITIALIZED;
174 BLOCXX_SSL_LOCKS_NOT_USED,
175 BLOCXX_SSL_LOCKS_USED,
176 BLOCXX_SSL_LOCKS_DISABLED
178SSLLocks_t m_locksState = BLOCXX_SSL_LOCKS_NOT_USED;
186 MutexLock initLock(m_initStateGuard);
187 if (m_initState == BLOCXX_SSL_LIBRARY_NOT_INITIALIZED)
189 m_initState = BLOCXX_SSL_LIBRARY_INITIALIZED;
192 SSL_load_error_strings();
196 MutexLock locksLock(m_locksStateGuard);
197 if (m_locksState == BLOCXX_SSL_LOCKS_NOT_USED)
199 m_locksState = BLOCXX_SSL_LOCKS_USED;
203 mutex_buf =
new Mutex[CRYPTO_num_locks()];
206 CRYPTO_set_id_callback(id_function);
207 CRYPTO_set_locking_callback(locking_function);
211 CRYPTO_set_dynlock_create_callback(dyn_create_function);
212 CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
213 CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
219 if (SSLCtxMgr::isClient() || SSLCtxMgr::isServer())
221 Secure::rand_save_state();
225 MutexLock locksLock(m_locksStateGuard);
226 if (m_locksState == BLOCXX_SSL_LOCKS_USED)
228 CRYPTO_set_id_callback(NULL);
229 CRYPTO_set_locking_callback(NULL);
230 CRYPTO_set_dynlock_create_callback(NULL);
231 CRYPTO_set_dynlock_lock_callback(NULL);
232 CRYPTO_set_dynlock_destroy_callback(NULL);
235 m_locksState = BLOCXX_SSL_LOCKS_NOT_USED;
240struct SSLGlobalWorkFactory
242 static SSLGlobalWork* create(
int )
244 return new SSLGlobalWork();
253SSL_CTX* SSLCtxMgr::m_ctxClient = 0;
254SSL_CTX* SSLCtxMgr::m_ctxServer = 0;
255certVerifyFuncPtr_t SSLCtxMgr::m_clientCertVerifyCB = 0;
256certVerifyFuncPtr_t SSLCtxMgr::m_serverCertVerifyCB = 0;
261SSLCtxMgr::getOpenSSLErrorDescription()
263 BIO* bio = BIO_new(BIO_s_mem());
268 ERR_print_errors(bio);
270 long len = BIO_get_mem_data(bio, &p);
272 int freerv = BIO_free(bio);
279void SSLCtxMgr::disableSSLInit()
281 MutexLock lock(m_initStateGuard);
282 if (m_initState == BLOCXX_SSL_LIBRARY_INITIALIZED)
284 BLOCXX_THROW(SSLException,
"SSLCtxMgr::disableSSLInit(): init() cannot be disabled as it has already been called");
286 m_initState = BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
291void SSLCtxMgr::disableLocks()
293 MutexLock lock(m_locksStateGuard);
294 if (m_locksState == BLOCXX_SSL_LOCKS_USED)
296 BLOCXX_THROW(SSLException,
"SSLCtxMgr::disableSSLLocks(): locks cannot be disabled as they are already in use");
298 m_locksState = BLOCXX_SSL_LOCKS_DISABLED;
303Bool SSLCtxMgr::getSSLInitDisabled()
305 MutexLock lock(m_initStateGuard);
306 return m_initState == BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
311Bool SSLCtxMgr::getSSLLocksDisabled()
313 MutexLock lock(m_locksStateGuard);
314 return m_locksState == BLOCXX_SSL_LOCKS_DISABLED;
319SSLCtxMgr::initCtx(
const String& certfile,
const String& keyfile, EVP_PKEY* pkey)
321 g_SSLGlobalWork.get();
324 SSL_CTX* ctx = SSL_CTX_new(SSLv23_method());
327 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::initCtx(): SSL_CTX_new returned 0: %1", getOpenSSLErrorDescription()).c_str());
329 SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
330 if (!certfile.empty())
332 if (SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str()) != 1)
335 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::initCtx(): Couldn't read certificate from file: %1: %2",
336 certfile, getOpenSSLErrorDescription()).c_str());
340 int rv = SSL_CTX_use_PrivateKey(ctx, pkey);
344 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::initCtx(): Unable to set private key: %1",
345 getOpenSSLErrorDescription()).c_str());
348 else if (SSL_CTX_use_PrivateKey_file(ctx, keyfile.empty()?certfile.c_str():keyfile.c_str(), SSL_FILETYPE_PEM) != 1)
351 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::initCtx(): Couldn't read key from file: %1: %2",
352 keyfile.empty()?certfile:keyfile, getOpenSSLErrorDescription()).c_str());
363SSLCtxMgr::loadDHParams(SSL_CTX* ctx,
const String& file)
367 BIO* bio = BIO_new_file(file.c_str(),
"r");
370 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::loadDHParams(): Couldn't open DH file %1: %2", file, getOpenSSLErrorDescription()).c_str());
372 DH* ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
376 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::loadDHParams(): PEM_read_bio_DHparams failed: %1", getOpenSSLErrorDescription()).c_str());
378 if (SSL_CTX_set_tmp_dh(ctx, ret) != 1)
380 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::loadDHParams(): Couldn't set DH parameters because SSL_CTX_set_tmp_dh failed: %1", getOpenSSLErrorDescription()).c_str());
385SSLCtxMgr::generateEphRSAKey(SSL_CTX* ctx)
389 RSA* rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
392 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::generateEphRSAKey(): RSA_generate_key failed: %1", getOpenSSLErrorDescription()).c_str());
394 if (SSL_CTX_set_tmp_rsa(ctx, rsa) != 1)
397 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::generateEphRSAKey(): SSL_CTX_set_tmp_rsa failed. Couldn't set RSA key: %1", getOpenSSLErrorDescription()).c_str());
403SSLCtxMgr::initClient(
const String& certfile,
const String& keyfile)
409 m_ctxClient = initCtx(certfile,keyfile);
413SSLCtxMgr::initServer(
const String& certfile,
const String& keyfile)
415 if (certfile.empty())
417 BLOCXX_THROW(SSLException,
"SSLCtxMgr::initCtx(): no certificate file specified");
423 m_ctxServer = initCtx(certfile,keyfile);
425 generateEphRSAKey(m_ctxServer);
426 String sessID(
"SSL_SESSION_");
427 sessID += String(Secure::rand_range<UInt16>(0, 10000));
429 (SSL_MAX_SSL_SESSION_ID_LENGTH < (sessID.length())) ?
430 SSL_MAX_SSL_SESSION_ID_LENGTH : (sessID.length());
432 if (SSL_CTX_set_session_id_context(m_ctxServer,
reinterpret_cast<const unsigned char*
>(sessID.c_str()), sessIDLen) != 1)
434 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::initServer(): SSL_CTX_set_session_id_context failed: %1", getOpenSSLErrorDescription()).c_str());
436 SSL_CTX_set_verify(m_ctxServer, SSL_VERIFY_PEER , NULL);
441SSLCtxMgr::pem_passwd_cb(
char* buf,
int size,
int ,
444 String passwd =
GetPass::getPass(
"Enter the password for the SSL certificate: ");
446 strncpy(buf, passwd.c_str(), size);
447 buf[size - 1] =
'\0';
449 return passwd.length();
454SSLCtxMgr::checkClientCert(SSL* ssl,
const String& hostName)
456 return checkCert(ssl, hostName, m_clientCertVerifyCB);
461SSLCtxMgr::checkServerCert(SSL* ssl,
const String& hostName)
463 return checkCert(ssl, hostName, m_serverCertVerifyCB);
468SSLCtxMgr::checkCert(SSL* ssl,
const String& hostName,
469 certVerifyFuncPtr_t certVerifyCB)
486 X509 *peer = SSL_get_peer_certificate(ssl);
487 X509Freer x509freer(peer);
492 if (certVerifyCB(peer, hostName) == 0)
506SSLCtxMgr::sslRead(SSL* ssl,
char* buf,
int len)
508 int cc = SSL_ERROR_WANT_READ;
511 while (cc == SSL_ERROR_WANT_READ && retries < BLOCXX_SSL_RETRY_LIMIT)
513 r = SSL_read(ssl, buf, len);
514 cc = SSL_get_error(ssl, r);
522 case SSL_ERROR_ZERO_RETURN:
531SSLCtxMgr::sslWrite(SSL* ssl,
const char* buf,
int len)
540 SignalScope ss(SIGPIPE, SIG_IGN);
544 cc = SSL_ERROR_WANT_WRITE;
546 while(cc == SSL_ERROR_WANT_WRITE && retries < BLOCXX_SSL_RETRY_LIMIT)
548 r = SSL_write(ssl, buf + offset, myLen);
549 cc = SSL_get_error(ssl, r);
553 if (cc == SSL_ERROR_NONE)
572 MutexLock initLock(m_initStateGuard);
573 if (m_initState == BLOCXX_SSL_LIBRARY_INITIALIZED)
580 m_initState = BLOCXX_SSL_LIBRARY_NOT_INITIALIZED;
585SSLCtxMgr::uninitClient()
589 SSL_CTX_free(m_ctxClient);
595SSLCtxMgr::uninitServer()
599 SSL_CTX_free(m_ctxServer);
610static int verify_callback(
int ok, X509_STORE_CTX *store)
612 int index = SSL_get_ex_data_X509_STORE_CTX_idx();
617 SSL* ssl =
static_cast<SSL*
>(X509_STORE_CTX_get_ex_data(store, index));
622 OWSSLContext* owctx =
static_cast<OWSSLContext*
>(SSL_get_ex_data(ssl, SSLServerCtx::SSL_DATA_INDEX));
641 owctx->peerCertPassedVerify = OWSSLContext::VERIFY_FAIL;
647 if (owctx->peerCertPassedVerify != OWSSLContext::VERIFY_FAIL)
649 owctx->peerCertPassedVerify = OWSSLContext::VERIFY_PASS;
657 X509 *cert = X509_STORE_CTX_get_current_cert(store);
658 int depth = X509_STORE_CTX_get_error_depth(store);
659 int err = X509_STORE_CTX_get_error(store);
661 fprintf(stderr,
"-Error with certificate at depth: %i\n", depth);
662 X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
663 fprintf(stderr,
" issuer = %s\n", data);
664 X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
665 fprintf(stderr,
" subject = %s\n", data);
666 fprintf(stderr,
" err %i:%s\n", err, X509_verify_cert_error_string(err));
677SSLCtxBase::SSLCtxBase(
const SSLOpts& opts)
680 m_ctx = SSLCtxMgr::initCtx(opts.certfile, opts.keyfile, opts.pkey);
682 SSLCtxMgr::generateEphRSAKey(m_ctx);
683 String sessID(
"SSL_SESSION_");
684 sessID += String(Secure::rand_range<UInt16>(0, 10000));
686 (SSL_MAX_SSL_SESSION_ID_LENGTH < (sessID.length())) ?
687 SSL_MAX_SSL_SESSION_ID_LENGTH : (sessID.length());
689 if (SSL_CTX_set_session_id_context(m_ctx,
reinterpret_cast<const unsigned char*
>(sessID.c_str()), sessIDLen) != 1)
692 BLOCXX_THROW(SSLException, Format(
"SSLCtxMgr::initServer(): SSL_CTX_set_session_id_context failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
695 if (opts.verifyMode != SSLOpts::MODE_DISABLED && !opts.trustStore.empty())
697 if (!FileSystem::exists(opts.trustStore))
700 BLOCXX_THROW(SSLException, Format(
"Error loading truststore %1",
701 opts.trustStore).c_str());
703 if (SSL_CTX_load_verify_locations(m_ctx,0,opts.trustStore.c_str()) != 1)
706 BLOCXX_THROW(SSLException, Format(
"Error loading truststore %1: %2", opts.trustStore, SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
715 switch (opts.verifyMode)
717 case SSLOpts::MODE_DISABLED:
718 SSL_CTX_set_verify(m_ctx, SSL_VERIFY_NONE, 0);
720 case SSLOpts::MODE_REQUIRED:
721 SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
723 case SSLOpts::MODE_OPTIONAL:
724 case SSLOpts::MODE_AUTOUPDATE:
725 SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER, verify_callback);
732 SSL_CTX_set_verify_depth(m_ctx, 4);
737SSLCtxBase::~SSLCtxBase()
749SSLCtxBase::getSSLCtx()
const
755 : verifyMode(MODE_DISABLED)
772SSLServerCtx::SSLServerCtx(
const SSLOpts& opts)
777SSLClientCtx::SSLClientCtx(
const SSLOpts& opts)
785SSLTrustStore::SSLTrustStore(
const String& storeLocation)
786 : m_store(storeLocation)
788 m_mapfile = m_store +
"/map";
789 if (FileSystem::exists(m_mapfile))
791 MutexLock mlock(m_mapGuard);
798SSLTrustStore::getUser(
const String& certhash, String& user, String& uid)
800 MutexLock mlock(m_mapGuard);
801 Map<String, UserInfo>::const_iterator iter = m_map.find(certhash);
802 if (iter == m_map.end())
806 user = iter->second.user;
807 uid = iter->second.uid;
813SSLTrustStore::addCertificate(X509* cert,
const String& user,
const String& uid)
815 static const int numtries = 1000;
818 unsigned long hash = X509_subject_name_hash(cert);
819 ss << std::hex << hash;
820 String filename = m_store +
"/" + ss.toString() +
".";
822 for (
i = 0;
i < numtries; ++
i)
824 String temp = filename + String(
i);
825 if (FileSystem::exists(temp))
834 BLOCXX_THROW(SSLException,
"Unable to find a valid filename to store cert");
836 FILE* fp = fopen(filename.c_str(),
"w");
839 BLOCXX_THROW_ERRNO_MSG(SSLException, Format(
"Unable to open new cert file for writing: %1", filename).c_str());
845 if (PEM_write_X509(fp, cert) != 1)
848 BLOCXX_THROW(SSLException, Format(
"SSL error while writing certificate to %1: %2", filename, SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
852 String digest = getCertMD5Fingerprint(cert);
853 MutexLock mlock(m_mapGuard);
857 m_map[digest] = info;
863SSLTrustStore::getCertMD5Fingerprint(X509* cert)
865 unsigned char digest[16];
866 unsigned int len = 16;
867 X509_digest(cert, EVP_md5(), digest, &len);
868 return MD5::convertBinToHex(digest);
873SSLTrustStore::writeMap()
875 std::ofstream f(m_mapfile.c_str(), std::ios::out);
880 for (Map<String, UserInfo>::const_iterator iter = m_map.begin();
881 iter != m_map.end(); ++iter)
883 f << iter->first <<
" " << iter->second.user
884 <<
" " << iter->second.uid <<
"\n";
891SSLTrustStore::readMap()
893 std::ifstream f(m_mapfile.c_str(), std::ios::in);
901 String line = String::getLine(f);
908 if (toks.size() != 3 && toks.size() != 2)
910 BLOCXX_THROW(SSLException, Format(
"Error processing user map %1 at line %2", m_mapfile, lineno).c_str());
914 if (toks.size() == 3)
918 m_map.
insert(std::make_pair(toks[0], info));
921 std::cerr <<
"cert<>user map initizialized with " << m_map.size() <<
" users" << std::endl;
928OWSSLContext::OWSSLContext()
929 : peerCertPassedVerify(VERIFY_NONE)
933OWSSLContext::~OWSSLContext()
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
#define BLOCXX_ASSERTMSG(CON, MSG)
BLOCXX_ASSERTMSG works the same as BLOCXX_ASSERT, but with a second string parameter that will be add...
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
#define BLOCXX_GLOBAL_MUTEX_INIT()
#define BLOCXX_LAZY_GLOBAL_INIT(...)
Statically initialize a LazyGlobal instance.
iterator insert(iterator position, const T &x)
Insert an element in the Array before an element specified by an iterator.
bool release()
Release ownership of this Mutex object.
void acquire()
Acquire ownership of this Mutex object.
BLOCXX_COMMON_API String getPass(const String &prompt)
The getpass function displays a prompt to the standard error output, and reads in a password from /de...
BLOCXX_COMMON_API UInt64 thread_t_ToUInt64(Thread_t thr)
Convert a Thread_t to an UInt64.
Array< String > StringArray
LazyGlobal< Mutex, int, GlobalMutexFactory > GlobalMutex