]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/modules/ssl.h
Update copyright headers.
[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, 2021 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 /** I/O hook provider for SSL modules. */
155 class SSLIOHookProvider : public IOHookProvider
156 {
157 public:
158         SSLIOHookProvider(Module* mod, const std::string& Name)
159                 : IOHookProvider(mod, "ssl/" + Name, IOH_SSL)
160         {
161         }
162 };
163
164 class SSLIOHook : public IOHook
165 {
166  protected:
167         /** Peer TLS (SSL) certificate, set by the TLS (SSL) module
168          */
169         reference<ssl_cert> certificate;
170
171         /** Reduce elements in a send queue by appending later elements to the first element until there are no more
172          * elements to append or a desired length is reached
173          * @param sendq SendQ to work on
174          * @param targetsize Target size of the front element
175          */
176         static void FlattenSendQueue(StreamSocket::SendQueue& sendq, size_t targetsize)
177         {
178                 if ((sendq.size() <= 1) || (sendq.front().length() >= targetsize))
179                         return;
180
181                 // Avoid multiple repeated TLS (SSL) encryption invocations
182                 // This adds a single copy of the queue, but avoids
183                 // much more overhead in terms of system calls invoked
184                 // by an IOHook.
185                 std::string tmp;
186                 tmp.reserve(std::min(targetsize, sendq.bytes())+1);
187                 do
188                 {
189                         tmp.append(sendq.front());
190                         sendq.pop_front();
191                 }
192                 while (!sendq.empty() && tmp.length() < targetsize);
193                 sendq.push_front(tmp);
194         }
195
196  public:
197         static SSLIOHook* IsSSL(StreamSocket* sock)
198         {
199                 IOHook* const lasthook = sock->GetLastHook();
200                 if (lasthook && (lasthook->prov->type == IOHookProvider::IOH_SSL))
201                         return static_cast<SSLIOHook*>(lasthook);
202
203                 return NULL;
204         }
205
206         SSLIOHook(IOHookProvider* hookprov)
207                 : IOHook(hookprov)
208         {
209         }
210
211         /**
212          * Get the certificate sent by this peer
213          * @return The TLS (SSL) certificate sent by the peer, NULL if no cert was sent
214          */
215         virtual ssl_cert* GetCertificate() const
216         {
217                 return certificate;
218         }
219
220         /**
221          * Get the fingerprint of the peer's certificate
222          * @return The fingerprint of the TLS (SSL) client certificate sent by the peer,
223          * empty if no cert was sent
224          */
225         virtual std::string GetFingerprint() const
226         {
227                 ssl_cert* cert = GetCertificate();
228                 if (cert && cert->IsUsable())
229                         return cert->GetFingerprint();
230                 return "";
231         }
232
233         /**
234          * Get the ciphersuite negotiated with the peer
235          * @param out String where the ciphersuite string will be appended to
236          */
237         virtual void GetCiphersuite(std::string& out) const = 0;
238
239
240         /** Retrieves the name of the TLS (SSL) connection which is sent via SNI.
241          * @param out String that the server name will be appended to.
242          * returns True if the server name was retrieved; otherwise, false.
243          */
244         virtual bool GetServerName(std::string& out) const = 0;
245 };
246
247 /** Helper functions for obtaining TLS (SSL) client certificates and key fingerprints
248  * from StreamSockets
249  */
250 class SSLClientCert
251 {
252  public:
253         /**
254          * Get the client certificate from a socket
255          * @param sock The socket to get the certificate from, the socket does not have to use TLS (SSL)
256          * @return The TLS (SSL) client certificate information, NULL if the peer is not using TLS (SSL)
257          */
258         static ssl_cert* GetCertificate(StreamSocket* sock)
259         {
260                 SSLIOHook* ssliohook = SSLIOHook::IsSSL(sock);
261                 if (!ssliohook)
262                         return NULL;
263
264                 return ssliohook->GetCertificate();
265         }
266
267         /**
268          * Get the fingerprint of a client certificate from a socket
269          * @param sock The socket to get the certificate fingerprint from, the
270          * socket does not have to use TLS (SSL)
271          * @return The key fingerprint from the TLS (SSL) certificate sent by the peer,
272          * empty if no cert was sent or the peer is not using TLS (SSL)
273          */
274         static std::string GetFingerprint(StreamSocket* sock)
275         {
276                 ssl_cert* cert = SSLClientCert::GetCertificate(sock);
277                 if (cert)
278                         return cert->GetFingerprint();
279                 return "";
280         }
281 };
282
283 class UserCertificateAPIBase : public DataProvider
284 {
285  public:
286         UserCertificateAPIBase(Module* parent)
287                 : DataProvider(parent, "m_sslinfo_api")
288         {
289         }
290
291         /** Get the TLS (SSL) certificate of a user
292          * @param user The user whose certificate to get, user may be remote
293          * @return The TLS (SSL) certificate of the user or NULL if the user is not using TLS (SSL)
294          */
295         virtual ssl_cert* GetCertificate(User* user) = 0;
296
297         /** Set the TLS (SSL) certificate of a user.
298          * @param user The user whose certificate to set.
299          * @param cert The TLS (SSL) certificate to set for the user.
300          */
301         virtual void SetCertificate(User* user, ssl_cert* cert) = 0;
302
303         /** Get the key fingerprint from a user's certificate
304          * @param user The user whose key fingerprint to get, user may be remote
305          * @return The key fingerprint from the user's TLS (SSL) certificate or an empty string
306          * if the user is not using TLS (SSL) or did not provide a client certificate
307          */
308         std::string GetFingerprint(User* user)
309         {
310                 ssl_cert* cert = GetCertificate(user);
311                 if (cert)
312                         return cert->GetFingerprint();
313                 return "";
314         }
315 };
316
317 /** API implemented by m_sslinfo that allows modules to retrieve the TLS (SSL) certificate
318  * information of local and remote users. It can also be used to find out whether a
319  * user is using TLS (SSL) or not.
320  */
321 class UserCertificateAPI : public dynamic_reference<UserCertificateAPIBase>
322 {
323  public:
324         UserCertificateAPI(Module* parent)
325                 : dynamic_reference<UserCertificateAPIBase>(parent, "m_sslinfo_api")
326         {
327         }
328 };