]> granicus.if.org Git - icinga2/blob - lib/base/tlsstream.hpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / tlsstream.hpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #ifndef TLSSTREAM_H
4 #define TLSSTREAM_H
5
6 #include "base/i2-base.hpp"
7 #include "base/socket.hpp"
8 #include "base/socketevents.hpp"
9 #include "base/stream.hpp"
10 #include "base/tlsutility.hpp"
11 #include "base/fifo.hpp"
12 #include <memory>
13 #include <utility>
14 #include <boost/asio/buffered_stream.hpp>
15 #include <boost/asio/io_service.hpp>
16 #include <boost/asio/ip/tcp.hpp>
17 #include <boost/asio/ssl/context.hpp>
18 #include <boost/asio/ssl/stream.hpp>
19
20 namespace icinga
21 {
22
23 enum TlsAction
24 {
25         TlsActionNone,
26         TlsActionRead,
27         TlsActionWrite,
28         TlsActionHandshake
29 };
30
31 /**
32  * A TLS stream.
33  *
34  * @ingroup base
35  */
36 class TlsStream final : public SocketEvents
37 {
38 public:
39         DECLARE_PTR_TYPEDEFS(TlsStream);
40
41         TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr<SSL_CTX>& sslContext = MakeSSLContext());
42         TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, const std::shared_ptr<boost::asio::ssl::context>& sslContext);
43         ~TlsStream() override;
44
45         Socket::Ptr GetSocket() const;
46
47         std::shared_ptr<X509> GetClientCertificate() const;
48         std::shared_ptr<X509> GetPeerCertificate() const;
49
50         void Handshake();
51
52         void Close() override;
53         void Shutdown() override;
54
55         size_t Peek(void *buffer, size_t count, bool allow_partial = false) override;
56         size_t Read(void *buffer, size_t count, bool allow_partial = false) override;
57         void Write(const void *buffer, size_t count) override;
58
59         bool IsEof() const override;
60
61         bool SupportsWaiting() const override;
62         bool IsDataAvailable() const override;
63
64         bool IsVerifyOK() const;
65         String GetVerifyError() const;
66
67 private:
68         std::shared_ptr<SSL> m_SSL;
69         bool m_Eof;
70         mutable boost::mutex m_Mutex;
71         mutable boost::condition_variable m_CV;
72         bool m_HandshakeOK;
73         bool m_VerifyOK;
74         String m_VerifyError;
75         int m_ErrorCode;
76         bool m_ErrorOccurred;
77
78         Socket::Ptr m_Socket;
79         ConnectionRole m_Role;
80
81         FIFO::Ptr m_SendQ;
82         FIFO::Ptr m_RecvQ;
83
84         TlsAction m_CurrentAction;
85         bool m_Retry;
86         bool m_Shutdown;
87
88         static int m_SSLIndex;
89         static bool m_SSLIndexInitialized;
90
91         TlsStream(const Socket::Ptr& socket, const String& hostname, ConnectionRole role, SSL_CTX* sslContext);
92
93         void OnEvent(int revents) override;
94
95         void HandleError() const;
96
97         static int ValidateCertificate(int preverify_ok, X509_STORE_CTX *ctx);
98         static void NullCertificateDeleter(X509 *certificate);
99
100         void CloseInternal(bool inDestructor);
101 };
102
103 struct UnbufferedAsioTlsStreamParams
104 {
105         boost::asio::io_service& IoService;
106         boost::asio::ssl::context& SslContext;
107         const String& Hostname;
108 };
109
110 typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> AsioTcpTlsStream;
111
112 class UnbufferedAsioTlsStream : public AsioTcpTlsStream
113 {
114 public:
115         inline
116         UnbufferedAsioTlsStream(UnbufferedAsioTlsStreamParams& init)
117                 : stream(init.IoService, init.SslContext), m_VerifyOK(true), m_Hostname(init.Hostname)
118         {
119         }
120
121         bool IsVerifyOK() const;
122         String GetVerifyError() const;
123         std::shared_ptr<X509> GetPeerCertificate();
124
125         template<class... Args>
126         inline
127         auto async_handshake(handshake_type type, Args&&... args) -> decltype(((AsioTcpTlsStream*)nullptr)->async_handshake(type, std::forward<Args>(args)...))
128         {
129                 BeforeHandshake(type);
130
131                 return AsioTcpTlsStream::async_handshake(type, std::forward<Args>(args)...);
132         }
133
134         template<class... Args>
135         inline
136         auto handshake(handshake_type type, Args&&... args) -> decltype(((AsioTcpTlsStream*)nullptr)->handshake(type, std::forward<Args>(args)...))
137         {
138                 BeforeHandshake(type);
139
140                 return AsioTcpTlsStream::handshake(type, std::forward<Args>(args)...);
141         }
142
143 private:
144         bool m_VerifyOK;
145         String m_VerifyError;
146         String m_Hostname;
147
148         void BeforeHandshake(handshake_type type);
149 };
150
151 class AsioTlsStream : public boost::asio::buffered_stream<UnbufferedAsioTlsStream>
152 {
153 public:
154         inline
155         AsioTlsStream(boost::asio::io_service& ioService, boost::asio::ssl::context& sslContext, const String& hostname = String())
156                 : AsioTlsStream(UnbufferedAsioTlsStreamParams{ioService, sslContext, hostname})
157         {
158         }
159
160 private:
161         inline
162         AsioTlsStream(UnbufferedAsioTlsStreamParams init)
163                 : buffered_stream(init)
164         {
165         }
166 };
167
168 typedef boost::asio::buffered_stream<boost::asio::ip::tcp::socket> AsioTcpStream;
169 typedef std::pair<std::shared_ptr<AsioTlsStream>, std::shared_ptr<AsioTcpStream>> OptionalTlsStream;
170
171 }
172
173 #endif /* TLSSTREAM_H */