]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sasl.cpp
1ef1fa073df4ae04e811a60ac6dc2d1f0be110c9
[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
32 static void SendSASL(const parameterlist& params)
33 {
34         if (!ServerInstance->PI->SendEncapsulatedData(params))
35         {
36                 SASLFallback(NULL, params);
37         }
38 }
39
40 /**
41  * Tracks SASL authentication state like charybdis does. --nenolod
42  */
43 class SaslAuthenticator
44 {
45  private:
46         std::string agent;
47         User *user;
48         SaslState state;
49         SaslResult result;
50         bool state_announced;
51
52  public:
53         SaslAuthenticator(User* user_, const std::string& method)
54                 : user(user_), state(SASL_INIT), state_announced(false)
55         {
56                 parameterlist params;
57                 params.push_back(sasl_target);
58                 params.push_back("SASL");
59                 params.push_back(user->uuid);
60                 params.push_back("*");
61                 params.push_back("S");
62                 params.push_back(method);
63
64                 LocalUser* localuser = IS_LOCAL(user);
65                 if (method == "EXTERNAL" && localuser)
66                 {
67                         std::string fp = SSLClientCert::GetFingerprint(&localuser->eh);
68
69                         if (fp.size())
70                                 params.push_back(fp);
71                 }
72
73                 SendSASL(params);
74         }
75
76         SaslResult GetSaslResult(const std::string &result_)
77         {
78                 if (result_ == "F")
79                         return SASL_FAIL;
80
81                 if (result_ == "A")
82                         return SASL_ABORT;
83
84                 return SASL_OK;
85         }
86
87         /* checks for and deals with a state change. */
88         SaslState ProcessInboundMessage(const std::vector<std::string> &msg)
89         {
90                 switch (this->state)
91                 {
92                  case SASL_INIT:
93                         this->agent = msg[0];
94                         this->user->Write("AUTHENTICATE %s", msg[3].c_str());
95                         this->state = SASL_COMM;
96                         break;
97                  case SASL_COMM:
98                         if (msg[0] != this->agent)
99                                 return this->state;
100
101                         if (msg[2] != "D")
102                                 this->user->Write("AUTHENTICATE %s", msg[3].c_str());
103                         else
104                         {
105                                 this->state = SASL_DONE;
106                                 this->result = this->GetSaslResult(msg[3]);
107                         }
108
109                         break;
110                  case SASL_DONE:
111                         break;
112                  default:
113                         ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
114                         break;
115                 }
116
117                 return this->state;
118         }
119
120         void Abort(void)
121         {
122                 this->state = SASL_DONE;
123                 this->result = SASL_ABORT;
124         }
125
126         bool SendClientMessage(const std::vector<std::string>& parameters)
127         {
128                 if (this->state != SASL_COMM)
129                         return true;
130
131                 parameterlist params;
132                 params.push_back(sasl_target);
133                 params.push_back("SASL");
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, "%s :SASL authentication successful", this->user->nick.c_str());
160                         break;
161                  case SASL_ABORT:
162                         this->user->WriteNumeric(906, "%s :SASL authentication aborted", this->user->nick.c_str());
163                         break;
164                  case SASL_FAIL:
165                         this->user->WriteNumeric(904, "%s :SASL authentication failed", this->user->nick.c_str());
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
251  public:
252         ModuleSASL()
253                 : authExt("sasl_auth", this), cap(this, "sasl"), auth(this, authExt, cap), sasl(this, authExt)
254         {
255         }
256
257         void init() CXX11_OVERRIDE
258         {
259                 ServiceProvider* providelist[] = { &auth, &sasl, &authExt };
260                 ServerInstance->Modules->AddServices(providelist, 3);
261
262                 if (!ServerInstance->Modules->Find("m_services_account.so") || !ServerInstance->Modules->Find("m_cap.so"))
263                         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!");
264         }
265
266         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
267         {
268                 sasl_target = ServerInstance->Config->ConfValue("sasl")->getString("target", "*");
269         }
270
271         ModResult OnUserRegister(LocalUser *user) CXX11_OVERRIDE
272         {
273                 SaslAuthenticator *sasl_ = authExt.get(user);
274                 if (sasl_)
275                 {
276                         sasl_->Abort();
277                         authExt.unset(user);
278                 }
279
280                 return MOD_RES_PASSTHRU;
281         }
282
283         Version GetVersion() CXX11_OVERRIDE
284         {
285                 return Version("Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE.",VF_VENDOR);
286         }
287
288         void OnEvent(Event &ev) CXX11_OVERRIDE
289         {
290                 cap.HandleEvent(ev);
291         }
292 };
293
294 MODULE_INIT(ModuleSASL)