1//
2// SecureSocketImpl.cpp
3//
4// Library: NetSSL_OpenSSL
5// Package: SSLSockets
6// Module: SecureSocketImpl
7//
8// Copyright (c) 2006-2010, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Net/SecureSocketImpl.h"
16#include "Poco/Net/SSLException.h"
17#include "Poco/Net/Context.h"
18#include "Poco/Net/X509Certificate.h"
19#include "Poco/Net/Utility.h"
20#include "Poco/Net/SecureStreamSocket.h"
21#include "Poco/Net/SecureStreamSocketImpl.h"
22#include "Poco/Net/StreamSocketImpl.h"
23#include "Poco/Net/StreamSocket.h"
24#include "Poco/Net/NetException.h"
25#include "Poco/Net/DNS.h"
26#include "Poco/NumberFormatter.h"
27#include "Poco/NumberParser.h"
28#include "Poco/Format.h"
29#include <openssl/x509v3.h>
30#include <openssl/err.h>
31
32
33using Poco::IOException;
34using Poco::TimeoutException;
35using Poco::InvalidArgumentException;
36using Poco::NumberFormatter;
37using Poco::Timespan;
38
39
40// workaround for C++-incompatible macro
41#define POCO_BIO_set_nbio_accept(b,n) BIO_ctrl(b,BIO_C_SET_ACCEPT,1,(void*)((n)?"a":NULL))
42
43
44namespace Poco {
45namespace Net {
46
47
48SecureSocketImpl::SecureSocketImpl(Poco::AutoPtr<SocketImpl> pSocketImpl, Context::Ptr pContext):
49 _pSSL(0),
50 _pSocket(pSocketImpl),
51 _pContext(pContext),
52 _needHandshake(false)
53{
54 poco_check_ptr (_pSocket);
55 poco_check_ptr (_pContext);
56}
57
58
59SecureSocketImpl::~SecureSocketImpl()
60{
61 try
62 {
63 reset();
64 }
65 catch (...)
66 {
67 poco_unexpected();
68 }
69}
70
71
72SocketImpl* SecureSocketImpl::acceptConnection(SocketAddress& clientAddr)
73{
74 poco_assert (!_pSSL);
75
76 StreamSocket ss = _pSocket->acceptConnection(clientAddr);
77 Poco::AutoPtr<SecureStreamSocketImpl> pSecureStreamSocketImpl = new SecureStreamSocketImpl(static_cast<StreamSocketImpl*>(ss.impl()), _pContext);
78 pSecureStreamSocketImpl->acceptSSL();
79 pSecureStreamSocketImpl->duplicate();
80 return pSecureStreamSocketImpl;
81}
82
83
84void SecureSocketImpl::acceptSSL()
85{
86 poco_assert (!_pSSL);
87
88 BIO* pBIO = BIO_new(BIO_s_socket());
89 if (!pBIO) throw SSLException("Cannot create BIO object");
90 BIO_set_fd(pBIO, static_cast<int>(_pSocket->sockfd()), BIO_NOCLOSE);
91
92 _pSSL = SSL_new(_pContext->sslContext());
93 if (!_pSSL)
94 {
95 BIO_free(pBIO);
96 throw SSLException("Cannot create SSL object");
97 }
98 SSL_set_bio(_pSSL, pBIO, pBIO);
99 SSL_set_accept_state(_pSSL);
100 _needHandshake = true;
101}
102
103
104void SecureSocketImpl::connect(const SocketAddress& address, bool performHandshake)
105{
106 if (_pSSL) reset();
107
108 poco_assert (!_pSSL);
109
110 _pSocket->connect(address);
111 connectSSL(performHandshake);
112}
113
114
115void SecureSocketImpl::connect(const SocketAddress& address, const Poco::Timespan& timeout, bool performHandshake)
116{
117 if (_pSSL) reset();
118
119 poco_assert (!_pSSL);
120
121 _pSocket->connect(address, timeout);
122 Poco::Timespan receiveTimeout = _pSocket->getReceiveTimeout();
123 Poco::Timespan sendTimeout = _pSocket->getSendTimeout();
124 _pSocket->setReceiveTimeout(timeout);
125 _pSocket->setSendTimeout(timeout);
126 connectSSL(performHandshake);
127 _pSocket->setReceiveTimeout(receiveTimeout);
128 _pSocket->setSendTimeout(sendTimeout);
129}
130
131
132void SecureSocketImpl::connectNB(const SocketAddress& address)
133{
134 if (_pSSL) reset();
135
136 poco_assert (!_pSSL);
137
138 _pSocket->connectNB(address);
139 connectSSL(false);
140}
141
142
143void SecureSocketImpl::connectSSL(bool performHandshake)
144{
145 poco_assert (!_pSSL);
146 poco_assert (_pSocket->initialized());
147
148 BIO* pBIO = BIO_new(BIO_s_socket());
149 if (!pBIO) throw SSLException("Cannot create SSL BIO object");
150 BIO_set_fd(pBIO, static_cast<int>(_pSocket->sockfd()), BIO_NOCLOSE);
151
152 _pSSL = SSL_new(_pContext->sslContext());
153 if (!_pSSL)
154 {
155 BIO_free(pBIO);
156 throw SSLException("Cannot create SSL object");
157 }
158 SSL_set_bio(_pSSL, pBIO, pBIO);
159
160#if OPENSSL_VERSION_NUMBER >= 0x0908060L && !defined(OPENSSL_NO_TLSEXT)
161 if (!_peerHostName.empty())
162 {
163 SSL_set_tlsext_host_name(_pSSL, _peerHostName.c_str());
164 }
165#endif
166
167 if (_pSession)
168 {
169 SSL_set_session(_pSSL, _pSession->sslSession());
170 }
171
172 try
173 {
174 if (performHandshake && _pSocket->getBlocking())
175 {
176 int ret = SSL_connect(_pSSL);
177 handleError(ret);
178 verifyPeerCertificate();
179 }
180 else
181 {
182 SSL_set_connect_state(_pSSL);
183 _needHandshake = true;
184 }
185 }
186 catch (...)
187 {
188 SSL_free(_pSSL);
189 _pSSL = 0;
190 throw;
191 }
192}
193
194
195void SecureSocketImpl::bind(const SocketAddress& address, bool reuseAddress, bool reusePort)
196{
197 poco_check_ptr (_pSocket);
198
199 _pSocket->bind(address, reuseAddress, reusePort);
200}
201
202
203void SecureSocketImpl::listen(int backlog)
204{
205 poco_check_ptr (_pSocket);
206
207 _pSocket->listen(backlog);
208}
209
210
211void SecureSocketImpl::shutdown()
212{
213 if (_pSSL)
214 {
215 // Don't shut down the socket more than once.
216 int shutdownState = SSL_get_shutdown(_pSSL);
217 bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN;
218 if (!shutdownSent)
219 {
220 // A proper clean shutdown requires us to
221 // call SSL_shutdown() a second time if the
222 // first call returns 0.
223 // Previously, this lead to problems with
224 // most web browsers, so we just called
225 // SSL_shutdown() once.
226 // It seems that behavior has changed in newer
227 // OpenSSL and/or browser versions, and things
228 // seem to work better now.
229 int rc = SSL_shutdown(_pSSL);
230#if FIXME
231 #1605: try to do a proper SSL_shutdown()
232 This fix for the issue above breaks the
233 HTTPSClientSessionTest::testCachedSession() Unit test
234 if (rc == 0 && _pSocket->getBlocking())
235 {
236 rc = SSL_shutdown(_pSSL);
237 }
238#endif
239 if (rc < 0) handleError(rc);
240 if (_pSocket->getBlocking())
241 {
242 _pSocket->shutdown();
243 }
244 }
245 }
246}
247
248
249void SecureSocketImpl::close()
250{
251 try
252 {
253 shutdown();
254 }
255 catch (...)
256 {
257 }
258 _pSocket->close();
259}
260
261
262int SecureSocketImpl::sendBytes(const void* buffer, int length, int flags)
263{
264 poco_assert (_pSocket->initialized());
265 poco_check_ptr (_pSSL);
266
267 int rc;
268 if (_needHandshake)
269 {
270 rc = completeHandshake();
271 if (rc == 1)
272 verifyPeerCertificate();
273 else if (rc == 0)
274 throw SSLConnectionUnexpectedlyClosedException();
275 else
276 return rc;
277 }
278 do
279 {
280 rc = SSL_write(_pSSL, buffer, length);
281 }
282 while (mustRetry(rc));
283 if (rc <= 0)
284 {
285 rc = handleError(rc);
286 if (rc == 0) throw SSLConnectionUnexpectedlyClosedException();
287 }
288 return rc;
289}
290
291
292int SecureSocketImpl::receiveBytes(void* buffer, int length, int flags)
293{
294 poco_assert (_pSocket->initialized());
295 poco_check_ptr (_pSSL);
296
297 int rc;
298 if (_needHandshake)
299 {
300 rc = completeHandshake();
301 if (rc == 1)
302 verifyPeerCertificate();
303 else
304 return rc;
305 }
306 do
307 {
308 rc = SSL_read(_pSSL, buffer, length);
309 }
310 while (mustRetry(rc));
311 if (rc <= 0)
312 {
313 return handleError(rc);
314 }
315 return rc;
316}
317
318
319int SecureSocketImpl::available() const
320{
321 poco_check_ptr (_pSSL);
322
323 return SSL_pending(_pSSL);
324}
325
326
327int SecureSocketImpl::completeHandshake()
328{
329 poco_assert (_pSocket->initialized());
330 poco_check_ptr (_pSSL);
331
332 int rc;
333 do
334 {
335 rc = SSL_do_handshake(_pSSL);
336 }
337 while (mustRetry(rc));
338 if (rc <= 0)
339 {
340 return handleError(rc);
341 }
342 _needHandshake = false;
343 return rc;
344}
345
346
347void SecureSocketImpl::verifyPeerCertificate()
348{
349 if (_peerHostName.empty())
350 _peerHostName = _pSocket->peerAddress().host().toString();
351
352 verifyPeerCertificate(_peerHostName);
353}
354
355
356void SecureSocketImpl::verifyPeerCertificate(const std::string& hostName)
357{
358 long certErr = verifyPeerCertificateImpl(hostName);
359 if (certErr != X509_V_OK)
360 {
361 std::string msg = Utility::convertCertificateError(certErr);
362 throw CertificateValidationException("Unacceptable certificate from " + hostName, msg);
363 }
364}
365
366
367long SecureSocketImpl::verifyPeerCertificateImpl(const std::string& hostName)
368{
369 Context::VerificationMode mode = _pContext->verificationMode();
370 if (mode == Context::VERIFY_NONE || !_pContext->extendedCertificateVerificationEnabled() ||
371 (mode != Context::VERIFY_STRICT && isLocalHost(hostName)))
372 {
373 return X509_V_OK;
374 }
375
376 X509* pCert = SSL_get_peer_certificate(_pSSL);
377 if (pCert)
378 {
379 X509Certificate cert(pCert);
380 return cert.verify(hostName) ? X509_V_OK : X509_V_ERR_APPLICATION_VERIFICATION;
381 }
382 else return X509_V_OK;
383}
384
385
386bool SecureSocketImpl::isLocalHost(const std::string& hostName)
387{
388 try
389 {
390 SocketAddress addr(hostName, 0);
391 return addr.host().isLoopback();
392 }
393 catch (Poco::Exception&)
394 {
395 return false;
396 }
397}
398
399
400X509* SecureSocketImpl::peerCertificate() const
401{
402 if (_pSSL)
403 return SSL_get_peer_certificate(_pSSL);
404 else
405 return 0;
406}
407
408
409bool SecureSocketImpl::mustRetry(int rc)
410{
411 if (rc <= 0)
412 {
413 int sslError = SSL_get_error(_pSSL, rc);
414 int socketError = _pSocket->lastError();
415 switch (sslError)
416 {
417 case SSL_ERROR_WANT_READ:
418 if (_pSocket->getBlocking())
419 {
420 if (_pSocket->poll(_pSocket->getReceiveTimeout(), Poco::Net::Socket::SELECT_READ))
421 return true;
422 else
423 throw Poco::TimeoutException();
424 }
425 break;
426 case SSL_ERROR_WANT_WRITE:
427 if (_pSocket->getBlocking())
428 {
429 if (_pSocket->poll(_pSocket->getSendTimeout(), Poco::Net::Socket::SELECT_WRITE))
430 return true;
431 else
432 throw Poco::TimeoutException();
433 }
434 break;
435 case SSL_ERROR_SYSCALL:
436 return socketError == POCO_EAGAIN || socketError == POCO_EINTR;
437 default:
438 return socketError == POCO_EINTR;
439 }
440 }
441 return false;
442}
443
444
445int SecureSocketImpl::handleError(int rc)
446{
447 if (rc > 0) return rc;
448
449 int sslError = SSL_get_error(_pSSL, rc);
450 int error = SocketImpl::lastError();
451
452 switch (sslError)
453 {
454 case SSL_ERROR_ZERO_RETURN:
455 return 0;
456 case SSL_ERROR_WANT_READ:
457 return SecureStreamSocket::ERR_SSL_WANT_READ;
458 case SSL_ERROR_WANT_WRITE:
459 return SecureStreamSocket::ERR_SSL_WANT_WRITE;
460 case SSL_ERROR_WANT_CONNECT:
461 case SSL_ERROR_WANT_ACCEPT:
462 case SSL_ERROR_WANT_X509_LOOKUP:
463 // these should not occur
464 poco_bugcheck();
465 return rc;
466 case SSL_ERROR_SYSCALL:
467 if (error != 0)
468 {
469 SocketImpl::error(error);
470 }
471 // fallthrough
472 default:
473 {
474 long lastError = ERR_get_error();
475 if (lastError == 0)
476 {
477 if (rc == 0)
478 {
479 // Most web browsers do this, don't report an error
480 if (_pContext->isForServerUse())
481 return 0;
482 else
483 throw SSLConnectionUnexpectedlyClosedException();
484 }
485 else if (rc == -1)
486 {
487 throw SSLConnectionUnexpectedlyClosedException();
488 }
489 else
490 {
491 SecureStreamSocketImpl::error(Poco::format("The BIO reported an error: %d", rc));
492 }
493 }
494 else
495 {
496 char buffer[256];
497 ERR_error_string_n(lastError, buffer, sizeof(buffer));
498 std::string msg(buffer);
499 throw SSLException(msg);
500 }
501 }
502 break;
503 }
504 return rc;
505}
506
507
508void SecureSocketImpl::setPeerHostName(const std::string& peerHostName)
509{
510 _peerHostName = peerHostName;
511}
512
513
514void SecureSocketImpl::reset()
515{
516 close();
517 if (_pSSL)
518 {
519 SSL_free(_pSSL);
520 _pSSL = 0;
521 }
522}
523
524
525void SecureSocketImpl::abort()
526{
527 _pSocket->shutdown();
528}
529
530
531Session::Ptr SecureSocketImpl::currentSession()
532{
533 if (_pSSL)
534 {
535 SSL_SESSION* pSession = SSL_get1_session(_pSSL);
536 if (pSession)
537 {
538 if (_pSession && pSession == _pSession->sslSession())
539 {
540 SSL_SESSION_free(pSession);
541 return _pSession;
542 }
543 else return new Session(pSession);
544 }
545 }
546 return 0;
547}
548
549
550void SecureSocketImpl::useSession(Session::Ptr pSession)
551{
552 _pSession = pSession;
553}
554
555
556bool SecureSocketImpl::sessionWasReused()
557{
558 if (_pSSL)
559 return SSL_session_reused(_pSSL) != 0;
560 else
561 return false;
562}
563
564
565} } // namespace Poco::Net
566