blocxx

SSLCtxMgr.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2001-2004 Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2004 Novell, Inc. All rights reserved.
00004 *
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 *
00008 *  - Redistributions of source code must retain the above copyright notice,
00009 *    this list of conditions and the following disclaimer.
00010 *
00011 *  - Redistributions in binary form must reproduce the above copyright notice,
00012 *    this list of conditions and the following disclaimer in the documentation
00013 *    and/or other materials provided with the distribution.
00014 *
00015 *  - Neither the name of Vintela, Inc. nor the names of its
00016 *    contributors may be used to endorse or promote products derived from this
00017 *    software without specific prior written permission.
00018 *
00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc. OR THE CONTRIBUTORS
00023 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00024 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00025 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00026 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00027 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00028 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00029 * POSSIBILITY OF SUCH DAMAGE.
00030 *******************************************************************************/
00031 
00037 #include "blocxx/BLOCXX_config.h"
00038 #ifdef BLOCXX_HAVE_OPENSSL
00039 #include "blocxx/SSLCtxMgr.hpp"
00040 #include "blocxx/GetPass.hpp"
00041 #include "blocxx/Format.hpp"
00042 #include "blocxx/FileSystem.hpp"
00043 #include "blocxx/ThreadImpl.hpp"
00044 #include "blocxx/Mutex.hpp"
00045 #include "blocxx/GlobalMutex.hpp"
00046 #include "blocxx/MutexLock.hpp"
00047 #include "blocxx/Assertion.hpp"
00048 #include "blocxx/MD5.hpp"
00049 #include "blocxx/Array.hpp"
00050 #include "blocxx/SecureRand.hpp"
00051 #include "blocxx/SignalScope.hpp"
00052 #include "blocxx/LazyGlobal.hpp"
00053 
00054 #include <openssl/rand.h>
00055 #include <openssl/err.h>
00056 #include <cstring>
00057 #include <csignal>
00058 #include <cerrno>
00059 #ifndef BLOCXX_WIN32
00060 #include <sys/time.h>
00061 #include <sys/resource.h>
00062 #endif
00063 #include <fcntl.h>
00064 
00065 #ifdef BLOCXX_HAVE_SYS_TYPES_H
00066 #include <sys/types.h>
00067 #endif
00068 
00069 #ifdef BLOCXX_HAVE_SYS_STAT_H
00070 #include <sys/stat.h>
00071 #endif
00072 
00073 #ifdef BLOCXX_HAVE_UNISTD_H
00074 #include <unistd.h>
00075 #endif
00076 
00077 #ifdef BLOCXX_DEBUG
00078 #include <iostream>
00079 #endif
00080 
00081 #include <fstream>
00082 
00083 // This struct has to be in the global namespace
00084 extern "C"
00085 {
00086 struct CRYPTO_dynlock_value
00087 {
00088    BLOCXX_NAMESPACE::Mutex mutex;
00089 };
00090 }
00091 
00092 namespace BLOCXX_NAMESPACE
00093 {
00094 
00095 namespace
00096 {
00097 
00098 BLOCXX_NAMESPACE::Mutex* mutex_buf = 0;
00099 
00100 extern "C"
00101 {
00102 
00103 static struct CRYPTO_dynlock_value * dyn_create_function(const char *,int)
00104 {
00105    return new CRYPTO_dynlock_value;
00106 }
00107 
00108 // these need to still be static, since they get exported because of extern "C"
00109 static void dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l,
00110               const char *, int)
00111 {
00112    if (mode & CRYPTO_LOCK)
00113    {
00114       l->mutex.acquire();
00115    }
00116    else
00117    {
00118       l->mutex.release();
00119    }
00120 }
00121 
00122 static void dyn_destroy_function(struct CRYPTO_dynlock_value *l,
00123              const char *, int)
00124 {
00125    delete l;
00126 }
00127 
00128 static unsigned long id_function()
00129 {
00130    return static_cast<unsigned long>(BLOCXX_NAMESPACE::ThreadImpl::thread_t_ToUInt64(BLOCXX_NAMESPACE::ThreadImpl::currentThread()));
00131 }
00132 
00133 static void locking_function(int mode, int n, const char*, int)
00134 {
00135    if (mode & CRYPTO_LOCK)
00136    {
00137       mutex_buf[n].acquire();
00138    }
00139    else
00140    {
00141       mutex_buf[n].release();
00142    }
00143 }
00144 } // end extern "C"
00145 
00146 class X509Freer
00147 {
00148 public:
00149    X509Freer(X509* x509)
00150       : m_x509(x509)
00151    {
00152    }
00153    ~X509Freer()
00154    {
00155       if (m_x509 != 0)
00156       {
00157          X509_free(m_x509);
00158       }
00159    }
00160 private:
00161    X509* m_x509;
00162 };
00163 
00164 enum SSLLibraryInit_t {
00165    BLOCXX_SSL_LIBRARY_NOT_INITIALIZED,
00166    BLOCXX_SSL_LIBRARY_INITIALIZED,
00167    BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED
00168 };
00169 SSLLibraryInit_t m_initState = BLOCXX_SSL_LIBRARY_NOT_INITIALIZED;
00170 GlobalMutex m_initStateGuard = BLOCXX_GLOBAL_MUTEX_INIT();
00171 
00172 
00173 enum SSLLocks_t {
00174    BLOCXX_SSL_LOCKS_NOT_USED,
00175    BLOCXX_SSL_LOCKS_USED,
00176    BLOCXX_SSL_LOCKS_DISABLED
00177 };
00178 SSLLocks_t m_locksState = BLOCXX_SSL_LOCKS_NOT_USED;
00179 GlobalMutex m_locksStateGuard = BLOCXX_GLOBAL_MUTEX_INIT();
00180 
00182 struct SSLGlobalWork
00183 {
00184    SSLGlobalWork()
00185    {
00186       MutexLock initLock(m_initStateGuard);
00187       if (m_initState == BLOCXX_SSL_LIBRARY_NOT_INITIALIZED)
00188       {
00189          m_initState = BLOCXX_SSL_LIBRARY_INITIALIZED;
00190 
00191          SSL_library_init();
00192          SSL_load_error_strings();
00193       }
00194       initLock.release();
00195 
00196       MutexLock locksLock(m_locksStateGuard);
00197       if (m_locksState == BLOCXX_SSL_LOCKS_NOT_USED)
00198       {
00199          m_locksState = BLOCXX_SSL_LOCKS_USED;
00200 
00201          if (!mutex_buf)
00202          {
00203             mutex_buf = new Mutex[CRYPTO_num_locks()];
00204          }
00205 
00206          CRYPTO_set_id_callback(id_function);
00207          CRYPTO_set_locking_callback(locking_function);
00208 
00209          // The following three CRYPTO_... functions are the OpenSSL functions
00210          // for registering the callbacks we implemented above
00211          CRYPTO_set_dynlock_create_callback(dyn_create_function);
00212          CRYPTO_set_dynlock_lock_callback(dyn_lock_function);
00213          CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function);
00214       }
00215    }
00216 
00217    ~SSLGlobalWork()
00218    {
00219       if (SSLCtxMgr::isClient() || SSLCtxMgr::isServer())
00220       {
00221          Secure::rand_save_state();
00222       }
00223       SSLCtxMgr::uninit();
00224 
00225       MutexLock locksLock(m_locksStateGuard);
00226       if (m_locksState == BLOCXX_SSL_LOCKS_USED)
00227       {
00228          CRYPTO_set_id_callback(NULL);
00229          CRYPTO_set_locking_callback(NULL);
00230          CRYPTO_set_dynlock_create_callback(NULL);
00231          CRYPTO_set_dynlock_lock_callback(NULL);
00232          CRYPTO_set_dynlock_destroy_callback(NULL);
00233          delete[] mutex_buf;
00234          mutex_buf = 0;
00235          m_locksState = BLOCXX_SSL_LOCKS_NOT_USED;
00236       }
00237    }
00238 };
00239 
00240 struct SSLGlobalWorkFactory
00241 {
00242    static SSLGlobalWork* create(int /*dummy*/)
00243    {
00244       return new SSLGlobalWork();
00245    }
00246 };
00247 
00248 LazyGlobal<SSLGlobalWork, int, SSLGlobalWorkFactory> g_SSLGlobalWork = BLOCXX_LAZY_GLOBAL_INIT(0);
00249 
00250 
00251 } // end unnamed namespace
00252 
00253 SSL_CTX* SSLCtxMgr::m_ctxClient = 0;
00254 SSL_CTX* SSLCtxMgr::m_ctxServer = 0;
00255 certVerifyFuncPtr_t SSLCtxMgr::m_clientCertVerifyCB = 0;
00256 certVerifyFuncPtr_t SSLCtxMgr::m_serverCertVerifyCB = 0;
00257 
00259 // static
00260 String
00261 SSLCtxMgr::getOpenSSLErrorDescription()
00262 {
00263    BIO* bio = BIO_new(BIO_s_mem());
00264    if (!bio)
00265    {
00266       return String();
00267    }
00268    ERR_print_errors(bio);
00269    char* p = 0;
00270    long len = BIO_get_mem_data(bio, &p);
00271    String rval(p, len);
00272    int freerv = BIO_free(bio);
00273    BLOCXX_ASSERT(freerv == 1);
00274    return rval;
00275 }
00276 
00278 // static
00279 void SSLCtxMgr::disableSSLInit()
00280 {
00281    MutexLock lock(m_initStateGuard);
00282    if (m_initState == BLOCXX_SSL_LIBRARY_INITIALIZED)
00283    {
00284       BLOCXX_THROW(SSLException, "SSLCtxMgr::disableSSLInit(): init() cannot be disabled as it has already been called");
00285    }
00286    m_initState = BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
00287 }
00288 
00290 // static
00291 void SSLCtxMgr::disableLocks()
00292 {
00293    MutexLock lock(m_locksStateGuard);
00294    if (m_locksState == BLOCXX_SSL_LOCKS_USED)
00295    {
00296       BLOCXX_THROW(SSLException, "SSLCtxMgr::disableSSLLocks(): locks cannot be disabled as they are already in use");
00297    }
00298    m_locksState = BLOCXX_SSL_LOCKS_DISABLED;
00299 }
00300 
00302 // static
00303 Bool SSLCtxMgr::getSSLInitDisabled()
00304 {
00305    MutexLock lock(m_initStateGuard);
00306    return m_initState == BLOCXX_SSL_LIBRARY_INITIALIZATION_DISABLED;
00307 }
00308 
00310 // static
00311 Bool SSLCtxMgr::getSSLLocksDisabled()
00312 {
00313    MutexLock lock(m_locksStateGuard);
00314    return m_locksState == BLOCXX_SSL_LOCKS_DISABLED;
00315 }
00316 
00318 SSL_CTX*
00319 SSLCtxMgr::initCtx(const String& certfile, const String& keyfile, EVP_PKEY* pkey)
00320 {
00321    g_SSLGlobalWork.get();  // trigger initialization of lazy global stuff
00322 
00323    ERR_clear_error();
00324    SSL_CTX* ctx = SSL_CTX_new(SSLv23_method());
00325    if (ctx == 0)
00326    {
00327       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): SSL_CTX_new returned 0: %1", getOpenSSLErrorDescription()).c_str());
00328    }
00329    SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
00330    if (!certfile.empty())
00331    {
00332       if (SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str()) != 1)
00333       {
00334          SSL_CTX_free(ctx);
00335          BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): Couldn't read certificate from file: %1: %2",
00336             certfile, getOpenSSLErrorDescription()).c_str());
00337       }
00338       if (pkey)
00339       {
00340          int rv = SSL_CTX_use_PrivateKey(ctx, pkey); 
00341          if (rv != 1)
00342          {
00343             SSL_CTX_free(ctx);
00344             BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): Unable to set private key: %1",
00345                getOpenSSLErrorDescription()).c_str());
00346          }
00347       }
00348       else if (SSL_CTX_use_PrivateKey_file(ctx, keyfile.empty()?certfile.c_str():keyfile.c_str(), SSL_FILETYPE_PEM) != 1)
00349       {
00350          SSL_CTX_free(ctx);
00351          BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initCtx(): Couldn't read key from file: %1: %2",
00352             keyfile.empty()?certfile:keyfile, getOpenSSLErrorDescription()).c_str());
00353       }
00354    }
00355 
00356    Secure::rand_init();
00357 
00358    return ctx;
00359 }
00360 
00362 void
00363 SSLCtxMgr::loadDHParams(SSL_CTX* ctx, const String& file)
00364 {
00365    BLOCXX_ASSERT(ctx != 0);
00366    ERR_clear_error();
00367    BIO* bio = BIO_new_file(file.c_str(), "r");
00368    if (bio == NULL)
00369    {
00370       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::loadDHParams(): Couldn't open DH file %1: %2", file, getOpenSSLErrorDescription()).c_str());
00371    }
00372    DH* ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
00373    BIO_free(bio);
00374    if (ret == 0)
00375    {
00376       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::loadDHParams(): PEM_read_bio_DHparams failed: %1", getOpenSSLErrorDescription()).c_str());
00377    }
00378    if (SSL_CTX_set_tmp_dh(ctx, ret) != 1)
00379    {
00380       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::loadDHParams(): Couldn't set DH parameters because SSL_CTX_set_tmp_dh failed: %1", getOpenSSLErrorDescription()).c_str());
00381    }
00382 }
00384 void
00385 SSLCtxMgr::generateEphRSAKey(SSL_CTX* ctx)
00386 {
00387    BLOCXX_ASSERT(ctx != 0);
00388    ERR_clear_error();
00389    RSA* rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
00390    if (rsa == 0)
00391    {
00392       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::generateEphRSAKey(): RSA_generate_key failed: %1", getOpenSSLErrorDescription()).c_str());
00393    }
00394    if (SSL_CTX_set_tmp_rsa(ctx, rsa) != 1)
00395    {
00396       RSA_free(rsa);
00397       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::generateEphRSAKey(): SSL_CTX_set_tmp_rsa failed. Couldn't set RSA key: %1", getOpenSSLErrorDescription()).c_str());
00398    }
00399    RSA_free(rsa);
00400 }
00402 void
00403 SSLCtxMgr::initClient(const String& certfile, const String& keyfile)
00404 {
00405    if (m_ctxClient)
00406    {
00407       uninitClient();
00408    }
00409    m_ctxClient = initCtx(certfile,keyfile);
00410 }
00412 void
00413 SSLCtxMgr::initServer(const String& certfile, const String& keyfile)
00414 {
00415    if (certfile.empty())
00416    {
00417       BLOCXX_THROW(SSLException, "SSLCtxMgr::initCtx(): no certificate file specified");
00418    }
00419    if (m_ctxServer)
00420    {
00421       uninitServer();
00422    }
00423    m_ctxServer = initCtx(certfile,keyfile);
00424    //loadDHParams(m_ctx, dhfile);
00425    generateEphRSAKey(m_ctxServer);
00426    String sessID("SSL_SESSION_");
00427    sessID += String(Secure::rand_range<UInt16>(0, 10000));
00428    int sessIDLen =
00429       (SSL_MAX_SSL_SESSION_ID_LENGTH < (sessID.length())) ?
00430       SSL_MAX_SSL_SESSION_ID_LENGTH : (sessID.length());
00431    ERR_clear_error();
00432    if (SSL_CTX_set_session_id_context(m_ctxServer, reinterpret_cast<const unsigned char*>(sessID.c_str()), sessIDLen) != 1)
00433    {
00434       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initServer(): SSL_CTX_set_session_id_context failed: %1", getOpenSSLErrorDescription()).c_str());
00435    }
00436    SSL_CTX_set_verify(m_ctxServer, SSL_VERIFY_PEER /*| SSL_VERIFY_FAIL_IF_NO_PEER_CERT*/, NULL);
00437 }
00439 // STATIC
00440 int
00441 SSLCtxMgr::pem_passwd_cb(char* buf, int size, int /*rwflag*/,
00442    void* /*userData*/)
00443 {
00444    String passwd = GetPass::getPass("Enter the password for the SSL certificate: ");
00445 
00446    strncpy(buf, passwd.c_str(), size);
00447    buf[size - 1] = '\0';
00448 
00449    return passwd.length();
00450 }
00452 // STATIC
00453 bool
00454 SSLCtxMgr::checkClientCert(SSL* ssl, const String& hostName)
00455 {
00456    return checkCert(ssl, hostName, m_clientCertVerifyCB);
00457 }
00459 // STATIC
00460 bool
00461 SSLCtxMgr::checkServerCert(SSL* ssl, const String& hostName)
00462 {
00463    return checkCert(ssl, hostName, m_serverCertVerifyCB);
00464 }
00466 // STATIC
00467 bool
00468 SSLCtxMgr::checkCert(SSL* ssl, const String& hostName,
00469    certVerifyFuncPtr_t certVerifyCB)
00470 {
00471    BLOCXX_ASSERT(ssl != 0);
00472 
00473    /* TODO this isn't working.
00474    if (SSL_get_verify_result(ssl)!=X509_V_OK)
00475    {
00476       cout << "SSL_get_verify_results failed." << endl;
00477       return false;
00478    }
00479    */
00480    /*Check the cert chain. The chain length
00481      is automatically checked by OpenSSL when we
00482      set the verify depth in the ctx */
00483    /*Check the common name*/
00484    if (certVerifyCB)
00485    {
00486       X509 *peer = SSL_get_peer_certificate(ssl);
00487       X509Freer x509freer(peer);
00488       if (peer == 0)
00489       {
00490          return false;
00491       }
00492       if (certVerifyCB(peer, hostName) == 0)
00493       {
00494          return false;
00495       }
00496       else
00497       {
00498          return true;
00499       }
00500    }
00501    return true;
00502 }
00504 // STATIC
00505 int
00506 SSLCtxMgr::sslRead(SSL* ssl, char* buf, int len)
00507 {
00508    int cc = SSL_ERROR_WANT_READ;
00509    int r = -1; 
00510    int retries = 0;
00511    while (cc == SSL_ERROR_WANT_READ && retries < BLOCXX_SSL_RETRY_LIMIT)
00512    {
00513       r = SSL_read(ssl, buf, len);
00514       cc = SSL_get_error(ssl, r);
00515       retries++;
00516    }
00517    
00518    switch (cc)
00519    {
00520       case SSL_ERROR_NONE:
00521          return r;
00522       case SSL_ERROR_ZERO_RETURN:
00523          return -1;
00524       default:
00525          return -1;
00526    }
00527 }
00529 // STATIC
00530 int
00531 SSLCtxMgr::sslWrite(SSL* ssl, const char* buf, int len)
00532 {
00533    int r = 0; 
00534    int cc;
00535    int retries;
00536    int myLen = len;
00537    int offset = 0;
00538 #ifndef BLOCXX_WIN32
00539    // block SIGPIPE so we don't kill the process if the socket is closed.
00540    SignalScope ss(SIGPIPE, SIG_IGN);
00541 #endif
00542    while (myLen > 0)
00543    {
00544       cc = SSL_ERROR_WANT_WRITE;
00545       retries = 0;
00546       while(cc == SSL_ERROR_WANT_WRITE && retries < BLOCXX_SSL_RETRY_LIMIT)
00547       {
00548          r = SSL_write(ssl, buf + offset, myLen);
00549          cc = SSL_get_error(ssl, r);
00550          retries++;
00551       }
00552 
00553       if (cc == SSL_ERROR_NONE)
00554       {
00555          myLen -= r;
00556          offset += r;
00557       }
00558       else
00559       {
00560          return -1;
00561       }
00562    }
00563    return len;
00564 }
00566 void
00567 SSLCtxMgr::uninit()
00568 {
00569    uninitClient();
00570    uninitServer();
00571       
00572    MutexLock initLock(m_initStateGuard);
00573    if (m_initState == BLOCXX_SSL_LIBRARY_INITIALIZED)
00574    {
00575       // free up memory allocated in SSL_library_init()
00576       EVP_cleanup();
00577       // free up memory allocated in SSL_load_error_strings()
00578       ERR_free_strings();
00579 
00580       m_initState = BLOCXX_SSL_LIBRARY_NOT_INITIALIZED;
00581    }
00582 }
00584 void
00585 SSLCtxMgr::uninitClient()
00586 {
00587    if (m_ctxClient)
00588    {
00589       SSL_CTX_free(m_ctxClient);
00590       m_ctxClient = NULL;
00591    }
00592 }
00594 void
00595 SSLCtxMgr::uninitServer()
00596 {
00597    if (m_ctxServer)
00598    {
00599       SSL_CTX_free(m_ctxServer);
00600       m_ctxServer = NULL;
00601    }
00602 }
00603 
00604 namespace
00605 {
00606 
00608 extern "C"
00609 {
00610 static int verify_callback(int ok, X509_STORE_CTX *store)
00611 {
00612    int index = SSL_get_ex_data_X509_STORE_CTX_idx();
00613    if (index < 0)
00614    {
00615       return 0;
00616    }
00617    SSL* ssl = static_cast<SSL*>(X509_STORE_CTX_get_ex_data(store, index));
00618    if (ssl == 0)
00619    {
00620       return 0;
00621    }
00622    OWSSLContext* owctx = static_cast<OWSSLContext*>(SSL_get_ex_data(ssl, SSLServerCtx::SSL_DATA_INDEX));
00623    BLOCXX_ASSERT(owctx);
00624    if (owctx == 0)
00625    {
00626       return 0;
00627    }
00628 
00639    if (!ok)
00640    {
00641       owctx->peerCertPassedVerify = OWSSLContext::VERIFY_FAIL;
00642    }
00643    else
00644    {
00645       // if the cert failed on a previous call, we don't want to change
00646       // the status.
00647       if (owctx->peerCertPassedVerify != OWSSLContext::VERIFY_FAIL)
00648       {
00649          owctx->peerCertPassedVerify = OWSSLContext::VERIFY_PASS;
00650       }
00651    }
00652 
00653 #ifdef BLOCXX_DEBUG
00654    if (!ok)
00655    {
00656       char data[256];
00657       X509 *cert = X509_STORE_CTX_get_current_cert(store);
00658       int  depth = X509_STORE_CTX_get_error_depth(store);
00659       int  err = X509_STORE_CTX_get_error(store);
00660 
00661       fprintf(stderr, "-Error with certificate at depth: %i\n", depth);
00662       X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
00663       fprintf(stderr, "  issuer   = %s\n", data);
00664       X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
00665       fprintf(stderr, "  subject  = %s\n", data);
00666       fprintf(stderr, "  err %i:%s\n", err, X509_verify_cert_error_string(err));
00667    }
00668 #endif
00669 
00670    return 1;
00671 }
00672 } // end extern "C"
00673 
00674 } // end unnamed namespace
00675 
00677 SSLCtxBase::SSLCtxBase(const SSLOpts& opts)
00678    : m_ctx(0)
00679 {
00680    m_ctx = SSLCtxMgr::initCtx(opts.certfile, opts.keyfile, opts.pkey);
00681    
00682    SSLCtxMgr::generateEphRSAKey(m_ctx); // TODO what the heck is this?
00683    String sessID("SSL_SESSION_");
00684    sessID += String(Secure::rand_range<UInt16>(0, 10000));
00685    int sessIDLen =
00686       (SSL_MAX_SSL_SESSION_ID_LENGTH < (sessID.length())) ?
00687       SSL_MAX_SSL_SESSION_ID_LENGTH : (sessID.length());
00688    ERR_clear_error();
00689    if (SSL_CTX_set_session_id_context(m_ctx, reinterpret_cast<const unsigned char*>(sessID.c_str()), sessIDLen) != 1)
00690    {
00691       SSL_CTX_free(m_ctx);
00692       BLOCXX_THROW(SSLException, Format("SSLCtxMgr::initServer(): SSL_CTX_set_session_id_context failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00693    }
00694 
00695    if (opts.verifyMode != SSLOpts::MODE_DISABLED && !opts.trustStore.empty())
00696    {
00697       if (!FileSystem::exists(opts.trustStore))
00698       {
00699          SSL_CTX_free(m_ctx);
00700          BLOCXX_THROW(SSLException, Format("Error loading truststore %1",
00701                                 opts.trustStore).c_str());
00702       }
00703       if (SSL_CTX_load_verify_locations(m_ctx,0,opts.trustStore.c_str()) != 1)
00704       {
00705          SSL_CTX_free(m_ctx);
00706          BLOCXX_THROW(SSLException, Format("Error loading truststore %1: %2", opts.trustStore, SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00707       }
00708    }
00709    /* TODO remove.
00710    if (SSL_CTX_set_default_verify_paths(m_ctx) != 1)
00711    {
00712       BLOCXX_THROW(SSLException, "Error loading default CA store(s)");
00713    }
00714    */
00715    switch (opts.verifyMode)
00716    {
00717    case SSLOpts::MODE_DISABLED:
00718       SSL_CTX_set_verify(m_ctx, SSL_VERIFY_NONE, 0);
00719       break;
00720    case SSLOpts::MODE_REQUIRED:
00721       SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
00722       break;
00723    case SSLOpts::MODE_OPTIONAL:
00724    case SSLOpts::MODE_AUTOUPDATE:
00725       SSL_CTX_set_verify(m_ctx, SSL_VERIFY_PEER, verify_callback);
00726       break;
00727    default:
00728       BLOCXX_ASSERTMSG(false, "Bad option, shouldn't happen");
00729       break;
00730    }
00731 
00732    SSL_CTX_set_verify_depth(m_ctx, 4);
00733 
00734 }
00735 
00737 SSLCtxBase::~SSLCtxBase()
00738 {
00739    if (m_ctx)
00740    {
00741       SSL_CTX_free(m_ctx);
00742    }
00743    ERR_clear_error();
00744    ERR_remove_state(0);
00745 }
00746 
00748 SSL_CTX*
00749 SSLCtxBase::getSSLCtx() const
00750 {
00751    return m_ctx;
00752 }
00753 
00754 SSLOpts::SSLOpts()
00755    : verifyMode(MODE_DISABLED)
00756    , pkey(0)
00757 {
00758 }
00759 
00760 SSLOpts::~SSLOpts()
00761 {
00762    if (pkey != 0)
00763    {
00764       EVP_PKEY_free(pkey); 
00765       pkey = 0; 
00766    }
00767 }
00768 
00769 
00770 
00772 SSLServerCtx::SSLServerCtx(const SSLOpts& opts)
00773    : SSLCtxBase(opts)
00774 {
00775 }
00777 SSLClientCtx::SSLClientCtx(const SSLOpts& opts)
00778    : SSLCtxBase(opts)
00779 {
00780 }
00781 
00782 static GlobalMutex m_mapGuard = BLOCXX_GLOBAL_MUTEX_INIT();
00783 
00785 SSLTrustStore::SSLTrustStore(const String& storeLocation)
00786    : m_store(storeLocation)
00787 {
00788    m_mapfile = m_store + "/map";
00789    if (FileSystem::exists(m_mapfile))
00790    {
00791       MutexLock mlock(m_mapGuard);
00792       readMap();
00793    }
00794 }
00795 
00797 bool
00798 SSLTrustStore::getUser(const String& certhash, String& user, String& uid)
00799 {
00800    MutexLock mlock(m_mapGuard);
00801    Map<String, UserInfo>::const_iterator iter = m_map.find(certhash);
00802    if (iter == m_map.end())
00803    {
00804       return false;
00805    }
00806    user = iter->second.user;
00807    uid = iter->second.uid;
00808    return true;
00809 }
00810 
00812 void
00813 SSLTrustStore::addCertificate(X509* cert, const String& user, const String& uid)
00814 {
00815    static const int numtries = 1000;
00816    BLOCXX_ASSERT(cert);
00817    OStringStream ss;
00818    unsigned long hash = X509_subject_name_hash(cert);
00819    ss << std::hex << hash;
00820    String filename = m_store + "/" + ss.toString() + ".";
00821    int i = 0;
00822    for (i = 0; i < numtries; ++i)
00823    {
00824       String temp = filename + String(i);
00825       if (FileSystem::exists(temp))
00826       {
00827          continue;
00828       }
00829       filename = temp;
00830       break;
00831    }
00832    if (i == numtries)
00833    {
00834       BLOCXX_THROW(SSLException, "Unable to find a valid filename to store cert");
00835    }
00836    FILE* fp = fopen(filename.c_str(), "w");
00837    if (!fp)
00838    {
00839       BLOCXX_THROW_ERRNO_MSG(SSLException, Format("Unable to open new cert file for writing: %1", filename).c_str());
00840    }
00841 
00842    ERR_clear_error();
00843    // Undocumented function in OpenSSL.  We assume it returns 1 on success
00844    // like most OpenSSL funcs.
00845    if (PEM_write_X509(fp, cert) != 1)
00846    {
00847       fclose(fp);
00848       BLOCXX_THROW(SSLException, Format("SSL error while writing certificate to %1: %2", filename, SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00849    }
00850    fclose(fp);
00851 
00852    String digest = getCertMD5Fingerprint(cert);
00853    MutexLock mlock(m_mapGuard);
00854    UserInfo info;
00855    info.user = user;
00856    info.uid = uid;
00857    m_map[digest] = info;
00858    writeMap();
00859 }
00860 
00862 String
00863 SSLTrustStore::getCertMD5Fingerprint(X509* cert)
00864 {
00865    unsigned char digest[16];
00866    unsigned int len = 16;
00867    X509_digest(cert, EVP_md5(), digest, &len);
00868    return MD5::convertBinToHex(digest);
00869 }
00870 
00872 void
00873 SSLTrustStore::writeMap()
00874 {
00875    std::ofstream f(m_mapfile.c_str(), std::ios::out);
00876    if (!f)
00877    {
00878       BLOCXX_THROW_ERRNO_MSG(SSLException, Format("SSL error opening map file: %1", m_mapfile).c_str());
00879    }
00880    for (Map<String, UserInfo>::const_iterator iter = m_map.begin();
00881       iter != m_map.end(); ++iter)
00882    {
00883       f << iter->first << " " << iter->second.user
00884          << " " << iter->second.uid << "\n";
00885    }
00886    f.close();
00887 }
00888 
00890 void
00891 SSLTrustStore::readMap()
00892 {
00893    std::ifstream f(m_mapfile.c_str(), std::ios::in);
00894    if (!f)
00895    {
00896       BLOCXX_THROW_ERRNO_MSG(SSLException, Format("SSL error opening map file: %1", m_mapfile).c_str());
00897    }
00898    int lineno = 0;
00899    while (f)
00900    {
00901       String line = String::getLine(f);
00902       if (!f)
00903       {
00904          break;
00905       }
00906       ++lineno;
00907       StringArray toks = line.tokenize();
00908       if (toks.size() != 3 && toks.size() != 2)
00909       {
00910          BLOCXX_THROW(SSLException, Format("Error processing user map %1 at line %2", m_mapfile, lineno).c_str());
00911       }
00912       UserInfo info;
00913       info.user = toks[1];
00914       if (toks.size() == 3)
00915       {
00916          info.uid = toks[2];
00917       }
00918       m_map.insert(std::make_pair(toks[0], info));
00919    }
00920 #ifdef BLOCXX_DEBUG
00921    std::cerr << "cert<>user map initizialized with " << m_map.size() << " users" << std::endl;
00922 #endif
00923    f.close();
00924 }
00925 
00927 
00928 OWSSLContext::OWSSLContext()
00929    : peerCertPassedVerify(VERIFY_NONE)
00930 {
00931 }
00933 OWSSLContext::~OWSSLContext()
00934 {
00935 }
00936 
00937 
00938 } // end namespace BLOCXX_NAMESPACE
00939 
00940 #endif // #ifdef BLOCXX_HAVE_OPENSSL
00941