1 /******************************************************************************
3 * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/) *
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. *
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. *
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 ******************************************************************************/
22 using namespace icinga;
24 int I2_EXPORT TlsStream::m_SSLIndex;
25 bool I2_EXPORT TlsStream::m_SSLIndexInitialized = false;
28 * Constructor for the TlsStream class.
30 * @param role The role of the client.
31 * @param sslContext The SSL context for the client.
33 TlsStream::TlsStream(const Stream::Ptr& innerStream, TlsRole role, shared_ptr<SSL_CTX> sslContext)
34 : m_SSLContext(sslContext), m_SendQueue(boost::make_shared<FIFO>()), m_RecvQueue(boost::make_shared<FIFO>()),
35 m_InnerStream(innerStream), m_Role(role)
37 m_InnerStream->OnDataAvailable.connect(boost::bind(&TlsStream::DataAvailableHandler, this));
38 m_InnerStream->OnClosed.connect(boost::bind(&TlsStream::ClosedHandler, this));
43 void TlsStream::Start(void)
45 m_SSL = shared_ptr<SSL>(SSL_new(m_SSLContext.get()), SSL_free);
50 throw_exception(OpenSSLException("SSL_new failed", ERR_get_error()));
52 if (!GetClientCertificate())
53 throw_exception(logic_error("No X509 client certificate was specified."));
55 if (!m_SSLIndexInitialized) {
56 m_SSLIndex = SSL_get_ex_new_index(0, const_cast<char *>("TlsStream"), NULL, NULL, NULL);
57 m_SSLIndexInitialized = true;
60 SSL_set_ex_data(m_SSL.get(), m_SSLIndex, this);
62 SSL_set_verify(m_SSL.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
64 m_BIO = BIO_new_I2Stream(m_InnerStream);
65 SSL_set_bio(m_SSL.get(), m_BIO, m_BIO);
67 if (m_Role == TlsRoleServer)
68 SSL_set_accept_state(m_SSL.get());
70 SSL_set_connect_state(m_SSL.get());
72 /*int rc = SSL_do_handshake(m_SSL.get());
76 OnConnected(GetSelf());
85 * Retrieves the X509 certficate for this client.
87 * @returns The X509 certificate.
89 shared_ptr<X509> TlsStream::GetClientCertificate(void) const
91 return shared_ptr<X509>(SSL_get_certificate(m_SSL.get()), &Utility::NullDeleter);
95 * Retrieves the X509 certficate for the peer.
97 * @returns The X509 certificate.
99 shared_ptr<X509> TlsStream::GetPeerCertificate(void) const
101 return shared_ptr<X509>(SSL_get_peer_certificate(m_SSL.get()), X509_free);
104 void TlsStream::DataAvailableHandler(void)
109 SetException(boost::current_exception());
115 void TlsStream::ClosedHandler(void)
117 SetException(m_InnerStream->GetException());
122 * Processes data for the stream.
124 void TlsStream::HandleIO(void)
126 char data[16 * 1024];
129 if (!IsConnected()) {
130 rc = SSL_do_handshake(m_SSL.get());
135 switch (SSL_get_error(m_SSL.get(), rc)) {
136 case SSL_ERROR_WANT_WRITE:
138 case SSL_ERROR_WANT_READ:
140 case SSL_ERROR_ZERO_RETURN:
144 I2Stream_check_exception(m_BIO);
145 throw_exception(OpenSSLException("SSL_do_handshake failed", ERR_get_error()));
150 bool new_data = false, read_ok = true;
153 rc = SSL_read(m_SSL.get(), data, sizeof(data));
156 m_RecvQueue->Write(data, rc);
159 switch (SSL_get_error(m_SSL.get(), rc)) {
160 case SSL_ERROR_WANT_WRITE:
162 case SSL_ERROR_WANT_READ:
165 case SSL_ERROR_ZERO_RETURN:
169 I2Stream_check_exception(m_BIO);
170 throw_exception(OpenSSLException("SSL_read failed", ERR_get_error()));
176 OnDataAvailable(GetSelf());
178 while (m_SendQueue->GetAvailableBytes() > 0) {
179 size_t count = m_SendQueue->GetAvailableBytes();
184 if (count > sizeof(data))
185 count = sizeof(data);
187 m_SendQueue->Peek(data, count);
189 rc = SSL_write(m_SSL.get(), (const char *)data, count);
192 m_SendQueue->Read(NULL, rc);
194 switch (SSL_get_error(m_SSL.get(), rc)) {
195 case SSL_ERROR_WANT_READ:
197 case SSL_ERROR_WANT_WRITE:
199 case SSL_ERROR_ZERO_RETURN:
203 I2Stream_check_exception(m_BIO);
204 throw_exception(OpenSSLException("SSL_write failed", ERR_get_error()));
213 void TlsStream::Close(void)
216 SSL_shutdown(m_SSL.get());
218 m_SendQueue->Close();
219 m_RecvQueue->Close();
224 size_t TlsStream::GetAvailableBytes(void) const
226 return m_RecvQueue->GetAvailableBytes();
229 size_t TlsStream::Peek(void *buffer, size_t count)
231 return m_RecvQueue->Peek(buffer, count);
234 size_t TlsStream::Read(void *buffer, size_t count)
236 return m_RecvQueue->Read(buffer, count);
239 void TlsStream::Write(const void *buffer, size_t count)
241 m_SendQueue->Write(buffer, count);