]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ircv3_sts.cpp
Rename `<bind:ssl>` to `<bind:sslprofile>`.
[user/henk/code/inspircd.git] / src / modules / m_ircv3_sts.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2020 Matt Schatz <genius3000@g3k.solutions>
5  *   Copyright (C) 2017-2020 Sadie Powell <sadie@witchery.services>
6  *
7  * This file is part of InspIRCd.  InspIRCd is free software: you can
8  * redistribute it and/or modify it under the terms of the GNU General Public
9  * License as published by the Free Software Foundation, version 2.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20
21 #include "inspircd.h"
22 #include "modules/cap.h"
23 #include "modules/ssl.h"
24
25 class STSCap : public Cap::Capability
26 {
27  private:
28         std::string host;
29         std::string plaintextpolicy;
30         std::string securepolicy;
31
32         bool OnList(LocalUser* user) CXX11_OVERRIDE
33         {
34                 // Don't send the cap to clients that only support cap-3.1.
35                 if (GetProtocol(user) == Cap::CAP_LEGACY)
36                         return false;
37
38                 // Don't send the cap to clients in a class which has STS disabled.
39                 if (!user->GetClass()->config->getBool("usests", true))
40                         return false;
41
42                 // Plaintext listeners have their own policy.
43                 SSLIOHook* sslhook = SSLIOHook::IsSSL(&user->eh);
44                 if (!sslhook)
45                         return true;
46
47                 // If no hostname has been provided for the connection, an STS persistence policy SHOULD NOT be advertised.
48                 std::string snihost;
49                 if (!sslhook->GetServerName(snihost))
50                         return false;
51
52                 // Before advertising an STS persistence policy over a secure connection, servers SHOULD verify whether the
53                 // hostname provided by clients, for example, via TLS Server Name Indication (SNI), has been whitelisted by
54                 // administrators in the server configuration.
55                 return InspIRCd::Match(snihost, host, ascii_case_insensitive_map);
56         }
57
58         bool OnRequest(LocalUser* user, bool adding) CXX11_OVERRIDE
59         {
60                 // Clients MUST NOT request this capability with CAP REQ. Servers MAY reply with a CAP NAK message if a
61                 // client requests this capability.
62                 return false;
63         }
64
65         const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
66         {
67                 return SSLIOHook::IsSSL(&user->eh) ? &securepolicy : &plaintextpolicy;
68         }
69
70  public:
71         STSCap(Module* mod)
72                 : Cap::Capability(mod, "sts")
73         {
74                 DisableAutoRegister();
75         }
76
77         ~STSCap()
78         {
79                 // TODO: Send duration=0 when STS vanishes.
80         }
81
82         void SetPolicy(const std::string& newhost, unsigned long duration, unsigned int port, bool preload)
83         {
84                 // To enforce an STS upgrade policy, servers MUST send this key to insecurely connected clients. Servers
85                 // MAY send this key to securely connected clients, but it will be ignored.
86                 std::string newplaintextpolicy("port=");
87                 newplaintextpolicy.append(ConvToStr(port));
88
89                 // To enforce an STS persistence policy, servers MUST send this key to securely connected clients. Servers
90                 // MAY send this key to all clients, but insecurely connected clients MUST ignore it.
91                 std::string newsecurepolicy("duration=");
92                 newsecurepolicy.append(ConvToStr(duration));
93
94                 // Servers MAY send this key to all clients, but insecurely connected clients MUST ignore it.
95                 if (preload)
96                         newsecurepolicy.append(",preload");
97
98                 // Apply the new policy.
99                 bool changed = false;
100                 if (!irc::equals(host, newhost))
101                 {
102                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing STS SNI hostname from \"%s\" to \"%s\"", host.c_str(), newhost.c_str());
103                         host = newhost;
104                         changed = true;
105                 }
106
107                 if (plaintextpolicy != newplaintextpolicy)
108                 {
109                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing plaintext STS policy from \"%s\" to \"%s\"", plaintextpolicy.c_str(), newplaintextpolicy.c_str());
110                         plaintextpolicy.swap(newplaintextpolicy);
111                         changed = true;
112                 }
113
114                 if (securepolicy != newsecurepolicy)
115                 {
116                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing secure STS policy from \"%s\" to \"%s\"", securepolicy.c_str(), newsecurepolicy.c_str());
117                         securepolicy.swap(newsecurepolicy);
118                         changed = true;
119                 }
120
121                 // If the policy has changed then notify all clients via cap-notify.
122                 if (changed)
123                         NotifyValueChange();
124         }
125 };
126
127 class ModuleIRCv3STS : public Module
128 {
129  private:
130         STSCap cap;
131
132         // The IRCv3 STS specification requires that the server is listening using TLS (SSL) using a valid certificate.
133         bool HasValidSSLPort(unsigned int port)
134         {
135                 for (std::vector<ListenSocket*>::const_iterator iter = ServerInstance->ports.begin(); iter != ServerInstance->ports.end(); ++iter)
136                 {
137                         ListenSocket* ls = *iter;
138
139                         // Is this listener on the right port?
140                         unsigned int saport = ls->bind_sa.port();
141                         if (saport != port)
142                                 continue;
143
144                         // Is this listener using TLS (SSL)?
145                         if (ls->bind_tag->getString("sslprofile", ls->bind_tag->getString("ssl")).empty())
146                                 continue;
147
148                         // TODO: Add a way to check if a listener's TLS cert is CA-verified.
149                         return true;
150                 }
151                 return false;
152         }
153
154  public:
155         ModuleIRCv3STS()
156                 : cap(this)
157         {
158         }
159
160         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
161         {
162                 // TODO: Multiple SNI profiles
163                 ConfigTag* tag = ServerInstance->Config->ConfValue("sts");
164                 if (tag == ServerInstance->Config->EmptyTag)
165                         throw ModuleException("You must define a STS policy!");
166
167                 const std::string host = tag->getString("host");
168                 if (host.empty())
169                         throw ModuleException("<sts:host> must contain a hostname, at " + tag->getTagLocation());
170
171                 unsigned int port = tag->getUInt("port", 0, 0, UINT16_MAX);
172                 if (!HasValidSSLPort(port))
173                         throw ModuleException("<sts:port> must be a TLS port, at " + tag->getTagLocation());
174
175                 unsigned long duration = tag->getDuration("duration", 5*60, 60);
176                 bool preload = tag->getBool("preload");
177                 cap.SetPolicy(host, duration, port, preload);
178
179                 if (!cap.IsRegistered())
180                         ServerInstance->Modules->AddService(cap);
181         }
182
183         Version GetVersion() CXX11_OVERRIDE
184         {
185                 return Version("Adds support for the IRCv3 Strict Transport Security specification.", VF_OPTCOMMON|VF_VENDOR);
186         }
187 };
188
189 MODULE_INIT(ModuleIRCv3STS)