blocxx
SSLSocketImpl.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Vintela, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Vintela, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
42
43#include "blocxx/BLOCXX_config.h"
44
45#ifdef BLOCXX_HAVE_OPENSSL
46
48#include "blocxx/Format.hpp"
49#include "blocxx/Assertion.hpp"
50#include "blocxx/Timeout.hpp"
51#include <openssl/err.h>
52#include "blocxx/Format.hpp"
54
55
56namespace BLOCXX_NAMESPACE
57{
61 , m_ssl(0)
62 , m_sslCtx(sslCtx)
63{
64}
65
66namespace
67{
68
69void sslWaitForIO(SocketBaseImpl& s, int type)
70{
71 if(type == SSL_ERROR_WANT_READ)
72 {
73 s.waitForInput(Timeout::infinite);
74 }
75 else
76 {
77 s.waitForOutput(Timeout::infinite);
78 }
79}
80
81void shutdownSSL(SSL* ssl)
82{
83 BLOCXX_ASSERT(ssl != 0);
84 if (SSL_shutdown(ssl) == -1)
85 {
86 // do nothing, since we're probably cleaning up. If we had a logger we should log the reason why this failed....
87 }
88 // we're not going to reuse the SSL context, so we do a
89 // unidirectional shutdown, and don't need to call it twice
90}
91
92void connectWithSSL(SSL* ssl, SocketBaseImpl& s)
93{
94 BLOCXX_ASSERT(ssl != 0);
95 int retries = 0;
96 ERR_clear_error();
97 int cc = SSL_connect(ssl);
98 cc = SSL_get_error(ssl, cc);
99 while((cc == SSL_ERROR_WANT_READ
100 || cc == SSL_ERROR_WANT_WRITE)
101 && retries < BLOCXX_SSL_RETRY_LIMIT)
102 {
103 sslWaitForIO(s, cc);
104 ERR_clear_error();
105 cc = SSL_connect(ssl);
106 cc = SSL_get_error(ssl, cc);
107 retries++;
108 }
109
110 if (cc != SSL_ERROR_NONE)
111 {
112 BLOCXX_THROW(SSLException, Format("SSL connect error: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
113 }
114}
115
116int acceptSSL(SSL* ssl, SocketBaseImpl& s, String& errorDescription)
117{
118 BLOCXX_ASSERT(ssl != 0);
119 int retries = 0;
120 int cc = SSL_ERROR_WANT_READ;
121 while((cc == SSL_ERROR_WANT_READ || cc == SSL_ERROR_WANT_WRITE)
122 && retries < BLOCXX_SSL_RETRY_LIMIT)
123 {
124 sslWaitForIO(s, cc);
125 ERR_clear_error();
126 cc = SSL_accept(ssl);
127 cc = SSL_get_error(ssl, cc);
128 retries++;
129 }
130 if (cc == SSL_ERROR_NONE)
131 {
132 return 0;
133 }
134 else
135 {
136 errorDescription = SSLCtxMgr::getOpenSSLErrorDescription();
137 return -1;
138 }
139}
140
141} // End of unnamed namespace
142
144SSLSocketImpl::SSLSocketImpl()
145 : SocketBaseImpl()
146 , m_ssl(0)
147 , m_sbio(0)
148{
149}
151SSLSocketImpl::SSLSocketImpl(SocketHandle_t fd,
152 SocketAddress::AddressType addrType, const SSLServerCtxRef& sslCtx)
153 : SocketBaseImpl(fd, addrType)
154{
155 BLOCXX_ASSERT(sslCtx);
156 ERR_clear_error();
157 m_ssl = SSL_new(sslCtx->getSSLCtx());
158 if (!m_ssl)
159 {
160 BLOCXX_THROW(SSLException, Format("SSL_new failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
161 }
162
163 if (SSL_set_ex_data(m_ssl, SSLServerCtx::SSL_DATA_INDEX, &m_owctx) == 0)
164 {
165 BLOCXX_THROW(SSLException, Format("SSL_set_ex_data failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
166 }
167
168 BIO* bio = BIO_new_socket(fd, BIO_NOCLOSE);
169 if (!bio)
170 {
171 SSL_free(m_ssl);
172 BLOCXX_THROW(SSLException, Format("BIO_new_socket failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
173 }
174
175 SSL_set_bio(m_ssl, bio, bio);
176 String errorDescription;
177 if (acceptSSL(m_ssl, *this, errorDescription) != 0)
178 {
179 shutdownSSL(m_ssl);
180 SSL_free(m_ssl);
181 ERR_remove_state(0); // cleanup memory SSL may have allocated
182 BLOCXX_THROW(SSLException, Format("SSLSocketImpl ctor: SSL accept error while connecting to %1: %2", m_peerAddress.toString(), errorDescription).c_str());
183 }
184 if (!SSLCtxMgr::checkClientCert(m_ssl, m_peerAddress.getName()))
185 {
186 shutdownSSL(m_ssl);
187 SSL_free(m_ssl);
188 ERR_remove_state(0); // cleanup memory SSL may have allocated
189 BLOCXX_THROW(SSLException, "SSL failed to authenticate client");
190 }
191}
192
193// TODO Get rid of this one later.
195SSLSocketImpl::SSLSocketImpl(SocketHandle_t fd,
196 SocketAddress::AddressType addrType)
197 : SocketBaseImpl(fd, addrType)
198{
199 ERR_clear_error();
200 m_ssl = SSL_new(SSLCtxMgr::getSSLCtxServer());
201 if (!m_ssl)
202 {
203 BLOCXX_THROW(SSLException, Format("SSL_new failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
204 }
205
206 m_sbio = BIO_new_socket(fd, BIO_NOCLOSE);
207 if (!m_sbio)
208 {
209 SSL_free(m_ssl);
210 BLOCXX_THROW(SSLException, Format("BIO_new_socket failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
211 }
212
213 SSL_set_bio(m_ssl, m_sbio, m_sbio);
214 String errorDescription;
215 if (acceptSSL(m_ssl, *this, errorDescription) != 0)
216 {
217 shutdownSSL(m_ssl);
218 SSL_free(m_ssl);
219 ERR_remove_state(0); // cleanup memory SSL may have allocated
220 BLOCXX_THROW(SSLException, Format("SSLSocketImpl ctor: SSL accept error while connecting to %1: %2", m_peerAddress.toString(), errorDescription).c_str());
221 }
222 if (!SSLCtxMgr::checkClientCert(m_ssl, m_peerAddress.getName()))
223 {
224 shutdownSSL(m_ssl);
225 SSL_free(m_ssl);
226 ERR_remove_state(0); // cleanup memory SSL may have allocated
227 BLOCXX_THROW(SSLException, "SSL failed to authenticate client");
228 }
229}
231SSLSocketImpl::SSLSocketImpl(const SocketAddress& addr)
232 : SocketBaseImpl(addr)
233{
234 connectSSL();
235}
237SSLSocketImpl::~SSLSocketImpl()
238{
239 try
240 {
241 disconnect();
242 if (m_ssl)
243 {
244 SSL_free(m_ssl);
245 m_ssl = 0;
246 }
247 ERR_remove_state(0); // cleanup memory SSL may have allocated
248 }
249 catch (...)
250 {
251 // no exceptions allowed out of destructors.
252 }
253}
256SSLSocketImpl::getSelectObj() const
257{
258#if defined(BLOCXX_WIN32)
259 Select_t st;
260 st.event = m_event;
261 st.sockfd = m_sockfd;
262 st.isSocket = true;
263 st.networkevents = FD_READ | FD_WRITE;
264 st.doreset = true;
265 return st;
266#else
267 return m_sockfd;
268#endif
269}
271void
272SSLSocketImpl::connect(const SocketAddress& addr)
273{
274 SocketBaseImpl::connect(addr);
275 connectSSL();
276}
278void
279SSLSocketImpl::connectSSL()
280{
281 m_isConnected = false;
282 BLOCXX_ASSERT(m_sslCtx);
283 if (m_ssl)
284 {
285 SSL_free(m_ssl);
286 m_ssl = 0;
287 }
288 ERR_clear_error();
289 m_ssl = SSL_new(m_sslCtx->getSSLCtx());
290
291 if (!m_ssl)
292 {
293 BLOCXX_THROW(SSLException, Format("SSL_new failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
294 }
295 m_sbio = BIO_new_socket(m_sockfd, BIO_NOCLOSE);
296 if (!m_sbio)
297 {
298 SSL_free(m_ssl);
299 BLOCXX_THROW(SSLException, Format("BIO_new_socket failed: %1", SSLCtxMgr::getOpenSSLErrorDescription()).c_str());
300 }
301 SSL_set_bio(m_ssl, m_sbio, m_sbio);
302
303 connectWithSSL(m_ssl, *this);
304
305 if (!SSLCtxMgr::checkServerCert(m_ssl, m_peerAddress.getName()))
306 {
307 BLOCXX_THROW(SSLException, "Failed to validate peer certificate");
308 }
309 m_isConnected = true;
310}
312void
313SSLSocketImpl::disconnect()
314{
315#if defined(BLOCXX_WIN32)
316 if (m_sockfd != INVALID_SOCKET && m_isConnected)
317#else
318 if (m_sockfd != -1 && m_isConnected)
319#endif
320 {
321 if (m_ssl)
322 {
323 shutdownSSL(m_ssl);
324 }
325 }
326 SocketBaseImpl::disconnect();
327}
329int
330SSLSocketImpl::writeAux(const void* dataOut, int dataOutLen)
331{
332 return SSLCtxMgr::sslWrite(m_ssl, static_cast<const char*>(dataOut),
333 dataOutLen);
334}
336int
337SSLSocketImpl::readAux(void* dataIn, int dataInLen)
338{
339 return SSLCtxMgr::sslRead(m_ssl, static_cast<char*>(dataIn),
340 dataInLen);
341}
343SSL*
344SSLSocketImpl::getSSL() const
345{
346 return m_ssl;
347}
348
350bool
351SSLSocketImpl::peerCertVerified() const
352{
353 return (m_owctx.peerCertPassedVerify == OWSSLContext::VERIFY_PASS);
354}
355
357// SSL buffer can contain the data therefore select
358// does not work without checking SSL_pending() first.
359bool
360SSLSocketImpl::waitForInput(const Timeout& timeout)
361{
362 // SSL buffer contains data -> read them
363 if (SSL_pending(m_ssl))
364 {
365 return false;
366 }
367 return SocketBaseImpl::waitForInput(timeout);
368}
370
371} // end namespace BLOCXX_NAMESPACE
372
374#endif // #ifdef BLOCXX_HAVE_OPENSSL
375
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define INVALID_SOCKET
Taken from RFC 1321.
IntrusiveReference< SSLClientCtx > SSLClientCtxRef