]> granicus.if.org Git - icinga2/blob - lib/base/tlsstream.cpp
Merge pull request #5913 from mcktr/fix/itl-http-certificate-age-5610
[icinga2] / lib / base / tlsstream.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/tlsstream.hpp"
21 #include "base/utility.hpp"
22 #include "base/exception.hpp"
23 #include "base/logger.hpp"
24 #include <iostream>
25
26 #ifndef _WIN32
27 #       include <poll.h>
28 #endif /* _WIN32 */
29
30 using namespace icinga;
31
32 int TlsStream::m_SSLIndex;
33 bool TlsStream::m_SSLIndexInitialized = false;
34
35 /**
36  * Constructor for the TlsStream class.
37  *
38  * @param role The role of the client.
39  * @param sslContext The SSL context for the client.
40  */
41 TlsStream::TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr<SSL_CTX>& sslContext)
42         : SocketEvents(socket, this), m_Eof(false), m_HandshakeOK(false), m_VerifyOK(true), m_ErrorCode(0),
43         m_ErrorOccurred(false),  m_Socket(socket), m_Role(role), m_SendQ(new FIFO()), m_RecvQ(new FIFO()),
44         m_CurrentAction(TlsActionNone), m_Retry(false), m_Shutdown(false)
45 {
46         std::ostringstream msgbuf;
47         char errbuf[120];
48
49         m_SSL = std::shared_ptr<SSL>(SSL_new(sslContext.get()), SSL_free);
50
51         if (!m_SSL) {
52                 msgbuf << "SSL_new() failed with code " << ERR_peek_error() << ", \"" << ERR_error_string(ERR_peek_error(), errbuf) << "\"";
53                 Log(LogCritical, "TlsStream", msgbuf.str());
54
55                 BOOST_THROW_EXCEPTION(openssl_error()
56                         << boost::errinfo_api_function("SSL_new")
57                         << errinfo_openssl_error(ERR_peek_error()));
58         }
59
60         if (!m_SSLIndexInitialized) {
61                 m_SSLIndex = SSL_get_ex_new_index(0, const_cast<char *>("TlsStream"), nullptr, nullptr, nullptr);
62                 m_SSLIndexInitialized = true;
63         }
64
65         SSL_set_ex_data(m_SSL.get(), m_SSLIndex, this);
66
67         SSL_set_verify(m_SSL.get(), SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, &TlsStream::ValidateCertificate);
68
69         socket->MakeNonBlocking();
70
71         SSL_set_fd(m_SSL.get(), socket->GetFD());
72
73         if (m_Role == RoleServer)
74                 SSL_set_accept_state(m_SSL.get());
75         else {
76 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
77                 if (!hostname.IsEmpty())
78                         SSL_set_tlsext_host_name(m_SSL.get(), hostname.CStr());
79 #endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
80
81                 SSL_set_connect_state(m_SSL.get());
82         }
83 }
84
85 TlsStream::~TlsStream()
86 {
87         CloseInternal(true);
88 }
89
90 int TlsStream::ValidateCertificate(int preverify_ok, X509_STORE_CTX *ctx)
91 {
92         auto *ssl = static_cast<SSL *>(X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
93         auto *stream = static_cast<TlsStream *>(SSL_get_ex_data(ssl, m_SSLIndex));
94
95         if (!preverify_ok) {
96                 stream->m_VerifyOK = false;
97
98                 std::ostringstream msgbuf;
99                 int err = X509_STORE_CTX_get_error(ctx);
100                 msgbuf << "code " << err << ": " << X509_verify_cert_error_string(err);
101                 stream->m_VerifyError = msgbuf.str();
102         }
103
104         return 1;
105 }
106
107 bool TlsStream::IsVerifyOK() const
108 {
109         return m_VerifyOK;
110 }
111
112 String TlsStream::GetVerifyError() const
113 {
114         return m_VerifyError;
115 }
116
117 /**
118  * Retrieves the X509 certficate for this client.
119  *
120  * @returns The X509 certificate.
121  */
122 std::shared_ptr<X509> TlsStream::GetClientCertificate() const
123 {
124         boost::mutex::scoped_lock lock(m_Mutex);
125         return std::shared_ptr<X509>(SSL_get_certificate(m_SSL.get()), &Utility::NullDeleter);
126 }
127
128 /**
129  * Retrieves the X509 certficate for the peer.
130  *
131  * @returns The X509 certificate.
132  */
133 std::shared_ptr<X509> TlsStream::GetPeerCertificate() const
134 {
135         boost::mutex::scoped_lock lock(m_Mutex);
136         return std::shared_ptr<X509>(SSL_get_peer_certificate(m_SSL.get()), X509_free);
137 }
138
139 void TlsStream::OnEvent(int revents)
140 {
141         int rc;
142         size_t count;
143
144         boost::mutex::scoped_lock lock(m_Mutex);
145
146         if (!m_SSL)
147                 return;
148
149         char buffer[64 * 1024];
150
151         if (m_CurrentAction == TlsActionNone) {
152                 if (revents & (POLLIN | POLLERR | POLLHUP))
153                         m_CurrentAction = TlsActionRead;
154                 else if (m_SendQ->GetAvailableBytes() > 0 && (revents & POLLOUT))
155                         m_CurrentAction = TlsActionWrite;
156                 else {
157                         ChangeEvents(POLLIN);
158                         return;
159                 }
160         }
161
162         bool success = false;
163
164         /* Clear error queue for this thread before using SSL_{read,write,do_handshake}.
165          * Otherwise SSL_*_error() does not work reliably.
166          */
167         ERR_clear_error();
168
169         switch (m_CurrentAction) {
170                 case TlsActionRead:
171                         do {
172                                 rc = SSL_read(m_SSL.get(), buffer, sizeof(buffer));
173
174                                 if (rc > 0) {
175                                         m_RecvQ->Write(buffer, rc);
176                                         success = true;
177                                 }
178                         } while (rc > 0);
179
180                         if (success)
181                                 m_CV.notify_all();
182
183                         break;
184                 case TlsActionWrite:
185                         count = m_SendQ->Peek(buffer, sizeof(buffer), true);
186
187                         rc = SSL_write(m_SSL.get(), buffer, count);
188
189                         if (rc > 0) {
190                                 m_SendQ->Read(nullptr, rc, true);
191                                 success = true;
192                         }
193
194                         break;
195                 case TlsActionHandshake:
196                         rc = SSL_do_handshake(m_SSL.get());
197
198                         if (rc > 0) {
199                                 success = true;
200                                 m_HandshakeOK = true;
201                                 m_CV.notify_all();
202                         }
203
204                         break;
205                 default:
206                         VERIFY(!"Invalid TlsAction");
207         }
208
209         if (rc <= 0) {
210                 int err = SSL_get_error(m_SSL.get(), rc);
211
212                 switch (err) {
213                         case SSL_ERROR_WANT_READ:
214                                 m_Retry = true;
215                                 ChangeEvents(POLLIN);
216
217                                 break;
218                         case SSL_ERROR_WANT_WRITE:
219                                 m_Retry = true;
220                                 ChangeEvents(POLLOUT);
221
222                                 break;
223                         case SSL_ERROR_ZERO_RETURN:
224                                 lock.unlock();
225
226                                 Close();
227
228                                 return;
229                         default:
230                                 m_ErrorCode = ERR_peek_error();
231                                 m_ErrorOccurred = true;
232
233                                 if (m_ErrorCode != 0) {
234                                         Log(LogWarning, "TlsStream")
235                                                 << "OpenSSL error: " << ERR_error_string(m_ErrorCode, nullptr);
236                                 } else {
237                                         Log(LogWarning, "TlsStream", "TLS stream was disconnected.");
238                                 }
239
240                                 lock.unlock();
241
242                                 Close();
243
244                                 return;
245                 }
246         }
247
248         if (success) {
249                 m_CurrentAction = TlsActionNone;
250
251                 if (!m_Eof) {
252                         if (m_SendQ->GetAvailableBytes() > 0)
253                                 ChangeEvents(POLLIN|POLLOUT);
254                         else
255                                 ChangeEvents(POLLIN);
256                 }
257
258                 lock.unlock();
259
260                 while (m_RecvQ->IsDataAvailable() && IsHandlingEvents())
261                         SignalDataAvailable();
262         }
263
264         if (m_Shutdown && !m_SendQ->IsDataAvailable()) {
265                 if (!success)
266                         lock.unlock();
267
268                 Close();
269         }
270 }
271
272 void TlsStream::HandleError() const
273 {
274         if (m_ErrorOccurred) {
275                 BOOST_THROW_EXCEPTION(openssl_error()
276                         << boost::errinfo_api_function("TlsStream::OnEvent")
277                         << errinfo_openssl_error(m_ErrorCode));
278         }
279 }
280
281 void TlsStream::Handshake()
282 {
283         boost::mutex::scoped_lock lock(m_Mutex);
284
285         m_CurrentAction = TlsActionHandshake;
286         ChangeEvents(POLLOUT);
287
288         while (!m_HandshakeOK && !m_ErrorOccurred && !m_Eof)
289                 m_CV.wait(lock);
290
291         if (m_Eof)
292                 BOOST_THROW_EXCEPTION(std::runtime_error("Socket was closed during TLS handshake."));
293
294         HandleError();
295 }
296
297 /**
298  * Processes data for the stream.
299  */
300 size_t TlsStream::Peek(void *buffer, size_t count, bool allow_partial)
301 {
302         boost::mutex::scoped_lock lock(m_Mutex);
303
304         if (!allow_partial)
305                 while (m_RecvQ->GetAvailableBytes() < count && !m_ErrorOccurred && !m_Eof)
306                         m_CV.wait(lock);
307
308         HandleError();
309
310         return m_RecvQ->Peek(buffer, count, true);
311 }
312
313 size_t TlsStream::Read(void *buffer, size_t count, bool allow_partial)
314 {
315         boost::mutex::scoped_lock lock(m_Mutex);
316
317         if (!allow_partial)
318                 while (m_RecvQ->GetAvailableBytes() < count && !m_ErrorOccurred && !m_Eof)
319                         m_CV.wait(lock);
320
321         HandleError();
322
323         return m_RecvQ->Read(buffer, count, true);
324 }
325
326 void TlsStream::Write(const void *buffer, size_t count)
327 {
328         boost::mutex::scoped_lock lock(m_Mutex);
329
330         m_SendQ->Write(buffer, count);
331
332         ChangeEvents(POLLIN|POLLOUT);
333 }
334
335 void TlsStream::Shutdown()
336 {
337         m_Shutdown = true;
338         ChangeEvents(POLLOUT);
339 }
340
341 /**
342  * Closes the stream.
343  */
344 void TlsStream::Close()
345 {
346         CloseInternal(false);
347 }
348
349 void TlsStream::CloseInternal(bool inDestructor)
350 {
351         if (m_Eof)
352                 return;
353
354         m_Eof = true;
355
356         if (!inDestructor)
357                 SignalDataAvailable();
358
359         SocketEvents::Unregister();
360
361         Stream::Close();
362
363         boost::mutex::scoped_lock lock(m_Mutex);
364
365         if (!m_SSL)
366                 return;
367
368         (void)SSL_shutdown(m_SSL.get());
369         m_SSL.reset();
370
371         m_Socket->Close();
372         m_Socket.reset();
373
374         m_CV.notify_all();
375 }
376
377 bool TlsStream::IsEof() const
378 {
379         return m_Eof;
380 }
381
382 bool TlsStream::SupportsWaiting() const
383 {
384         return true;
385 }
386
387 bool TlsStream::IsDataAvailable() const
388 {
389         boost::mutex::scoped_lock lock(m_Mutex);
390
391         return m_RecvQ->GetAvailableBytes() > 0;
392 }
393
394 Socket::Ptr TlsStream::GetSocket() const
395 {
396         return m_Socket;
397 }