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 
43 #include "blocxx/BLOCXX_config.h"
44 
45 #ifdef BLOCXX_HAVE_OPENSSL
46 
47 #include "blocxx/SSLSocketImpl.hpp"
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"
53 #include "blocxx/SocketUtils.hpp"
54 
55 
56 namespace BLOCXX_NAMESPACE
57 {
60  : SocketBaseImpl()
61  , m_ssl(0)
62  , m_sslCtx(sslCtx)
63 {
64 }
65 
66 namespace
67 {
68 
69 void 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 
81 void 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 
92 void 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 
116 int 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 
145  : SocketBaseImpl()
146  , m_ssl(0)
147  , m_sbio(0)
148 {
149 }
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.
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 }
231 SSLSocketImpl::SSLSocketImpl(const SocketAddress& addr)
232  : SocketBaseImpl(addr)
233 {
234  connectSSL();
235 }
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 }
255 Select_t
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 }
271 void
272 SSLSocketImpl::connect(const SocketAddress& addr)
273 {
275  connectSSL();
276 }
278 void
280 {
281  m_isConnected = false;
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 }
312 void
314 {
315 #if defined(BLOCXX_WIN32)
317 #else
318  if (m_sockfd != -1 && m_isConnected)
319 #endif
320  {
321  if (m_ssl)
322  {
323  shutdownSSL(m_ssl);
324  }
325  }
327 }
329 int
330 SSLSocketImpl::writeAux(const void* dataOut, int dataOutLen)
331 {
332  return SSLCtxMgr::sslWrite(m_ssl, static_cast<const char*>(dataOut),
333  dataOutLen);
334 }
336 int
337 SSLSocketImpl::readAux(void* dataIn, int dataInLen)
338 {
339  return SSLCtxMgr::sslRead(m_ssl, static_cast<char*>(dataIn),
340  dataInLen);
341 }
343 SSL*
344 SSLSocketImpl::getSSL() const
345 {
346  return m_ssl;
347 }
348 
350 bool
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.
359 bool
360 SSLSocketImpl::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