blocxx

SSLSocketImpl.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, 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 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00043 #include "blocxx/BLOCXX_config.h"
00044 
00045 #ifdef BLOCXX_HAVE_OPENSSL
00046 
00047 #include "blocxx/SSLSocketImpl.hpp"
00048 #include "blocxx/Format.hpp"
00049 #include "blocxx/Assertion.hpp"
00050 #include "blocxx/Timeout.hpp"
00051 #include <openssl/err.h>
00052 #include "blocxx/Format.hpp"
00053 #include "blocxx/SocketUtils.hpp"
00054 
00055 
00056 namespace BLOCXX_NAMESPACE
00057 {
00059 SSLSocketImpl::SSLSocketImpl(SSLClientCtxRef sslCtx) 
00060    : SocketBaseImpl()
00061    , m_ssl(0)
00062    , m_sslCtx(sslCtx)
00063 {
00064 }
00065 
00066 namespace
00067 {
00068 
00069 void sslWaitForIO(SocketBaseImpl& s, int type)
00070 {
00071    if(type == SSL_ERROR_WANT_READ)
00072    {
00073       s.waitForInput(Timeout::infinite);
00074    }
00075    else
00076    {
00077       s.waitForOutput(Timeout::infinite);
00078    }
00079 }
00080 
00081 void shutdownSSL(SSL* ssl)
00082 {
00083    BLOCXX_ASSERT(ssl != 0);
00084    if (SSL_shutdown(ssl) == -1)
00085    {
00086       // do nothing, since we're probably cleaning up.  If we had a logger we should log the reason why this failed....
00087    }
00088    // we're not going to reuse the SSL context, so we do a 
00089    // unidirectional shutdown, and don't need to call it twice
00090 }
00091 
00092 void connectWithSSL(SSL* ssl, SocketBaseImpl& s)
00093 {
00094    BLOCXX_ASSERT(ssl != 0);
00095    int retries = 0;
00096    ERR_clear_error();
00097    int cc = SSL_connect(ssl);
00098    cc = SSL_get_error(ssl, cc);
00099    while((cc == SSL_ERROR_WANT_READ 
00100       || cc == SSL_ERROR_WANT_WRITE)
00101       && retries < BLOCXX_SSL_RETRY_LIMIT)
00102    {
00103       sslWaitForIO(s, cc);
00104       ERR_clear_error();
00105       cc = SSL_connect(ssl);
00106       cc = SSL_get_error(ssl, cc);
00107       retries++;
00108    }
00109 
00110    if (cc != SSL_ERROR_NONE)
00111    {
00112       BLOCXX_THROW(SSLException, Format("SSL connect error: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00113    }
00114 }
00115 
00116 int acceptSSL(SSL* ssl, SocketBaseImpl& s, String& errorDescription)
00117 {
00118    BLOCXX_ASSERT(ssl != 0);
00119    int retries = 0;
00120    int cc = SSL_ERROR_WANT_READ;
00121    while((cc == SSL_ERROR_WANT_READ || cc == SSL_ERROR_WANT_WRITE)
00122       && retries < BLOCXX_SSL_RETRY_LIMIT)
00123    {
00124       sslWaitForIO(s, cc);
00125       ERR_clear_error();
00126       cc = SSL_accept(ssl);
00127       cc = SSL_get_error(ssl, cc);
00128       retries++;
00129    }
00130    if (cc == SSL_ERROR_NONE)
00131    {
00132       return 0;
00133    }
00134    else
00135    {
00136       errorDescription = SSLCtxMgr::getOpenSSLErrorDescription();
00137       return -1;
00138    }
00139 }
00140 
00141 }  // End of unnamed namespace
00142 
00144 SSLSocketImpl::SSLSocketImpl() 
00145    : SocketBaseImpl()
00146    , m_ssl(0)
00147    , m_sbio(0)
00148 {
00149 }
00151 SSLSocketImpl::SSLSocketImpl(SocketHandle_t fd, 
00152    SocketAddress::AddressType addrType, const SSLServerCtxRef& sslCtx) 
00153    : SocketBaseImpl(fd, addrType)
00154 {
00155    BLOCXX_ASSERT(sslCtx);
00156    ERR_clear_error();
00157    m_ssl = SSL_new(sslCtx->getSSLCtx());
00158    if (!m_ssl)
00159    {
00160       BLOCXX_THROW(SSLException, Format("SSL_new failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00161    }
00162 
00163    if (SSL_set_ex_data(m_ssl, SSLServerCtx::SSL_DATA_INDEX, &m_owctx) == 0)
00164    {
00165       BLOCXX_THROW(SSLException, Format("SSL_set_ex_data failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00166    }
00167 
00168    BIO* bio = BIO_new_socket(fd, BIO_NOCLOSE);
00169    if (!bio)
00170    {
00171       SSL_free(m_ssl);
00172       BLOCXX_THROW(SSLException, Format("BIO_new_socket failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00173    }
00174       
00175    SSL_set_bio(m_ssl, bio, bio);
00176    String errorDescription;
00177    if (acceptSSL(m_ssl, *this, errorDescription) != 0)
00178    {
00179       shutdownSSL(m_ssl);
00180       SSL_free(m_ssl);
00181       ERR_remove_state(0); // cleanup memory SSL may have allocated
00182       BLOCXX_THROW(SSLException, Format("SSLSocketImpl ctor: SSL accept error while connecting to %1: %2", m_peerAddress.toString(), errorDescription).c_str());
00183    }
00184    if (!SSLCtxMgr::checkClientCert(m_ssl, m_peerAddress.getName()))
00185    {
00186       shutdownSSL(m_ssl);
00187       SSL_free(m_ssl);
00188       ERR_remove_state(0); // cleanup memory SSL may have allocated
00189       BLOCXX_THROW(SSLException, "SSL failed to authenticate client");
00190    }
00191 }
00192 
00193 // TODO Get rid of this one later. 
00195 SSLSocketImpl::SSLSocketImpl(SocketHandle_t fd, 
00196    SocketAddress::AddressType addrType) 
00197    : SocketBaseImpl(fd, addrType)
00198 {
00199    ERR_clear_error();
00200    m_ssl = SSL_new(SSLCtxMgr::getSSLCtxServer());
00201    if (!m_ssl)
00202    {
00203       BLOCXX_THROW(SSLException, Format("SSL_new failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00204    }
00205 
00206    m_sbio = BIO_new_socket(fd, BIO_NOCLOSE);
00207    if (!m_sbio)
00208    {
00209       SSL_free(m_ssl);
00210       BLOCXX_THROW(SSLException, Format("BIO_new_socket failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00211    }
00212       
00213    SSL_set_bio(m_ssl, m_sbio, m_sbio);
00214    String errorDescription;
00215    if (acceptSSL(m_ssl, *this, errorDescription) != 0)
00216    {
00217       shutdownSSL(m_ssl);
00218       SSL_free(m_ssl);
00219       ERR_remove_state(0); // cleanup memory SSL may have allocated
00220       BLOCXX_THROW(SSLException, Format("SSLSocketImpl ctor: SSL accept error while connecting to %1: %2", m_peerAddress.toString(), errorDescription).c_str());
00221    }
00222    if (!SSLCtxMgr::checkClientCert(m_ssl, m_peerAddress.getName()))
00223    {
00224       shutdownSSL(m_ssl);
00225       SSL_free(m_ssl);
00226       ERR_remove_state(0); // cleanup memory SSL may have allocated
00227       BLOCXX_THROW(SSLException, "SSL failed to authenticate client");
00228    }
00229 }
00231 SSLSocketImpl::SSLSocketImpl(const SocketAddress& addr) 
00232    : SocketBaseImpl(addr)
00233 {
00234    connectSSL();
00235 }
00237 SSLSocketImpl::~SSLSocketImpl()
00238 {
00239    try
00240    {
00241       disconnect(); 
00242       if (m_ssl)
00243       {
00244          SSL_free(m_ssl);
00245          m_ssl = 0; 
00246       }
00247       ERR_remove_state(0); // cleanup memory SSL may have allocated
00248    }
00249    catch (...)
00250    {
00251       // no exceptions allowed out of destructors.
00252    }
00253 }
00255 Select_t
00256 SSLSocketImpl::getSelectObj() const
00257 {
00258 #if defined(BLOCXX_WIN32)
00259    Select_t st;
00260    st.event = m_event;
00261    st.sockfd = m_sockfd;
00262    st.isSocket = true;
00263    st.networkevents = FD_READ | FD_WRITE;
00264    st.doreset = true;
00265    return st;
00266 #else
00267    return m_sockfd;
00268 #endif
00269 }
00271 void 
00272 SSLSocketImpl::connect(const SocketAddress& addr)
00273 {
00274    SocketBaseImpl::connect(addr);
00275    connectSSL();
00276 }
00278 void 
00279 SSLSocketImpl::connectSSL()
00280 {
00281    m_isConnected = false;
00282    BLOCXX_ASSERT(m_sslCtx); 
00283    if (m_ssl)
00284    {
00285       SSL_free(m_ssl); 
00286       m_ssl = 0;
00287    }
00288    ERR_clear_error();
00289    m_ssl = SSL_new(m_sslCtx->getSSLCtx()); 
00290    
00291    if (!m_ssl)
00292    {
00293       BLOCXX_THROW(SSLException, Format("SSL_new failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00294    }
00295    m_sbio = BIO_new_socket(m_sockfd, BIO_NOCLOSE);
00296    if (!m_sbio)
00297    {
00298       SSL_free(m_ssl);
00299       BLOCXX_THROW(SSLException, Format("BIO_new_socket failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
00300    }
00301    SSL_set_bio(m_ssl, m_sbio, m_sbio);
00302 
00303    connectWithSSL(m_ssl, *this);
00304 
00305    if (!SSLCtxMgr::checkServerCert(m_ssl, m_peerAddress.getName()))
00306    {
00307       BLOCXX_THROW(SSLException, "Failed to validate peer certificate");
00308    }
00309    m_isConnected = true;
00310 }
00312 void
00313 SSLSocketImpl::disconnect()
00314 {
00315 #if defined(BLOCXX_WIN32)
00316    if (m_sockfd != INVALID_SOCKET && m_isConnected)
00317 #else
00318    if (m_sockfd != -1 && m_isConnected)
00319 #endif
00320    {
00321       if (m_ssl)
00322       {
00323          shutdownSSL(m_ssl);
00324       }
00325    }
00326    SocketBaseImpl::disconnect();
00327 }
00329 int 
00330 SSLSocketImpl::writeAux(const void* dataOut, int dataOutLen)
00331 {
00332    return SSLCtxMgr::sslWrite(m_ssl, static_cast<const char*>(dataOut), 
00333          dataOutLen);
00334 }
00336 int 
00337 SSLSocketImpl::readAux(void* dataIn, int dataInLen)
00338 {
00339    return SSLCtxMgr::sslRead(m_ssl, static_cast<char*>(dataIn), 
00340          dataInLen);
00341 }
00343 SSL*
00344 SSLSocketImpl::getSSL() const
00345 {
00346    return m_ssl; 
00347 }
00348 
00350 bool
00351 SSLSocketImpl::peerCertVerified() const
00352 {
00353     return (m_owctx.peerCertPassedVerify == OWSSLContext::VERIFY_PASS); 
00354 }
00355 
00357 // SSL buffer can contain the data therefore select
00358 // does not work without checking SSL_pending() first.
00359 bool
00360 SSLSocketImpl::waitForInput(const Timeout& timeout)
00361 {
00362    // SSL buffer contains data -> read them
00363    if (SSL_pending(m_ssl))
00364    {
00365       return false;
00366    }
00367    return SocketBaseImpl::waitForInput(timeout);
00368 }
00370 
00371 } // end namespace BLOCXX_NAMESPACE
00372 
00374 #endif // #ifdef BLOCXX_HAVE_OPENSSL
00375