]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sslinfo.cpp
Tidy up source files:
[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->WriteServ("NOTICE %s :*** You cannot view SSL certificate information for other users", user->nick.c_str());
103                         return CMD_FAILURE;
104                 }
105                 ssl_cert* cert = CertExt.get(target);
106                 if (!cert)
107                 {
108                         user->WriteServ("NOTICE %s :*** No SSL certificate for this user", user->nick.c_str());
109                 }
110                 else if (cert->GetError().length())
111                 {
112                         user->WriteServ("NOTICE %s :*** No SSL certificate information for this user (%s).", user->nick.c_str(), cert->GetError().c_str());
113                 }
114                 else
115                 {
116                         user->WriteServ("NOTICE %s :*** Distinguished Name: %s", user->nick.c_str(), cert->GetDN().c_str());
117                         user->WriteServ("NOTICE %s :*** Issuer:             %s", user->nick.c_str(), cert->GetIssuer().c_str());
118                         user->WriteServ("NOTICE %s :*** Key Fingerprint:    %s", user->nick.c_str(), cert->GetFingerprint().c_str());
119                 }
120                 return CMD_SUCCESS;
121         }
122 };
123
124 class ModuleSSLInfo : public Module
125 {
126         CommandSSLInfo cmd;
127
128  public:
129         ModuleSSLInfo() : cmd(this)
130         {
131         }
132
133         void init()
134         {
135                 ServerInstance->Modules->AddService(cmd);
136
137                 ServerInstance->Modules->AddService(cmd.CertExt);
138
139                 Implementation eventlist[] = { I_OnWhois, I_OnPreCommand, I_OnSetConnectClass, I_OnUserConnect, I_OnPostConnect };
140                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
141         }
142
143         Version GetVersion()
144         {
145                 return Version("SSL Certificate Utilities", VF_VENDOR);
146         }
147
148         void OnWhois(User* source, User* dest)
149         {
150                 ssl_cert* cert = cmd.CertExt.get(dest);
151                 if (cert)
152                 {
153                         ServerInstance->SendWhoisLine(source, dest, 671, "%s %s :is using a secure connection", source->nick.c_str(), dest->nick.c_str());
154                         bool operonlyfp = ServerInstance->Config->ConfValue("sslinfo")->getBool("operonly");
155                         if ((!operonlyfp || source == dest || source->IsOper()) && !cert->fingerprint.empty())
156                                 ServerInstance->SendWhoisLine(source, dest, 276, "%s %s :has client certificate fingerprint %s",
157                                         source->nick.c_str(), dest->nick.c_str(), cert->fingerprint.c_str());
158                 }
159         }
160
161         bool OneOfMatches(const char* host, const char* ip, const char* hostlist)
162         {
163                 std::stringstream hl(hostlist);
164                 std::string xhost;
165                 while (hl >> xhost)
166                 {
167                         if (InspIRCd::Match(host, xhost, ascii_case_insensitive_map) || InspIRCd::MatchCIDR(ip, xhost, ascii_case_insensitive_map))
168                         {
169                                 return true;
170                         }
171                 }
172                 return false;
173         }
174
175         ModResult OnPreCommand(std::string &command, std::vector<std::string> &parameters, LocalUser *user, bool validated, const std::string &original_line)
176         {
177                 if ((command == "OPER") && (validated))
178                 {
179                         OperIndex::iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]);
180                         if (i != ServerInstance->Config->oper_blocks.end())
181                         {
182                                 OperInfo* ifo = i->second;
183                                 ssl_cert* cert = cmd.CertExt.get(user);
184
185                                 if (ifo->oper_block->getBool("sslonly") && !cert)
186                                 {
187                                         user->WriteNumeric(491, "%s :This oper login requires an SSL connection.", user->nick.c_str());
188                                         user->CommandFloodPenalty += 10000;
189                                         return MOD_RES_DENY;
190                                 }
191
192                                 std::string fingerprint;
193                                 if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || cert->GetFingerprint() != fingerprint))
194                                 {
195                                         user->WriteNumeric(491, "%s :This oper login requires a matching SSL fingerprint.",user->nick.c_str());
196                                         user->CommandFloodPenalty += 10000;
197                                         return MOD_RES_DENY;
198                                 }
199                         }
200                 }
201
202                 // Let core handle it for extra stuff
203                 return MOD_RES_PASSTHRU;
204         }
205
206         void OnUserConnect(LocalUser* user)
207         {
208                 SocketCertificateRequest req(&user->eh, this);
209                 if (!req.cert)
210                         return;
211                 cmd.CertExt.set(user, req.cert);
212         }
213
214         void OnPostConnect(User* user)
215         {
216                 ssl_cert *cert = cmd.CertExt.get(user);
217                 if (!cert || cert->fingerprint.empty())
218                         return;
219                 // find an auto-oper block for this user
220                 for(OperIndex::iterator i = ServerInstance->Config->oper_blocks.begin(); i != ServerInstance->Config->oper_blocks.end(); i++)
221                 {
222                         OperInfo* ifo = i->second;
223                         std::string fp = ifo->oper_block->getString("fingerprint");
224                         if (fp == cert->fingerprint && ifo->oper_block->getBool("autologin"))
225                                 user->Oper(ifo);
226                 }
227         }
228
229         ModResult OnSetConnectClass(LocalUser* user, ConnectClass* myclass)
230         {
231                 SocketCertificateRequest req(&user->eh, this);
232                 bool ok = true;
233                 if (myclass->config->getString("requiressl") == "trusted")
234                 {
235                         ok = (req.cert && req.cert->IsCAVerified());
236                 }
237                 else if (myclass->config->getBool("requiressl"))
238                 {
239                         ok = (req.cert != NULL);
240                 }
241
242                 if (!ok)
243                         return MOD_RES_DENY;
244                 return MOD_RES_PASSTHRU;
245         }
246
247         void OnRequest(Request& request)
248         {
249                 if (strcmp("GET_USER_CERT", request.id) == 0)
250                 {
251                         UserCertificateRequest& req = static_cast<UserCertificateRequest&>(request);
252                         req.cert = cmd.CertExt.get(req.user);
253                 }
254         }
255 };
256
257 MODULE_INIT(ModuleSSLInfo)