]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sslinfo.cpp
Get rid of the OnRemoteKill hook, make use of GetRouting() and TR_CUSTOM to route...
[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
23 /* $ModDesc: Provides SSL metadata, including /WHOIS information and /SSLINFO command */
24
25 class SSLCertExt : public ExtensionItem {
26  public:
27         SSLCertExt(Module* parent) : ExtensionItem("ssl_cert", parent) {}
28         ssl_cert* get(const Extensible* item) const
29         {
30                 return static_cast<ssl_cert*>(get_raw(item));
31         }
32         void set(Extensible* item, ssl_cert* value)
33         {
34                 value->refcount_inc();
35                 ssl_cert* old = static_cast<ssl_cert*>(set_raw(item, value));
36                 if (old && old->refcount_dec())
37                         delete old;
38         }
39
40         std::string serialize(SerializeFormat format, const Extensible* container, void* item) const
41         {
42                 return static_cast<ssl_cert*>(item)->GetMetaLine();
43         }
44
45         void unserialize(SerializeFormat format, Extensible* container, const std::string& value)
46         {
47                 ssl_cert* cert = new ssl_cert;
48                 set(container, cert);
49
50                 std::stringstream s(value);
51                 std::string v;
52                 getline(s,v,' ');
53
54                 cert->invalid = (v.find('v') != std::string::npos);
55                 cert->trusted = (v.find('T') != std::string::npos);
56                 cert->revoked = (v.find('R') != std::string::npos);
57                 cert->unknownsigner = (v.find('s') != std::string::npos);
58                 if (v.find('E') != std::string::npos)
59                 {
60                         getline(s,cert->error,'\n');
61                 }
62                 else
63                 {
64                         getline(s,cert->fingerprint,' ');
65                         getline(s,cert->dn,' ');
66                         getline(s,cert->issuer,'\n');
67                 }
68         }
69
70         void free(void* item)
71         {
72                 ssl_cert* old = static_cast<ssl_cert*>(item);
73                 if (old && old->refcount_dec())
74                         delete old;
75         }
76 };
77
78 /** Handle /SSLINFO
79  */
80 class CommandSSLInfo : public Command
81 {
82  public:
83         SSLCertExt CertExt;
84
85         CommandSSLInfo(Module* Creator) : Command(Creator, "SSLINFO", 1), CertExt(Creator)
86         {
87                 this->syntax = "<nick>";
88         }
89
90         CmdResult Handle (const std::vector<std::string> &parameters, User *user)
91         {
92                 User* target = ServerInstance->FindNickOnly(parameters[0]);
93
94                 if ((!target) || (target->registered != REG_ALL))
95                 {
96                         user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nickname", user->nick.c_str(), parameters[0].c_str());
97                         return CMD_FAILURE;
98                 }
99                 bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
100                 if (operonlyfp && !user->IsOper() && target != user)
101                 {
102                         user->WriteNotice("*** You cannot view SSL certificate information for other users");
103                         return CMD_FAILURE;
104                 }
105                 ssl_cert* cert = CertExt.get(target);
106                 if (!cert)
107                 {
108                         user->WriteNotice("*** No SSL certificate for this user");
109                 }
110                 else if (cert->GetError().length())
111                 {
112                         user->WriteNotice("*** No SSL certificate information for this user (" + cert->GetError() + ").");
113                 }
114                 else
115                 {
116                         user->WriteNotice("*** Distinguished Name: " + cert->GetDN());
117                         user->WriteNotice("*** Issuer:             " + cert->GetIssuer());
118                         user->WriteNotice("*** Key Fingerprint:    " + cert->GetFingerprint());
119                 }
120                 return CMD_SUCCESS;
121         }
122 };
123
124 class UserCertificateAPIImpl : public UserCertificateAPIBase
125 {
126         SSLCertExt& ext;
127
128  public:
129         UserCertificateAPIImpl(Module* mod, SSLCertExt& certext)
130                 : UserCertificateAPIBase(mod), ext(certext)
131         {
132         }
133
134         ssl_cert* GetCertificate(User* user) CXX11_OVERRIDE
135         {
136                 return ext.get(user);
137         }
138 };
139
140 class ModuleSSLInfo : public Module
141 {
142         CommandSSLInfo cmd;
143         UserCertificateAPIImpl APIImpl;
144
145  public:
146         ModuleSSLInfo()
147                 : cmd(this), APIImpl(this, cmd.CertExt)
148         {
149         }
150
151         void init() CXX11_OVERRIDE
152         {
153                 ServerInstance->Modules->AddService(APIImpl);
154                 ServerInstance->Modules->AddService(cmd);
155                 ServerInstance->Modules->AddService(cmd.CertExt);
156
157                 Implementation eventlist[] = { I_OnWhois, I_OnPreCommand, I_OnSetConnectClass, I_OnUserConnect, I_OnPostConnect };
158                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
159         }
160
161         Version GetVersion() CXX11_OVERRIDE
162         {
163                 return Version("SSL Certificate Utilities", VF_VENDOR);
164         }
165
166         void OnWhois(User* source, User* dest) CXX11_OVERRIDE
167         {
168                 ssl_cert* cert = cmd.CertExt.get(dest);
169                 if (cert)
170                 {
171                         ServerInstance->SendWhoisLine(source, dest, 671, "%s %s :is using a secure connection", source->nick.c_str(), dest->nick.c_str());
172                         bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
173                         if ((!operonlyfp || source == dest || source->IsOper()) && !cert->fingerprint.empty())
174                                 ServerInstance->SendWhoisLine(source, dest, 276, "%s %s :has client certificate fingerprint %s",
175                                         source->nick.c_str(), dest->nick.c_str(), cert->fingerprint.c_str());
176                 }
177         }
178
179         ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line) CXX11_OVERRIDE
180         {
181                 if ((command == "OPER") && (validated))
182                 {
183                         OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
184                         if (i != ServerInstance->Config->oper_blocks.end())
185                         {
186                                 OperInfo* ifo = i->second;
187                                 ssl_cert* cert = cmd.CertExt.get(user);
188
189                                 if (ifo->oper_block->getBool("sslonly") && !cert)
190                                 {
191                                         user->WriteNumeric(491, "%s :This oper login requires an SSL connection.", user->nick.c_str());
192                                         user->CommandFloodPenalty += 10000;
193                                         return MOD_RES_DENY;
194                                 }
195
196                                 std::string fingerprint;
197                                 if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
198                                 {
199                                         user->WriteNumeric(491, "%s :This oper login requires a matching SSL fingerprint.",user->nick.c_str());
200                                         user->CommandFloodPenalty += 10000;
201                                         return MOD_RES_DENY;
202                                 }
203                         }
204                 }
205
206                 // Let core handle it for extra stuff
207                 return MOD_RES_PASSTHRU;
208         }
209
210         void OnUserConnect(LocalUser* user) CXX11_OVERRIDE
211         {
212                 ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
213                 if (cert)
214                         cmd.CertExt.set(user, cert);
215         }
216
217         void OnPostConnect(User* user) CXX11_OVERRIDE
218         {
219                 ssl_cert *cert = cmd.CertExt.get(user);
220                 if (!cert || cert->fingerprint.empty())
221                         return;
222                 // find an auto-oper block for this user
223                 for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
224                 {
225                         OperInfo* ifo = i->second;
226                         std::string fp = ifo->oper_block->getString("fingerprint");
227                         if (fp == cert->fingerprint && ifo->oper_block->getBool("autologin"))
228                                 user->Oper(ifo);
229                 }
230         }
231
232         ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass) CXX11_OVERRIDE
233         {
234                 ssl_cert* cert = SSLClientCert::GetCertificate(&user->eh);
235                 bool ok = true;
236                 if (myclass->config->getString("requiressl") == "trusted")
237                 {
238                         ok = (cert && cert->IsCAVerified());
239                 }
240                 else if (myclass->config->getBool("requiressl"))
241                 {
242                         ok = (cert != NULL);
243                 }
244
245                 if (!ok)
246                         return MOD_RES_DENY;
247                 return MOD_RES_PASSTHRU;
248         }
249 };
250
251 MODULE_INIT(ModuleSSLInfo)