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
202void SecureSocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool ipV6Only)
203{
204 poco_check_ptr(_pSocket);
205
206 _pSocket->bind6(address, reuseAddress, ipV6Only);
207}
208
209void SecureSocketImpl::listen(int backlog)
210{
211 poco_check_ptr (_pSocket);
212
213 _pSocket->listen(backlog);
214}
215
216
217void SecureSocketImpl::shutdown()
218{
219 if (_pSSL)
220 {
221 // Don't shut down the socket more than once.
222 int shutdownState = SSL_get_shutdown(_pSSL);
223 bool shutdownSent = (shutdownState & SSL_SENT_SHUTDOWN) == SSL_SENT_SHUTDOWN;
224 if (!shutdownSent)
225 {
226 // A proper clean shutdown requires us to
227 // call SSL_shutdown() a second time if the
228 // first call returns 0.
229 // Previously, this lead to problems with
230 // most web browsers, so we just called
231 // SSL_shutdown() once.
232 // It seems that behavior has changed in newer
233 // OpenSSL and/or browser versions, and things
234 // seem to work better now.
235 int rc = SSL_shutdown(_pSSL);
236#if FIXME
237 #1605: try to do a proper SSL_shutdown()
238 This fix for the issue above breaks the
239 HTTPSClientSessionTest::testCachedSession() Unit test
240 if (rc == 0 && _pSocket->getBlocking())
241 {
242 rc = SSL_shutdown(_pSSL);
243 }
244#endif
245 if (rc < 0) handleError(rc);
246 if (_pSocket->getBlocking())
247 {
248 _pSocket->shutdown();
249 }
250 }
251 }
252}
253
254
255void SecureSocketImpl::close()
256{
257 try
258 {
259 shutdown();
260 }
261 catch (...)
262 {
263 }
264 _pSocket->close();
265}
266
267
268int SecureSocketImpl::sendBytes(const void* buffer, int length, int /*flags*/)
269{
270 poco_assert (_pSocket->initialized());
271 poco_check_ptr (_pSSL);
272
273 int rc;
274 if (_needHandshake)
275 {
276 rc = completeHandshake();
277 if (rc == 1)
278 verifyPeerCertificate();
279 else if (rc == 0)
280 throw SSLConnectionUnexpectedlyClosedException();
281 else
282 return rc;
283 }
284 do
285 {
286 rc = SSL_write(_pSSL, buffer, length);
287 }
288 while (mustRetry(rc));
289 if (rc <= 0)
290 {
291 rc = handleError(rc);
292 if (rc == 0) throw SSLConnectionUnexpectedlyClosedException();
293 }
294 return rc;
295}
296
297
298int SecureSocketImpl::receiveBytes(void* buffer, int length, int /*flags*/)
299{
300 poco_assert (_pSocket->initialized());
301 poco_check_ptr (_pSSL);
302
303 int rc;
304 if (_needHandshake)
305 {
306 rc = completeHandshake();
307 if (rc == 1)
308 verifyPeerCertificate();
309 else
310 return rc;
311 }
312 do
313 {
314 rc = SSL_read(_pSSL, buffer, length);
315 }
316 while (mustRetry(rc));
317 if (rc <= 0)
318 {
319 return handleError(rc);
320 }
321 return rc;
322}
323
324
325int SecureSocketImpl::available() const
326{
327 poco_check_ptr (_pSSL);
328
329 return SSL_pending(_pSSL);
330}
331
332
333int SecureSocketImpl::completeHandshake()
334{
335 poco_assert (_pSocket->initialized());
336 poco_check_ptr (_pSSL);
337
338 int rc;
339 do
340 {
341 rc = SSL_do_handshake(_pSSL);
342 }
343 while (mustRetry(rc));
344 if (rc <= 0)
345 {
346 return handleError(rc);
347 }
348 _needHandshake = false;
349 return rc;
350}
351
352
353void SecureSocketImpl::verifyPeerCertificate()
354{
355 if (_peerHostName.empty())
356 verifyPeerCertificate(_pSocket->peerAddress().host().toString());
357 else
358 verifyPeerCertificate(_peerHostName);
359}
360
361
362void SecureSocketImpl::verifyPeerCertificate(const std::string& hostName)
363{
364 long certErr = verifyPeerCertificateImpl(hostName);
365 if (certErr != X509_V_OK)
366 {
367 std::string msg = Utility::convertCertificateError(certErr);
368 throw CertificateValidationException("Unacceptable certificate from " + hostName, msg);
369 }
370}
371
372
373long SecureSocketImpl::verifyPeerCertificateImpl(const std::string& hostName)
374{
375 Context::VerificationMode mode = _pContext->verificationMode();
376 if (mode == Context::VERIFY_NONE || !_pContext->extendedCertificateVerificationEnabled() ||
377 (mode != Context::VERIFY_STRICT && isLocalHost(hostName)))
378 {
379 return X509_V_OK;
380 }
381
382 X509* pCert = SSL_get_peer_certificate(_pSSL);
383 if (pCert)
384 {
385 X509Certificate cert(pCert);
386 return cert.verify(hostName) ? X509_V_OK : X509_V_ERR_APPLICATION_VERIFICATION;
387 }
388 else return X509_V_OK;
389}
390
391
392bool SecureSocketImpl::isLocalHost(const std::string& hostName)
393{
394 try
395 {
396 SocketAddress addr(hostName, 0);
397 return addr.host().isLoopback();
398 }
399 catch (Poco::Exception&)
400 {
401 return false;
402 }
403}
404
405
406X509* SecureSocketImpl::peerCertificate() const
407{
408 if (_pSSL)
409 return SSL_get_peer_certificate(_pSSL);
410 else
411 return 0;
412}
413
414
415bool SecureSocketImpl::mustRetry(int rc)
416{
417 if (rc <= 0)
418 {
419 int sslError = SSL_get_error(_pSSL, rc);
420 int socketError = _pSocket->lastError();
421 switch (sslError)
422 {
423 case SSL_ERROR_WANT_READ:
424 if (_pSocket->getBlocking())
425 {
426 if (_pSocket->poll(_pSocket->getReceiveTimeout(), Poco::Net::Socket::SELECT_READ))
427 return true;
428 else
429 throw Poco::TimeoutException();
430 }
431 break;
432 case SSL_ERROR_WANT_WRITE:
433 if (_pSocket->getBlocking())
434 {
435 if (_pSocket->poll(_pSocket->getSendTimeout(), Poco::Net::Socket::SELECT_WRITE))
436 return true;
437 else
438 throw Poco::TimeoutException();
439 }
440 break;
441 case SSL_ERROR_SYSCALL:
442 return socketError == POCO_EAGAIN || socketError == POCO_EINTR;
443 default:
444 return socketError == POCO_EINTR;
445 }
446 }
447 return false;
448}
449
450
451int SecureSocketImpl::handleError(int rc)
452{
453 if (rc > 0) return rc;
454
455 int sslError = SSL_get_error(_pSSL, rc);
456 int error = SocketImpl::lastError();
457
458 switch (sslError)
459 {
460 case SSL_ERROR_ZERO_RETURN:
461 return 0;
462 case SSL_ERROR_WANT_READ:
463 return SecureStreamSocket::ERR_SSL_WANT_READ;
464 case SSL_ERROR_WANT_WRITE:
465 return SecureStreamSocket::ERR_SSL_WANT_WRITE;
466 case SSL_ERROR_WANT_CONNECT:
467 case SSL_ERROR_WANT_ACCEPT:
468 case SSL_ERROR_WANT_X509_LOOKUP:
469 // these should not occur
470 poco_bugcheck();
471 return rc;
472 case SSL_ERROR_SYSCALL:
473 if (error != 0)
474 {
475 SocketImpl::error(error);
476 }
477 // fallthrough
478 default:
479 {
480 long lastError = ERR_get_error();
481 if (lastError == 0)
482 {
483 if (rc == 0)
484 {
485 // Most web browsers do this, don't report an error
486 if (_pContext->isForServerUse())
487 return 0;
488 else
489 throw SSLConnectionUnexpectedlyClosedException();
490 }
491 else if (rc == -1)
492 {
493 throw SSLConnectionUnexpectedlyClosedException();
494 }
495 else
496 {
497 SecureStreamSocketImpl::error(Poco::format("The BIO reported an error: %d", rc));
498 }
499 }
500 else
501 {
502 char buffer[256];
503 ERR_error_string_n(lastError, buffer, sizeof(buffer));
504 std::string msg(buffer);
505 throw SSLException(msg);
506 }
507 }
508 break;
509 }
510 return rc;
511}
512
513
514void SecureSocketImpl::setPeerHostName(const std::string& peerHostName)
515{
516 _peerHostName = peerHostName;
517}
518
519
520void SecureSocketImpl::reset()
521{
522 close();
523 if (_pSSL)
524 {
525 SSL_free(_pSSL);
526 _pSSL = 0;
527 }
528}
529
530
531void SecureSocketImpl::abort()
532{
533 _pSocket->shutdown();
534}
535
536
537Session::Ptr SecureSocketImpl::currentSession()
538{
539 if (_pSSL)
540 {
541 SSL_SESSION* pSession = SSL_get1_session(_pSSL);
542 if (pSession)
543 {
544 if (_pSession && pSession == _pSession->sslSession())
545 {
546 SSL_SESSION_free(pSession);
547 return _pSession;
548 }
549 else return new Session(pSession);
550 }
551 }
552 return 0;
553}
554
555
556void SecureSocketImpl::useSession(Session::Ptr pSession)
557{
558 _pSession = pSession;
559}
560
561
562bool SecureSocketImpl::sessionWasReused()
563{
564 if (_pSSL)
565 return SSL_session_reused(_pSSL) != 0;
566 else
567 return false;
568}
569
570
571} } // namespace Poco::Net
572