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
88 BLOCXX_NAMESPACE::Mutex mutex;
98BLOCXX_NAMESPACE::Mutex* mutex_buf = 0;
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()
282 if (m_initState == BLOCXX_SSL_LIBRARY_INITIALIZED)
286 m_initState = BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
291void SSLCtxMgr::disableLocks()
294 if (m_locksState == BLOCXX_SSL_LOCKS_USED)
298 m_locksState = BLOCXX_SSL_LOCKS_DISABLED;
303Bool SSLCtxMgr::getSSLInitDisabled()
306 return m_initState == BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
311Bool SSLCtxMgr::getSSLLocksDisabled()
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());
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)
336 certfile, getOpenSSLErrorDescription()).c_str());
340 int rv = SSL_CTX_use_PrivateKey(ctx, pkey);
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)
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");
372 DH* ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
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);
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())
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)
436 SSL_CTX_set_verify(m_ctxServer, SSL_VERIFY_PEER , NULL);
441SSLCtxMgr::pem_passwd_cb(
char* buf,
int size,
int ,
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)
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)
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.
The Bool class is an abstraction for the boolean data type.
This class can be used to store a global variable that is lazily initialized in a thread safe manner.
bool release()
Release ownership of this Mutex object.
void acquire()
Acquire ownership of this Mutex object.
This String class is an abstract data type that represents as NULL terminated string of characters.
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