]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sslinfo.cpp
85e7b77dd3f560ddbefdd02d3f232cf0e6a47731
[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(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(const std::vector<std::string>& parameters, User* user) 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
152 class ModuleSSLInfo : public Module, public Whois::EventListener
153 {
154         CommandSSLInfo cmd;
155         UserCertificateAPIImpl APIImpl;
156
157  public:
158         ModuleSSLInfo()
159                 : Whois::EventListener(this)
160                 , cmd(this)
161                 , APIImpl(this, cmd.CertExt)
162         {
163         }
164
165         Version GetVersion() CXX11_OVERRIDE
166         {
167                 return Version("SSL Certificate Utilities", VF_VENDOR);
168         }
169
170         void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
171         {
172                 ssl_cert* cert = cmd.CertExt.get(whois.GetTarget());
173                 if (cert)
174                 {
175                         whois.SendLine(RPL_WHOISSECURE, "is using a secure connection");
176                         bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
177                         if ((!operonlyfp || whois.IsSelfWhois() || whois.GetSource()->IsOper()) && !cert->fingerprint.empty())
178                                 whois.SendLine(RPL_WHOISCERTFP, InspIRCd::Format("has client certificate fingerprint %s", cert->fingerprint.c_str()));
179                 }
180         }
181
182         ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
183         {
184                 if ((command == "OPER") && (validated))
185                 {
186                         ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
187                         if (i != ServerInstance->Config->oper_blocks.end())
188                         {
189                                 OperInfo* ifo = i->second;
190                                 ssl_cert* cert = cmd.CertExt.get(user);
191
192                                 if (ifo->oper_block->getBool("sslonly") && !cert)
193                                 {
194                                         user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires an SSL connection.");
195                                         user->CommandFloodPenalty += 10000;
196                                         return MOD_RES_DENY;
197                                 }
198
199                                 std::string fingerprint;
200                                 if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
201                                 {
202                                         user->WriteNumeric(ERR_NOOPERHOST, "This oper login requires a matching SSL certificate fingerprint.");
203                                         user->CommandFloodPenalty += 10000;
204                                         return MOD_RES_DENY;
205                                 }
206                         }
207                 }
208
209                 // Let core handle it for extra stuff
210                 return MOD_RES_PASSTHRU;
211         }
212
213         void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
214         {
215                 ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
216                 if (cert)
217                         cmd.CertExt.set(user, cert);
218         }
219
220         void OnPostConnect(User* user) CXX11_OVERRIDE
221         {
222                 LocalUser* const localuser = IS_LOCAL(user);
223                 if (!localuser)
224                         return;
225
226                 const SSLIOHook* const ssliohook = SSLIOHook::IsSSL(&localuser->eh);
227                 if (!ssliohook)
228                         return;
229
230                 ssl_cert* const cert = ssliohook->GetCertificate();
231
232                 {
233                         std::string text = "*** You are connected to ";
234                         if (!ssliohook->GetServerName(text))
235                                 text.append(ServerInstance->Config->ServerName);
236                         text.append(" using SSL cipher '");
237                         ssliohook->GetCiphersuite(text);
238                         text.push_back('\'');
239                         if ((cert) && (!cert->GetFingerprint().empty()))
240                                 text.append(" and your SSL certificate fingerprint is ").append(cert->GetFingerprint());
241                         user->WriteNotice(text);
242                 }
243
244                 if (!cert)
245                         return;
246                 // find an auto-oper block for this user
247                 for (ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); ++i)
248                 {
249                         OperInfo* ifo = i->second;
250                         std::string fp = ifo->oper_block->getString("fingerprint");
251                         if (fp == cert->fingerprint && ifo->oper_block->getBool("autologin"))
252                                 user->Oper(ifo);
253                 }
254         }
255
256         ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
257         {
258                 ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
259                 bool ok = true;
260                 if (myclass->config->getString("requiressl") == "trusted")
261                 {
262                         ok = (cert && cert->IsCAVerified());
263                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires a trusted SSL cert. Client %s one.", (ok ? "has" : "does not have"));
264                 }
265                 else if (myclass->config->getBool("requiressl"))
266                 {
267                         ok = (cert != NULL);
268                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class requires any SSL cert. Client %s one.", (ok ? "has" : "does not have"));
269                 }
270
271                 if (!ok)
272                         return MOD_RES_DENY;
273                 return MOD_RES_PASSTHRU;
274         }
275 };
276
277 MODULE_INIT(ModuleSSLInfo)