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 | |
203 | void SecureSocketImpl::listen(int backlog) |
204 | { |
205 | poco_check_ptr (_pSocket); |
206 | |
207 | _pSocket->listen(backlog); |
208 | } |
209 | |
210 | |
211 | void 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 | |
249 | void SecureSocketImpl::close() |
250 | { |
251 | try |
252 | { |
253 | shutdown(); |
254 | } |
255 | catch (...) |
256 | { |
257 | } |
258 | _pSocket->close(); |
259 | } |
260 | |
261 | |
262 | int 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 | |
292 | int 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 | |
319 | int SecureSocketImpl::available() const |
320 | { |
321 | poco_check_ptr (_pSSL); |
322 | |
323 | return SSL_pending(_pSSL); |
324 | } |
325 | |
326 | |
327 | int 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 | |
347 | void SecureSocketImpl::verifyPeerCertificate() |
348 | { |
349 | if (_peerHostName.empty()) |
350 | _peerHostName = _pSocket->peerAddress().host().toString(); |
351 | |
352 | verifyPeerCertificate(_peerHostName); |
353 | } |
354 | |
355 | |
356 | void 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 | |
367 | long 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 | |
386 | bool 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 | |
400 | X509* SecureSocketImpl::peerCertificate() const |
401 | { |
402 | if (_pSSL) |
403 | return SSL_get_peer_certificate(_pSSL); |
404 | else |
405 | return 0; |
406 | } |
407 | |
408 | |
409 | bool 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 | |
445 | int 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 | |
508 | void SecureSocketImpl::setPeerHostName(const std::string& peerHostName) |
509 | { |
510 | _peerHostName = peerHostName; |
511 | } |
512 | |
513 | |
514 | void SecureSocketImpl::reset() |
515 | { |
516 | close(); |
517 | if (_pSSL) |
518 | { |
519 | SSL_free(_pSSL); |
520 | _pSSL = 0; |
521 | } |
522 | } |
523 | |
524 | |
525 | void SecureSocketImpl::abort() |
526 | { |
527 | _pSocket->shutdown(); |
528 | } |
529 | |
530 | |
531 | Session::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 | |
550 | void SecureSocketImpl::useSession(Session::Ptr pSession) |
551 | { |
552 | _pSession = pSession; |
553 | } |
554 | |
555 | |
556 | bool 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 | |