]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sslinfo.cpp
WebSocket: send messages as text by default.
[user/henk/code/inspircd.git] / src / modules / m_sslinfo.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
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/ssl.h"
22 #include "modules/whois.h"
23
24 enum
25 {
26         // From oftc-hybrid.
27         RPL_WHOISCERTFP = 276,
28
29         // From UnrealIRCd.
30         RPL_WHOISSECURE = 671
31 };
32
33 class SSLCertExt : public ExtensionItem {
34  public:
35         SSLCertExt(Module* parent)
36                 : ExtensionItem("ssl_cert", ExtensionItem::EXT_USER, parent)
37         {
38         }
39
40         ssl_cert* get(const Extensible* item) const
41         {
42                 return static_cast<ssl_cert*>(get_raw(item));
43         }
44         void set(Extensible* item, ssl_cert* value)
45         {
46                 value->refcount_inc();
47                 ssl_cert* old = static_cast<ssl_cert*>(set_raw(item, value));
48                 if (old && old->refcount_dec())
49                         delete old;
50         }
51
52         std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
53         {
54                 return static_cast<ssl_cert*>(item)->GetMetaLine();
55         }
56
57         void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
58         {
59                 ssl_cert* cert = new ssl_cert;
60                 set(container, cert);
61
62                 std::stringstream s(value);
63                 std::string v;
64                 getline(s,v,' ');
65
66                 cert->invalid = (v.find('v') != std::string::npos);
67                 cert->trusted = (v.find('T') != std::string::npos);
68                 cert->revoked = (v.find('R') != std::string::npos);
69                 cert->unknownsigner = (v.find('s') != std::string::npos);
70                 if (v.find('E') != std::string::npos)
71                 {
72                         getline(s,cert->error,'\n');
73                 }
74                 else
75                 {
76                         getline(s,cert->fingerprint,' ');
77                         getline(s,cert->dn,' ');
78                         getline(s,cert->issuer,'\n');
79                 }
80         }
81
82         void free(Extensible* container, void* item) CXX11_OVERRIDE
83         {
84                 ssl_cert* old = static_cast<ssl_cert*>(item);
85                 if (old && old->refcount_dec())
86                         delete old;
87         }
88 };
89
90 /** Handle /SSLINFO
91  */
92 class CommandSSLInfo : public Command
93 {
94  public:
95         SSLCertExt CertExt;
96
97         CommandSSLInfo(Module* Creator) : Command(Creator, "SSLINFO", 1), CertExt(Creator)
98         {
99                 this->syntax = "<nick>";
100         }
101
102         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
103         {
104                 User* target = ServerInstance->FindNickOnly(parameters[0]);
105
106                 if ((!target) || (target->registered != REG_ALL))
107                 {
108                         user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
109                         return CMD_FAILURE;
110                 }
111                 bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
112                 if (operonlyfp && !user->IsOper() && target != user)
113                 {
114                         user->WriteNotice("*** You cannot view SSL certificate information for other users");
115                         return CMD_FAILURE;
116                 }
117                 ssl_cert* cert = CertExt.get(target);
118                 if (!cert)
119                 {
120                         user->WriteNotice("*** No SSL certificate for this user");
121                 }
122                 else if (cert->GetError().length())
123                 {
124                         user->WriteNotice("*** No SSL certificate information for this user (" + cert->GetError() + ").");
125                 }
126                 else
127                 {
128                         user->WriteNotice("*** Distinguished Name: " + cert->GetDN());
129                         user->WriteNotice("*** Issuer:             " + cert->GetIssuer());
130                         user->WriteNotice("*** Key Fingerprint:    " + cert->GetFingerprint());
131                 }
132                 return CMD_SUCCESS;
133         }
134 };
135
136 class UserCertificateAPIImpl : public UserCertificateAPIBase
137 {
138         SSLCertExt& ext;
139
140  public:
141         UserCertificateAPIImpl(Module* mod, SSLCertExt& certext)
142                 : UserCertificateAPIBase(mod), ext(certext)
143         {
144         }
145
146         ssl_cert* GetCertificate(User* user) CXX11_OVERRIDE
147         {
148                 return ext.get(user);
149         }
150
151         void SetCertificate(User* user, ssl_cert* cert) CXX11_OVERRIDE
152         {
153                 ext.set(user, cert);
154         }
155 };
156
157 class ModuleSSLInfo : public Module, public Whois::EventListener
158 {
159         CommandSSLInfo cmd;
160         UserCertificateAPIImpl APIImpl;
161
162  public:
163         ModuleSSLInfo()
164                 : Whois::EventListener(this)
165                 , cmd(this)
166                 , APIImpl(this, cmd.CertExt)
167         {
168         }
169
170         Version GetVersion() CXX11_OVERRIDE
171         {
172                 return Version("SSL Certificate Utilities", VF_VENDOR);
173         }
174
175         void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
176         {
177                 ssl_cert* cert = cmd.CertExt.get(whois.GetTarget());
178                 if (cert)
179                 {
180                         whois.SendLine(RPL_WHOISSECURE, "is using a secure connection");
181                         bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
182                         if ((!operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper()) && !cert->fingerprint.empty())
183                                 whois.SendLine(RPL_WHOISCERTFP, InspIRCd::Format("has client certificate fingerprint %s", cert->fingerprint.c_str()));
184                 }
185         }
186
187         ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) CXX11_OVERRIDE
188         {
189                 if ((command == "OPER") && (validated))
190                 {
191                         ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
192                         if (i != ServerInstance->Config->oper_blocks.end())
193                         {
194                                 OperInfo* ifo = i->second;
195                                 ssl_cert* cert = cmd.CertExt.get(user);
196
197                                 if (ifo->oper_block->getBool("sslonly") && !cert)
198                                 {
199                                         user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires an SSL connection.");
200                                         user->CommandFloodPenalty += 10000;
201                                         return MOD_RES_DENY;
202                                 }
203
204                                 std::string fingerprint;
205                                 if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
206                                 {
207                                         user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires a matching SSL certificate fingerprint.");
208                                         user->CommandFloodPenalty += 10000;
209                                         return MOD_RES_DENY;
210                                 }
211                         }
212                 }
213
214                 // Let core handle it for extra stuff
215                 return MOD_RES_PASSTHRU;
216         }
217
218         void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
219         {
220                 ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
221                 if (cert)
222                         cmd.CertExt.set(user, cert);
223         }
224
225         void OnPostConnect(User* user) CXX11_OVERRIDE
226         {
227                 LocalUser* const localuser = IS_LOCAL(user);
228                 if (!localuser)
229                         return;
230
231                 const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(&localuser->eh);
232                 if (!ssliohook)
233                         return;
234
235                 ssl_cert* const cert = ssliohook->GetCertificate();
236
237                 {
238                         std::string text = "*** You are connected to ";
239                         if (!ssliohook->GetServerName(text))
240                                 text.append(ServerInstance->Config->ServerName);
241                         text.append(" using SSL cipher '");
242                         ssliohook->GetCiphersuite(text);
243                         text.push_back('\'');
244                         if ((cert) && (!cert->GetFingerprint().empty()))
245                                 text.append(" and your SSL certificate fingerprint is ").append(cert->GetFingerprint());
246                         user->WriteNotice(text);
247                 }
248
249                 if (!cert)
250                         return;
251                 // find an auto-oper block for this user
252                 for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
253                 {
254                         OperInfo* ifo = i->second;
255                         std::string fp = ifo->oper_block->getString("fingerprint");
256                         if (fp == cert->fingerprint && ifo->oper_block->getBool("autologin"))
257                                 user->Oper(ifo);
258                 }
259         }
260
261         ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
262         {
263                 ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
264                 bool ok = true;
265                 if (myclass->config->getString("requiressl") == "trusted")
266                 {
267                         ok = (cert && cert->IsCAVerified());
268                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires a trusted SSL cert. Client %s one.", (ok ? "has" : "does not have"));
269                 }
270                 else if (myclass->config->getBool("requiressl"))
271                 {
272                         ok = (cert != NULL);
273                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires any SSL cert. Client %s one.", (ok ? "has" : "does not have"));
274                 }
275
276                 if (!ok)
277                         return MOD_RES_DENY;
278                 return MOD_RES_PASSTHRU;
279         }
280 };
281
282 MODULE_INIT(ModuleSSLInfo)