]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sasl.cpp
Merge branch 'master+sslcleanup'
[user/henk/code/inspircd.git] / src / modules / m_sasl.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
6  *
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.
10  *
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
14  * details.
15  *
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/>.
18  */
19
20
21 #include "inspircd.h"
22 #include "modules/cap.h"
23 #include "modules/account.h"
24 #include "modules/sasl.h"
25 #include "modules/ssl.h"
26
27 enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
28 enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
29
30 static std::string sasl_target = "*";
31 static Events::ModuleEventProvider* saslevprov;
32
33 static void SendSASL(const parameterlist& params)
34 {
35         if (!ServerInstance->PI->SendEncapsulatedData(sasl_target, "SASL", params))
36         {
37                 FOREACH_MOD_CUSTOM(*saslevprov, SASLEventListener, OnSASLAuth, (params));
38         }
39 }
40
41 /**
42  * Tracks SASL authentication state like charybdis does. --nenolod
43  */
44 class SaslAuthenticator
45 {
46  private:
47         std::string agent;
48         User *user;
49         SaslState state;
50         SaslResult result;
51         bool state_announced;
52
53  public:
54         SaslAuthenticator(User* user_, const std::string& method)
55                 : user(user_), state(SASL_INIT), state_announced(false)
56         {
57                 parameterlist params;
58                 params.push_back(user->uuid);
59                 params.push_back("*");
60                 params.push_back("S");
61                 params.push_back(method);
62
63                 LocalUser* localuser = IS_LOCAL(user);
64                 if (method == "EXTERNAL" && localuser)
65                 {
66                         std::string fp = SSLClientCert::GetFingerprint(&localuser->eh);
67
68                         if (fp.size())
69                                 params.push_back(fp);
70                 }
71
72                 SendSASL(params);
73         }
74
75         SaslResult GetSaslResult(const std::string &result_)
76         {
77                 if (result_ == "F")
78                         return SASL_FAIL;
79
80                 if (result_ == "A")
81                         return SASL_ABORT;
82
83                 return SASL_OK;
84         }
85
86         /* checks for and deals with a state change. */
87         SaslState ProcessInboundMessage(const std::vector<std::string> &msg)
88         {
89                 switch (this->state)
90                 {
91                  case SASL_INIT:
92                         this->agent = msg[0];
93                         this->state = SASL_COMM;
94                         /* fall through */
95                  case SASL_COMM:
96                         if (msg[0] != this->agent)
97                                 return this->state;
98
99                         if (msg[2] == "C")
100                                 this->user->Write("AUTHENTICATE %s", msg[3].c_str());
101                         else if (msg[2] == "D")
102                         {
103                                 this->state = SASL_DONE;
104                                 this->result = this->GetSaslResult(msg[3]);
105                         }
106                         else if (msg[2] == "M")
107                                 this->user->WriteNumeric(908, "%s %s :are available SASL mechanisms", this->user->nick.c_str(), msg[3].c_str());
108                         else
109                                 ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Services sent an unknown SASL message \"%s\" \"%s\"", msg[2].c_str(), msg[3].c_str());
110
111                         break;
112                  case SASL_DONE:
113                         break;
114                  default:
115                         ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
116                         break;
117                 }
118
119                 return this->state;
120         }
121
122         void Abort(void)
123         {
124                 this->state = SASL_DONE;
125                 this->result = SASL_ABORT;
126         }
127
128         bool SendClientMessage(const std::vector<std::string>& parameters)
129         {
130                 if (this->state != SASL_COMM)
131                         return true;
132
133                 parameterlist params;
134                 params.push_back(this->user->uuid);
135                 params.push_back(this->agent);
136                 params.push_back("C");
137
138                 params.insert(params.end(), parameters.begin(), parameters.end());
139
140                 SendSASL(params);
141
142                 if (parameters[0][0] == '*')
143                 {
144                         this->Abort();
145                         return false;
146                 }
147
148                 return true;
149         }
150
151         void AnnounceState(void)
152         {
153                 if (this->state_announced)
154                         return;
155
156                 switch (this->result)
157                 {
158                  case SASL_OK:
159                         this->user->WriteNumeric(903, ":SASL authentication successful");
160                         break;
161                  case SASL_ABORT:
162                         this->user->WriteNumeric(906, ":SASL authentication aborted");
163                         break;
164                  case SASL_FAIL:
165                         this->user->WriteNumeric(904, ":SASL authentication failed");
166                         break;
167                  default:
168                         break;
169                 }
170
171                 this->state_announced = true;
172         }
173 };
174
175 class CommandAuthenticate : public Command
176 {
177  public:
178         SimpleExtItem<SaslAuthenticator>& authExt;
179         GenericCap& cap;
180         CommandAuthenticate(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext, GenericCap& Cap)
181                 : Command(Creator, "AUTHENTICATE", 1), authExt(ext), cap(Cap)
182         {
183                 works_before_reg = true;
184         }
185
186         CmdResult Handle (const std::vector<std::string>& parameters, User *user)
187         {
188                 /* Only allow AUTHENTICATE on unregistered clients */
189                 if (user->registered != REG_ALL)
190                 {
191                         if (!cap.ext.get(user))
192                                 return CMD_FAILURE;
193
194                         SaslAuthenticator *sasl = authExt.get(user);
195                         if (!sasl)
196                                 authExt.set(user, new SaslAuthenticator(user, parameters[0]));
197                         else if (sasl->SendClientMessage(parameters) == false)  // IAL abort extension --nenolod
198                         {
199                                 sasl->AnnounceState();
200                                 authExt.unset(user);
201                         }
202                 }
203                 return CMD_FAILURE;
204         }
205 };
206
207 class CommandSASL : public Command
208 {
209  public:
210         SimpleExtItem<SaslAuthenticator>& authExt;
211         CommandSASL(Module* Creator, SimpleExtItem<SaslAuthenticator>& ext) : Command(Creator, "SASL", 2), authExt(ext)
212         {
213                 this->flags_needed = FLAG_SERVERONLY; // should not be called by users
214         }
215
216         CmdResult Handle(const std::vector<std::string>& parameters, User *user)
217         {
218                 User* target = ServerInstance->FindNick(parameters[1]);
219                 if ((!target) || (IS_SERVER(target)))
220                 {
221                         ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User not found in sasl ENCAP event: %s", parameters[1].c_str());
222                         return CMD_FAILURE;
223                 }
224
225                 SaslAuthenticator *sasl = authExt.get(target);
226                 if (!sasl)
227                         return CMD_FAILURE;
228
229                 SaslState state = sasl->ProcessInboundMessage(parameters);
230                 if (state == SASL_DONE)
231                 {
232                         sasl->AnnounceState();
233                         authExt.unset(target);
234                 }
235                 return CMD_SUCCESS;
236         }
237
238         RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
239         {
240                 return ROUTE_BROADCAST;
241         }
242 };
243
244 class ModuleSASL : public Module
245 {
246         SimpleExtItem<SaslAuthenticator> authExt;
247         GenericCap cap;
248         CommandAuthenticate auth;
249         CommandSASL sasl;
250         Events::ModuleEventProvider sasleventprov;
251
252  public:
253         ModuleSASL()
254                 : authExt("sasl_auth", ExtensionItem::EXT_USER, this)
255                 , cap(this, "sasl")
256                 , auth(this, authExt, cap)
257                 , sasl(this, authExt)
258                 , sasleventprov(this, "event/sasl")
259         {
260                 saslevprov = &sasleventprov;
261         }
262
263         void init() CXX11_OVERRIDE
264         {
265                 if (!ServerInstance->Modules->Find("m_services_account.so") || !ServerInstance->Modules->Find("m_cap.so"))
266                         ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: m_services_account.so and m_cap.so are not loaded! m_sasl.so will NOT function correctly until these two modules are loaded!");
267         }
268
269         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
270         {
271                 sasl_target = ServerInstance->Config->ConfValue("sasl")->getString("target", "*");
272         }
273
274         ModResult OnUserRegister(LocalUser *user) CXX11_OVERRIDE
275         {
276                 SaslAuthenticator *sasl_ = authExt.get(user);
277                 if (sasl_)
278                 {
279                         sasl_->Abort();
280                         authExt.unset(user);
281                 }
282
283                 return MOD_RES_PASSTHRU;
284         }
285
286         Version GetVersion() CXX11_OVERRIDE
287         {
288                 return Version("Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE.",VF_VENDOR);
289         }
290 };
291
292 MODULE_INIT(ModuleSASL)