2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
5 * Copyright (C) 2017 Peter Powell <petpow@saberuk.com>
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.
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
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/>.
22 #include "modules/cap.h"
23 #include "modules/ssl.h"
25 class STSCap : public Cap::Capability
29 std::string plaintextpolicy;
30 std::string securepolicy;
32 bool OnList(LocalUser* user) CXX11_OVERRIDE
34 // Don't send the cap to clients that only support cap-3.1.
35 if (GetProtocol(user) == Cap::CAP_LEGACY)
38 // Don't send the cap to clients in a class which has STS disabled.
39 if (user->GetClass()->config->getBool("usests", true))
42 // Plaintext listeners have their own policy.
43 SSLIOHook* sslhook = SSLIOHook::IsSSL(&user->eh);
47 // If no hostname has been provided for the connection, an STS persistence policy SHOULD NOT be advertised.
49 if (!sslhook->GetServerName(snihost))
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);
58 bool OnRequest(LocalUser* user, bool adding) CXX11_OVERRIDE
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.
65 const std::string* GetValue(LocalUser* user) const CXX11_OVERRIDE
67 return SSLIOHook::IsSSL(&user->eh) ? &securepolicy : &plaintextpolicy;
72 : Cap::Capability(mod, "sts")
78 // TODO: Send duration=0 when STS vanishes.
81 void SetPolicy(const std::string& newhost, unsigned long duration, unsigned int port, bool preload)
83 // To enforce an STS upgrade policy, servers MUST send this key to insecurely connected clients. Servers
84 // MAY send this key to securely connected clients, but it will be ignored.
85 std::string newplaintextpolicy("port=");
86 newplaintextpolicy.append(ConvToStr(port));
88 // To enforce an STS persistence policy, servers MUST send this key to securely connected clients. Servers
89 // MAY send this key to all clients, but insecurely connected clients MUST ignore it.
90 std::string newsecurepolicy("duration=");
91 newsecurepolicy.append(ConvToStr(duration));
93 // Servers MAY send this key to all clients, but insecurely connected clients MUST ignore it.
95 newsecurepolicy.append(",preload");
97 // Apply the new policy.
99 if (!irc::equals(host, newhost))
101 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing STS SNI hostname from \"%s\" to \"%s\"", host.c_str(), newhost.c_str());
106 if (plaintextpolicy != newplaintextpolicy)
108 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing plaintext STS policy from \"%s\" to \"%s\"", plaintextpolicy.c_str(), newplaintextpolicy.c_str());
109 plaintextpolicy.swap(newplaintextpolicy);
113 if (securepolicy != newsecurepolicy)
115 ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Changing secure STS policy from \"%s\" to \"%s\"", securepolicy.c_str(), newsecurepolicy.c_str());
116 securepolicy.swap(newsecurepolicy);
120 // If the policy has changed then notify all clients via cap-notify.
126 class ModuleIRCv3STS : public Module
131 // The IRCv3 STS specification requires that the server is listening using SSL using a valid certificate.
132 bool HasValidSSLPort(unsigned int port)
134 for (std::vector<ListenSocket*>::const_iterator iter = ServerInstance->ports.begin(); iter != ServerInstance->ports.end(); ++iter)
136 ListenSocket* ls = *iter;
138 // Is this listener on the right port?
139 unsigned int saport = ls->bind_sa.port();
143 // Is this listener using SSL?
144 if (ls->bind_tag->getString("ssl").empty())
147 // TODO: Add a way to check if a listener's TLS cert is CA-verified.
159 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
161 // TODO: Multiple SNI profiles
162 ConfigTag* tag = ServerInstance->Config->ConfValue("sts");
163 if (tag == ServerInstance->Config->EmptyTag)
164 throw ModuleException("You must define a STS policy!");
166 const std::string host = tag->getString("host");
168 throw ModuleException("<sts:host> must contain a hostname, at " + tag->getTagLocation());
170 unsigned int port = tag->getUInt("port", 0, 0, UINT16_MAX);
171 if (!HasValidSSLPort(port))
172 throw ModuleException("<sts:port> must be a TLS port, at " + tag->getTagLocation());
174 unsigned long duration = tag->getDuration("duration", 60*60*24*30*2);
175 bool preload = tag->getBool("preload");
176 cap.SetPolicy(host, duration, port, preload);
179 Version GetVersion() CXX11_OVERRIDE
181 return Version("Provides IRCv3 Strict Transport Security policy advertisement", VF_OPTCOMMON|VF_VENDOR);
185 MODULE_INIT(ModuleIRCv3STS)