]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_sasl.cpp
ModResult conversion: Change return type of all module functions
[user/henk/code/inspircd.git] / src / modules / m_sasl.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "m_cap.h"
16 #include "account.h"
17
18 /* $ModDesc: Provides support for IRC Authentication Layer (aka: atheme SASL) via AUTHENTICATE. */
19
20 enum SaslState { SASL_INIT, SASL_COMM, SASL_DONE };
21 enum SaslResult { SASL_OK, SASL_FAIL, SASL_ABORT };
22
23 /**
24  * Tracks SASL authentication state like charybdis does. --nenolod
25  */
26 class SaslAuthenticator : public classbase
27 {
28  private:
29         InspIRCd *ServerInstance;
30         std::string agent;
31         User *user;
32         SaslState state;
33         SaslResult result;
34         bool state_announced;
35
36  public:
37         SaslAuthenticator(User *user_, std::string method, InspIRCd *instance, Module *ctor)
38                 : ServerInstance(instance), user(user_), state(SASL_INIT), state_announced(false)
39         {
40                 this->user->Extend("sasl_authenticator", this);
41
42                 parameterlist params;
43                 params.push_back("*");
44                 params.push_back("SASL");
45                 params.push_back(user->uuid);
46                 params.push_back("*");
47                 params.push_back("S");
48                 params.push_back(method);
49
50                 ServerInstance->PI->SendEncapsulatedData(params);
51         }
52
53         SaslResult GetSaslResult(const std::string &result_)
54         {
55                 if (result_ == "F")
56                         return SASL_FAIL;
57
58                 if (result_ == "A")
59                         return SASL_ABORT;
60
61                 return SASL_OK;
62         }
63
64         /* checks for and deals with a state change. */
65         SaslState ProcessInboundMessage(const std::vector<std::string> &msg)
66         {
67                 switch (this->state)
68                 {
69                  case SASL_INIT:
70                         this->agent = msg[0];
71                         this->user->Write("AUTHENTICATE %s", msg[3].c_str());
72                         this->state = SASL_COMM;
73                         break;
74                  case SASL_COMM:
75                         if (msg[0] != this->agent)
76                                 return this->state;
77
78                         if (msg[2] != "D")
79                                 this->user->Write("AUTHENTICATE %s", msg[3].c_str());
80                         else
81                         {
82                                 this->state = SASL_DONE;
83                                 this->result = this->GetSaslResult(msg[3]);
84                         }
85
86                         break;
87                  case SASL_DONE:
88                         break;
89                  default:
90                         ServerInstance->Logs->Log("m_sasl", DEFAULT, "WTF: SaslState is not a known state (%d)", this->state);
91                         break;
92                 }
93
94                 return this->state;
95         }
96
97         void Abort(void)
98         {
99                 this->state = SASL_DONE;
100                 this->result = SASL_ABORT;
101         }
102
103         bool SendClientMessage(const std::vector<std::string>& parameters)
104         {
105                 if (this->state != SASL_COMM)
106                         return true;
107
108                 parameterlist params;
109                 params.push_back("*");
110                 params.push_back("SASL");
111                 params.push_back(this->user->uuid);
112                 params.push_back(this->agent);
113                 params.push_back("C");
114
115                 params.insert(params.end(), parameters.begin(), parameters.end());
116
117                 ServerInstance->PI->SendEncapsulatedData(params);
118
119                 if (parameters[0][0] == '*')
120                 {
121                         this->Abort();
122                         return false;
123                 }
124
125                 return true;
126         }
127
128         void AnnounceState(void)
129         {
130                 if (this->state_announced)
131                         return;
132
133                 switch (this->result)
134                 {
135                  case SASL_OK:
136                         this->user->WriteNumeric(903, "%s :SASL authentication successful", this->user->nick.c_str());
137                         break;
138                  case SASL_ABORT:
139                         this->user->WriteNumeric(906, "%s :SASL authentication aborted", this->user->nick.c_str());
140                         break;
141                  case SASL_FAIL:
142                         this->user->WriteNumeric(904, "%s :SASL authentication failed", this->user->nick.c_str());
143                         break;
144                  default:
145                         break;
146                 }
147
148                 this->state_announced = true;
149         }
150
151         ~SaslAuthenticator()
152         {
153                 this->user->Shrink("sasl_authenticator");
154                 this->AnnounceState();
155         }
156 };
157
158 class CommandAuthenticate : public Command
159 {
160  public:
161         CommandAuthenticate (InspIRCd* Instance, Module* Creator) : Command(Instance, Creator, "AUTHENTICATE", 0, 1, true)
162         {
163         }
164
165         CmdResult Handle (const std::vector<std::string>& parameters, User *user)
166         {
167                 /* Only allow AUTHENTICATE on unregistered clients */
168                 if (user->registered != REG_ALL)
169                 {
170                         if (!user->GetExt("sasl"))
171                                 return CMD_FAILURE;
172
173                         SaslAuthenticator *sasl;
174                         if (!(user->GetExt("sasl_authenticator", sasl)))
175                                 sasl = new SaslAuthenticator(user, parameters[0], ServerInstance, creator);
176                         else if (sasl->SendClientMessage(parameters) == false)  // IAL abort extension --nenolod
177                                 delete sasl;
178                 }
179                 return CMD_FAILURE;
180         }
181 };
182
183 class CommandSASL : public Command
184 {
185  public:
186         CommandSASL(InspIRCd* Instance, Module* Creator) : Command(Instance, Creator, "SASL", 0, 2)
187         {
188                 this->disabled = true; // should not be called by users
189         }
190
191         CmdResult Handle(const std::vector<std::string>& parameters, User *user)
192         {
193                 User* target = ServerInstance->FindNick(parameters[1]);
194                 if (!target)
195                 {
196                         ServerInstance->Logs->Log("m_sasl", DEBUG,"User not found in sasl ENCAP event: %s", parameters[1].c_str());
197                         return CMD_FAILURE;
198                 }
199
200                 SaslAuthenticator *sasl;
201                 if (!target->GetExt("sasl_authenticator", sasl))
202                         return CMD_FAILURE;
203
204                 SaslState state = sasl->ProcessInboundMessage(parameters);
205                 if (state == SASL_DONE)
206                 {
207                         delete sasl;
208                         target->Shrink("sasl");
209                 }
210                 return CMD_SUCCESS;
211         }
212 };
213
214 class ModuleSASL : public Module
215 {
216         CommandAuthenticate auth;
217         CommandSASL sasl;
218  public:
219
220         ModuleSASL(InspIRCd* Me)
221                 : Module(Me), auth(Me, this), sasl(Me, this)
222         {
223                 Implementation eventlist[] = { I_OnEvent, I_OnUserRegister, I_OnPostConnect, I_OnUserDisconnect, I_OnCleanup };
224                 ServerInstance->Modules->Attach(eventlist, this, 5);
225
226                 ServerInstance->AddCommand(&auth);
227                 ServerInstance->AddCommand(&sasl);
228
229                 if (!ServerInstance->Modules->Find("m_services_account.so") || !ServerInstance->Modules->Find("m_cap.so"))
230                         ServerInstance->Logs->Log("m_sasl", 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!");
231         }
232
233         virtual ModResult OnUserRegister(User *user)
234         {
235                 SaslAuthenticator *sasl_;
236                 if (user->GetExt("sasl_authenticator", sasl_))
237                 {
238                         sasl_->Abort();
239                         delete sasl_;
240                         user->Shrink("sasl_authenticator");
241                 }
242
243                 return MOD_RES_PASSTHRU;
244         }
245
246         virtual void OnCleanup(int target_type, void *item)
247         {
248                 if (target_type == TYPE_USER)
249                         OnUserDisconnect((User*)item);
250         }
251
252         virtual void OnUserDisconnect(User *user)
253         {
254                 SaslAuthenticator *sasl_;
255                 if (user->GetExt("sasl_authenticator", sasl_))
256                 {
257                         delete sasl_;
258                         user->Shrink("sasl_authenticator");
259                 }
260         }
261
262         virtual void OnPostConnect(User* user)
263         {
264                 if (!IS_LOCAL(user))
265                         return;
266
267                 std::string* str = NULL;
268
269                 if (user->GetExt("accountname", str))
270                         ServerInstance->PI->SendMetaData(user, "accountname", *str);
271
272                 return;
273         }
274
275         virtual ~ModuleSASL()
276         {
277         }
278
279         virtual Version GetVersion()
280         {
281                 return Version("$Id$",VF_VENDOR,API_VERSION);
282         }
283
284         virtual void OnEvent(Event *ev)
285         {
286                 GenericCapHandler(ev, "sasl", "sasl");
287         }
288 };
289
290 MODULE_INIT(ModuleSASL)