]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/modules/ssl.h
Fix secure websocket users not being seen as secure.
[user/henk/code/inspircd.git] / include / modules / ssl.h
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2020 Matt Schatz <genius3000@g3k.solutions>
5  *   Copyright (C) 2019 B00mX0r <b00mx0r@aureus.pw>
6  *   Copyright (C) 2018 Dylan Frank <b00mx0r@aureus.pw>
7  *   Copyright (C) 2013, 2017-2019 Sadie Powell <sadie@witchery.services>
8  *   Copyright (C) 2013, 2015-2016 Attila Molnar <attilamolnar@hush.com>
9  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
10  *   Copyright (C) 2012 ChrisTX <xpipe@hotmail.de>
11  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
12  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
13  *   Copyright (C) 2006 Craig Edwards <brain@inspircd.org>
14  *
15  * This file is part of InspIRCd.  InspIRCd is free software: you can
16  * redistribute it and/or modify it under the terms of the GNU General Public
17  * License as published by the Free Software Foundation, version 2.
18  *
19  * This program is distributed in the hope that it will be useful, but WITHOUT
20  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22  * details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  */
27
28
29 #pragma once
30
31 #include <string>
32 #include "iohook.h"
33
34 /** ssl_cert is a class which abstracts TLS (SSL) certificate
35  * and key information.
36  *
37  * Because gnutls and openssl represent key information in
38  * wildly different ways, this class allows it to be accessed
39  * in a unified manner. These classes are attached to ssl-
40  * connected local users using SSLCertExt
41  */
42 class ssl_cert : public refcountbase
43 {
44  public:
45         std::string dn;
46         std::string issuer;
47         std::string error;
48         std::string fingerprint;
49         bool trusted, invalid, unknownsigner, revoked;
50
51         ssl_cert() : trusted(false), invalid(true), unknownsigner(true), revoked(false) {}
52
53         /** Get certificate distinguished name
54          * @return Certificate DN
55          */
56         const std::string& GetDN()
57         {
58                 return dn;
59         }
60
61         /** Get Certificate issuer
62          * @return Certificate issuer
63          */
64         const std::string& GetIssuer()
65         {
66                 return issuer;
67         }
68
69         /** Get error string if an error has occurred
70          * @return The error associated with this users certificate,
71          * or an empty string if there is no error.
72          */
73         const std::string& GetError()
74         {
75                 return error;
76         }
77
78         /** Get key fingerprint.
79          * @return The key fingerprint as a hex string.
80          */
81         const std::string& GetFingerprint()
82         {
83                 return fingerprint;
84         }
85
86         /** Get trust status
87          * @return True if this is a trusted certificate
88          * (the certificate chain validates)
89          */
90         bool IsTrusted()
91         {
92                 return trusted;
93         }
94
95         /** Get validity status
96          * @return True if the certificate itself is
97          * correctly formed.
98          */
99         bool IsInvalid()
100         {
101                 return invalid;
102         }
103
104         /** Get signer status
105          * @return True if the certificate appears to be
106          * self-signed.
107          */
108         bool IsUnknownSigner()
109         {
110                 return unknownsigner;
111         }
112
113         /** Get revokation status.
114          * @return True if the certificate is revoked.
115          * Note that this only works properly for GnuTLS
116          * right now.
117          */
118         bool IsRevoked()
119         {
120                 return revoked;
121         }
122
123         /** Get certificate usability
124         * @return True if the certificate is not expired nor revoked
125         */
126         bool IsUsable()
127         {
128                 return !invalid && !revoked && error.empty();
129         }
130
131         /** Get CA trust status
132         * @return True if the certificate is issued by a CA
133         * and valid.
134         */
135         bool IsCAVerified()
136         {
137                 return IsUsable() && trusted && !unknownsigner;
138         }
139
140         std::string GetMetaLine()
141         {
142                 std::stringstream value;
143                 bool hasError = !error.empty();
144                 value << (IsInvalid() ? "v" : "V") << (IsTrusted() ? "T" : "t") << (IsRevoked() ? "R" : "r")
145                         << (IsUnknownSigner() ? "s" : "S") << (hasError ? "E" : "e") << " ";
146                 if (hasError)
147                         value << GetError();
148                 else
149                         value << GetFingerprint() << " " << GetDN() << " " << GetIssuer();
150                 return value.str();
151         }
152 };
153
154 class SSLIOHook : public IOHook
155 {
156  protected:
157         /** Peer TLS (SSL) certificate, set by the TLS (SSL) module
158          */
159         reference<ssl_cert> certificate;
160
161         /** Reduce elements in a send queue by appending later elements to the first element until there are no more
162          * elements to append or a desired length is reached
163          * @param sendq SendQ to work on
164          * @param targetsize Target size of the front element
165          */
166         static void FlattenSendQueue(StreamSocket::SendQueue& sendq, size_t targetsize)
167         {
168                 if ((sendq.size() <= 1) || (sendq.front().length() >= targetsize))
169                         return;
170
171                 // Avoid multiple repeated TLS (SSL) encryption invocations
172                 // This adds a single copy of the queue, but avoids
173                 // much more overhead in terms of system calls invoked
174                 // by an IOHook.
175                 std::string tmp;
176                 tmp.reserve(std::min(targetsize, sendq.bytes())+1);
177                 do
178                 {
179                         tmp.append(sendq.front());
180                         sendq.pop_front();
181                 }
182                 while (!sendq.empty() && tmp.length() < targetsize);
183                 sendq.push_front(tmp);
184         }
185
186  public:
187         static SSLIOHook* IsSSL(StreamSocket* sock)
188         {
189                 IOHook* const lasthook = sock->GetLastHook();
190                 if (lasthook && (lasthook->prov->type == IOHookProvider::IOH_SSL))
191                         return static_cast<SSLIOHook*>(lasthook);
192
193                 return NULL;
194         }
195
196         SSLIOHook(IOHookProvider* hookprov)
197                 : IOHook(hookprov)
198         {
199         }
200
201         /**
202          * Get the certificate sent by this peer
203          * @return The TLS (SSL) certificate sent by the peer, NULL if no cert was sent
204          */
205         virtual ssl_cert* GetCertificate() const
206         {
207                 return certificate;
208         }
209
210         /**
211          * Get the fingerprint of the peer's certificate
212          * @return The fingerprint of the TLS (SSL) client certificate sent by the peer,
213          * empty if no cert was sent
214          */
215         virtual std::string GetFingerprint() const
216         {
217                 ssl_cert* cert = GetCertificate();
218                 if (cert && cert->IsUsable())
219                         return cert->GetFingerprint();
220                 return "";
221         }
222
223         /**
224          * Get the ciphersuite negotiated with the peer
225          * @param out String where the ciphersuite string will be appended to
226          */
227         virtual void GetCiphersuite(std::string& out) const = 0;
228
229
230         /** Retrieves the name of the TLS (SSL) connection which is sent via SNI.
231          * @param out String that the server name will be appended to.
232          * returns True if the server name was retrieved; otherwise, false.
233          */
234         virtual bool GetServerName(std::string& out) const = 0;
235 };
236
237 /** Helper functions for obtaining TLS (SSL) client certificates and key fingerprints
238  * from StreamSockets
239  */
240 class SSLClientCert
241 {
242  public:
243         /**
244          * Get the client certificate from a socket
245          * @param sock The socket to get the certificate from, the socket does not have to use TLS (SSL)
246          * @return The TLS (SSL) client certificate information, NULL if the peer is not using TLS (SSL)
247          */
248         static ssl_cert* GetCertificate(StreamSocket* sock)
249         {
250                 SSLIOHook* ssliohook = SSLIOHook::IsSSL(sock);
251                 if (!ssliohook)
252                         return NULL;
253
254                 return ssliohook->GetCertificate();
255         }
256
257         /**
258          * Get the fingerprint of a client certificate from a socket
259          * @param sock The socket to get the certificate fingerprint from, the
260          * socket does not have to use TLS (SSL)
261          * @return The key fingerprint from the TLS (SSL) certificate sent by the peer,
262          * empty if no cert was sent or the peer is not using TLS (SSL)
263          */
264         static std::string GetFingerprint(StreamSocket* sock)
265         {
266                 ssl_cert* cert = SSLClientCert::GetCertificate(sock);
267                 if (cert)
268                         return cert->GetFingerprint();
269                 return "";
270         }
271 };
272
273 class UserCertificateAPIBase : public DataProvider
274 {
275  public:
276         UserCertificateAPIBase(Module* parent)
277                 : DataProvider(parent, "m_sslinfo_api")
278         {
279         }
280
281         /** Get the TLS (SSL) certificate of a user
282          * @param user The user whose certificate to get, user may be remote
283          * @return The TLS (SSL) certificate of the user or NULL if the user is not using TLS (SSL)
284          */
285         virtual ssl_cert* GetCertificate(User* user) = 0;
286
287         /** Set the TLS (SSL) certificate of a user.
288          * @param user The user whose certificate to set.
289          * @param cert The TLS (SSL) certificate to set for the user.
290          */
291         virtual void SetCertificate(User* user, ssl_cert* cert) = 0;
292
293         /** Get the key fingerprint from a user's certificate
294          * @param user The user whose key fingerprint to get, user may be remote
295          * @return The key fingerprint from the user's TLS (SSL) certificate or an empty string
296          * if the user is not using TLS (SSL) or did not provide a client certificate
297          */
298         std::string GetFingerprint(User* user)
299         {
300                 ssl_cert* cert = GetCertificate(user);
301                 if (cert)
302                         return cert->GetFingerprint();
303                 return "";
304         }
305 };
306
307 /** API implemented by m_sslinfo that allows modules to retrieve the TLS (SSL) certificate
308  * information of local and remote users. It can also be used to find out whether a
309  * user is using TLS (SSL) or not.
310  */
311 class UserCertificateAPI : public dynamic_reference<UserCertificateAPIBase>
312 {
313  public:
314         UserCertificateAPI(Module* parent)
315                 : dynamic_reference<UserCertificateAPIBase>(parent, "m_sslinfo_api")
316         {
317         }
318 };