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