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 | |
33 | using Poco::IOException; |
34 | using Poco::TimeoutException; |
35 | using Poco::InvalidArgumentException; |
36 | using Poco::NumberFormatter; |
37 | using 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 | |
44 | namespace Poco { |
45 | namespace Net { |
46 | |
47 | |
48 | SecureSocketImpl::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 | |
59 | SecureSocketImpl::~SecureSocketImpl() |
60 | { |
61 | try |
62 | { |
63 | reset(); |
64 | } |
65 | catch (...) |
66 | { |
67 | poco_unexpected(); |
68 | } |
69 | } |
70 | |
71 | |
72 | SocketImpl* 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 | |
84 | void 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 | |
104 | void 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 | |
115 | void 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 | |
132 | void 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 | |
143 | void 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 | |
195 | void 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 | void SecureSocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool ipV6Only) |
203 | { |
204 | poco_check_ptr(_pSocket); |
205 | |
206 | _pSocket->bind6(address, reuseAddress, ipV6Only); |
207 | } |
208 | |
209 | void SecureSocketImpl::listen(int backlog) |
210 | { |
211 | poco_check_ptr (_pSocket); |
212 | |
213 | _pSocket->listen(backlog); |
214 | } |
215 | |
216 | |
217 | void 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 | |
255 | void SecureSocketImpl::close() |
256 | { |
257 | try |
258 | { |
259 | shutdown(); |
260 | } |
261 | catch (...) |
262 | { |
263 | } |
264 | _pSocket->close(); |
265 | } |
266 | |
267 | |
268 | int 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 | |
298 | int 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 | |
325 | int SecureSocketImpl::available() const |
326 | { |
327 | poco_check_ptr (_pSSL); |
328 | |
329 | return SSL_pending(_pSSL); |
330 | } |
331 | |
332 | |
333 | int 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 | |
353 | void SecureSocketImpl::verifyPeerCertificate() |
354 | { |
355 | if (_peerHostName.empty()) |
356 | verifyPeerCertificate(_pSocket->peerAddress().host().toString()); |
357 | else |
358 | verifyPeerCertificate(_peerHostName); |
359 | } |
360 | |
361 | |
362 | void 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 | |
373 | long 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 | |
392 | bool 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 | |
406 | X509* SecureSocketImpl::peerCertificate() const |
407 | { |
408 | if (_pSSL) |
409 | return SSL_get_peer_certificate(_pSSL); |
410 | else |
411 | return 0; |
412 | } |
413 | |
414 | |
415 | bool 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 | |
451 | int 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 | |
514 | void SecureSocketImpl::setPeerHostName(const std::string& peerHostName) |
515 | { |
516 | _peerHostName = peerHostName; |
517 | } |
518 | |
519 | |
520 | void SecureSocketImpl::reset() |
521 | { |
522 | close(); |
523 | if (_pSSL) |
524 | { |
525 | SSL_free(_pSSL); |
526 | _pSSL = 0; |
527 | } |
528 | } |
529 | |
530 | |
531 | void SecureSocketImpl::abort() |
532 | { |
533 | _pSocket->shutdown(); |
534 | } |
535 | |
536 | |
537 | Session::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 | |
556 | void SecureSocketImpl::useSession(Session::Ptr pSession) |
557 | { |
558 | _pSession = pSession; |
559 | } |
560 | |
561 | |
562 | bool 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 | |