]> git.stg.codes - stg.git/blob - projects/stargazer/plugins/other/radius/radius.cpp
Merge branch 'master' of madf.dyndns.org:/var/git/stg
[stg.git] / projects / stargazer / plugins / other / radius / radius.cpp
1 /*
2  *    This program is free software; you can redistribute it and/or modify
3  *    it under the terms of the GNU General Public License as published by
4  *    the Free Software Foundation; either version 2 of the License, or
5  *    (at your option) any later version.
6  *
7  *    This program is distributed in the hope that it will be useful,
8  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *    GNU General Public License for more details.
11  *
12  *    You should have received a copy of the GNU General Public License
13  *    along with this program; if not, write to the Free Software
14  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16
17 /*
18  *    Author : Maxim Mamontov <faust@stargazer.dp.ua>
19  */
20
21 /*
22  *  This file contains a realization of radius data access plugin for Stargazer
23  *
24  *  $Revision: 1.14 $
25  *  $Date: 2009/12/13 14:17:13 $
26  *
27  */
28
29 #include <csignal>
30 #include <cerrno>
31 #include <algorithm>
32
33 #include "radius.h"
34 #include "common.h"
35
36 extern volatile const time_t stgTime;
37
38 void InitEncrypt(BLOWFISH_CTX * ctx, const string & password);
39 void Decrypt(BLOWFISH_CTX * ctx, char * dst, const char * src, int len8);
40 void Encrypt(BLOWFISH_CTX * ctx, char * dst, const char * src, int len8);
41
42 //-----------------------------------------------------------------------------
43 //-----------------------------------------------------------------------------
44 //-----------------------------------------------------------------------------
45 class RAD_CREATOR
46 {
47 private:
48     RADIUS * rad;
49
50 public:
51     RAD_CREATOR()
52         : rad(new RADIUS())
53         {
54         };
55     ~RAD_CREATOR()
56         {
57         delete rad;
58         };
59
60     RADIUS * GetPlugin()
61         {
62         return rad;
63         };
64 };
65 //-----------------------------------------------------------------------------
66 //-----------------------------------------------------------------------------
67 //-----------------------------------------------------------------------------
68 RAD_CREATOR radc;
69 //-----------------------------------------------------------------------------
70 //-----------------------------------------------------------------------------
71 //-----------------------------------------------------------------------------
72 BASE_PLUGIN * GetPlugin()
73 {
74 return radc.GetPlugin();
75 }
76 //-----------------------------------------------------------------------------
77 //-----------------------------------------------------------------------------
78 //-----------------------------------------------------------------------------
79 int RAD_SETTINGS::ParseIntInRange(const string & str, int min, int max, int * val)
80 {
81 if (str2x(str.c_str(), *val))
82     {
83     errorStr = "Incorrect value \'" + str + "\'.";
84     return -1;
85     }
86 if (*val < min || *val > max)
87     {
88     errorStr = "Value \'" + str + "\' out of range.";
89     return -1;
90     }
91 return 0;
92 }
93 //-----------------------------------------------------------------------------
94 int RAD_SETTINGS::ParseServices(const vector<string> & str, list<string> * lst)
95 {
96     copy(str.begin(), str.end(), back_inserter(*lst));
97     list<string>::iterator it(find(lst->begin(),
98                                    lst->end(),
99                                    "empty"));
100     if (it != lst->end())
101         *it = "";
102
103     return 0;
104 }
105 //-----------------------------------------------------------------------------
106 int RAD_SETTINGS::ParseSettings(const MODULE_SETTINGS & s)
107 {
108 int p;
109 PARAM_VALUE pv;
110 vector<PARAM_VALUE>::const_iterator pvi;
111 ///////////////////////////
112 pv.param = "Port";
113 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
114 if (pvi == s.moduleParams.end())
115     {
116     errorStr = "Parameter \'Port\' not found.";
117     printfd(__FILE__, "Parameter 'Port' not found\n");
118     return -1;
119     }
120 if (ParseIntInRange(pvi->value[0], 2, 65535, &p))
121     {
122     errorStr = "Cannot parse parameter \'Port\': " + errorStr;
123     printfd(__FILE__, "Cannot parse parameter 'Port'\n");
124     return -1;
125     }
126 port = p;
127 ///////////////////////////
128 pv.param = "Password";
129 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
130 if (pvi == s.moduleParams.end())
131     {
132     errorStr = "Parameter \'Password\' not found.";
133     printfd(__FILE__, "Parameter 'Password' not found\n");
134     return -1;
135     }
136 password = pvi->value[0];
137 ///////////////////////////
138 pv.param = "AuthServices";
139 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
140 if (pvi != s.moduleParams.end())
141     {
142     ParseServices(pvi->value, &authServices);
143     }
144 ///////////////////////////
145 pv.param = "AcctServices";
146 pvi = find(s.moduleParams.begin(), s.moduleParams.end(), pv);
147 if (pvi != s.moduleParams.end())
148     {
149     ParseServices(pvi->value, &acctServices);
150     }
151
152 return 0;
153 }
154 //-----------------------------------------------------------------------------
155 //-----------------------------------------------------------------------------
156 //-----------------------------------------------------------------------------
157 RADIUS::RADIUS()
158     : nonstop(false),
159       isRunning(false),
160       users(NULL),
161       stgSettings(NULL),
162       store(NULL),
163       sock(-1)
164 {
165 InitEncrypt(&ctx, "");
166 }
167 //-----------------------------------------------------------------------------
168 void RADIUS::SetUsers(USERS * u)
169 {
170 users = u;
171 }
172 //-----------------------------------------------------------------------------
173 void RADIUS::SetStgSettings(const SETTINGS * s)
174 {
175 stgSettings = s;
176 }
177 //-----------------------------------------------------------------------------
178 void RADIUS::SetSettings(const MODULE_SETTINGS & s)
179 {
180 settings = s;
181 }
182 //-----------------------------------------------------------------------------
183 void RADIUS::SetStore(BASE_STORE * s)
184 {
185 store = s;
186 }
187 //-----------------------------------------------------------------------------
188 int RADIUS::ParseSettings()
189 {
190 int ret = radSettings.ParseSettings(settings);
191 if (ret)
192     errorStr = radSettings.GetStrError();
193 return ret;
194 }
195 //-----------------------------------------------------------------------------
196 bool RADIUS::IsRunning()
197 {
198 return isRunning;
199 }
200 //-----------------------------------------------------------------------------
201 const string RADIUS::GetVersion() const
202 {
203 return "RADIUS data access plugin v 0.6";
204 }
205 //-----------------------------------------------------------------------------
206 uint16_t RADIUS::GetStartPosition() const
207 {
208 // Start before any authorizers!!!
209 return 20;
210 }
211 //-----------------------------------------------------------------------------
212 uint16_t RADIUS::GetStopPosition() const
213 {
214 return 20;
215 }
216 //-----------------------------------------------------------------------------
217 int RADIUS::PrepareNet()
218 {
219 sock = socket(AF_INET, SOCK_DGRAM, 0);
220
221 if (sock < 0)
222     {
223     errorStr = "Cannot create socket.";
224     printfd(__FILE__, "Cannot create socket\n");
225     return -1;
226     }
227
228 struct sockaddr_in inAddr;
229 inAddr.sin_family = AF_INET;
230 inAddr.sin_port = htons(radSettings.GetPort());
231 inAddr.sin_addr.s_addr = inet_addr("0.0.0.0");
232
233 if (bind(sock, (struct sockaddr*)&inAddr, sizeof(inAddr)) < 0)
234     {
235     errorStr = "RADIUS: Bind failed.";
236     printfd(__FILE__, "Cannot bind socket\n");
237     return -1;
238     }
239
240 return 0;
241 }
242 //-----------------------------------------------------------------------------
243 int RADIUS::FinalizeNet()
244 {
245 close(sock);
246 return 0;
247 }
248 //-----------------------------------------------------------------------------
249 int RADIUS::Start()
250 {
251 string password(radSettings.GetPassword());
252
253 authServices = radSettings.GetAuthServices();
254 acctServices = radSettings.GetAcctServices();
255
256 InitEncrypt(&ctx, password);
257
258 nonstop = true;
259
260 if (PrepareNet())
261     {
262     return -1;
263     }
264
265 if (!isRunning)
266     {
267     if (pthread_create(&thread, NULL, Run, this))
268         {
269         errorStr = "Cannot create thread.";
270         printfd(__FILE__, "Cannot create thread\n");
271         return -1;
272         }
273     }
274
275 errorStr = "";
276 return 0;
277 }
278 //-----------------------------------------------------------------------------
279 int RADIUS::Stop()
280 {
281 if (!IsRunning())
282     return 0;
283
284 nonstop = false;
285
286 map<string, RAD_SESSION>::iterator it;
287 for (it = sessions.begin(); it != sessions.end(); ++it)
288     {
289     user_iter ui;
290     if (users->FindByName(it->second.userName, &ui))
291         {
292         ui->Unauthorize(this);
293         }
294     }
295 sessions.erase(sessions.begin(), sessions.end());
296
297 FinalizeNet();
298
299 if (isRunning)
300     {
301     //5 seconds to thread stops itself
302     for (int i = 0; i < 25 && isRunning; i++)
303         {
304         usleep(200000);
305         }
306
307     //after 5 seconds waiting thread still running. now killing it
308     if (isRunning)
309         {
310         if (pthread_kill(thread, SIGINT))
311             {
312             errorStr = "Cannot kill thread.";
313             printfd(__FILE__, "Cannot kill thread\n");
314             return -1;
315             }
316         printfd(__FILE__, "RADIUS::Stop killed Run\n");
317         }
318     }
319
320 return 0;
321 }
322 //-----------------------------------------------------------------------------
323 void * RADIUS::Run(void * d)
324 {
325 RADIUS * rad = (RADIUS *)d;
326 RAD_PACKET packet;
327
328 rad->isRunning = true;
329
330 while (rad->nonstop)
331     {
332     if (!rad->WaitPackets(rad->sock))
333         {
334         continue;
335         }
336     struct sockaddr_in outerAddr;
337     if (rad->RecvData(&packet, &outerAddr))
338         {
339         printfd(__FILE__, "RADIUS::Run Error on RecvData\n");
340         }
341     else
342         {
343         if (rad->ProcessData(&packet))
344             {
345             packet.packetType = RAD_REJECT_PACKET;
346             }
347         rad->Send(packet, &outerAddr);
348         }
349     }
350
351 rad->isRunning = false;
352
353 return NULL;
354 }
355 //-----------------------------------------------------------------------------
356 int RADIUS::RecvData(RAD_PACKET * packet, struct sockaddr_in * outerAddr)
357 {
358     int8_t buf[RAD_MAX_PACKET_LEN];
359     socklen_t outerAddrLen = sizeof(struct sockaddr_in);
360     int dataLen = recvfrom(sock, buf, RAD_MAX_PACKET_LEN, 0, reinterpret_cast<struct sockaddr *>(outerAddr), &outerAddrLen);
361     if (dataLen > 0) {
362         Decrypt(&ctx, (char *)packet, (const char *)buf, dataLen / 8);
363     }
364     if (strncmp((char *)packet->magic, RAD_ID, RAD_MAGIC_LEN))
365         {
366         printfd(__FILE__, "RADIUS::RecvData Error magic. Wanted: '%s', got: '%s'\n", RAD_ID, packet->magic);
367         return -1;
368         }
369     return 0;
370 }
371 //-----------------------------------------------------------------------------
372 int RADIUS::Send(const RAD_PACKET & packet, struct sockaddr_in * outerAddr)
373 {
374 size_t len = sizeof(RAD_PACKET);
375 char buf[1032];
376
377 Encrypt(&ctx, buf, (char *)&packet, len / 8);
378 return sendto(sock, buf, len, 0, reinterpret_cast<struct sockaddr *>(outerAddr), sizeof(struct sockaddr_in));
379 }
380 //-----------------------------------------------------------------------------
381 int RADIUS::ProcessData(RAD_PACKET * packet)
382 {
383 if (strncmp((const char *)packet->protoVer, "01", 2))
384     {
385     printfd(__FILE__, "RADIUS::ProcessData packet.protoVer incorrect\n");
386     return -1;
387     }
388 switch (packet->packetType)
389     {
390     case RAD_AUTZ_PACKET:
391         return ProcessAutzPacket(packet);
392     case RAD_AUTH_PACKET:
393         return ProcessAuthPacket(packet);
394     case RAD_POST_AUTH_PACKET:
395         return ProcessPostAuthPacket(packet);
396     case RAD_ACCT_START_PACKET:
397         return ProcessAcctStartPacket(packet);
398     case RAD_ACCT_STOP_PACKET:
399         return ProcessAcctStopPacket(packet);
400     case RAD_ACCT_UPDATE_PACKET:
401         return ProcessAcctUpdatePacket(packet);
402     case RAD_ACCT_OTHER_PACKET:
403         return ProcessAcctOtherPacket(packet);
404     default:
405         printfd(__FILE__, "RADIUS::ProcessData Unsupported packet type: %d\n", packet->packetType);
406         return -1;
407     };
408 return 0;
409 }
410 //-----------------------------------------------------------------------------
411 int RADIUS::ProcessAutzPacket(RAD_PACKET * packet)
412 {
413 USER_CONF conf;
414
415 if (!IsAllowedService((char *)packet->service))
416     {
417     printfd(__FILE__, "RADIUS::ProcessAutzPacket service '%s' is not allowed to authorize\n", packet->service);
418     packet->packetType = RAD_REJECT_PACKET;
419     return 0;
420     }
421
422 if (store->RestoreUserConf(&conf, (char *)packet->login))
423     {
424     packet->packetType = RAD_REJECT_PACKET;
425     printfd(__FILE__, "RADIUS::ProcessAutzPacket cannot restore conf for user '%s'\n", packet->login);
426     return 0;
427     }
428
429 // At this point service can be authorized at least
430 // So we send a plain-text password
431
432 packet->packetType = RAD_ACCEPT_PACKET;
433 strncpy((char *)packet->password, conf.password.c_str(), RAD_PASSWORD_LEN);
434
435 return 0;
436 }
437 //-----------------------------------------------------------------------------
438 int RADIUS::ProcessAuthPacket(RAD_PACKET * packet)
439 {
440 user_iter ui;
441
442 if (!CanAcctService((char *)packet->service))
443     {
444
445     // There are no sense to check for allowed service
446     // It has allready checked at previous stage (authorization)
447
448     printfd(__FILE__, "RADIUS::ProcessAuthPacket service '%s' neednot stargazer authentication\n", (char *)packet->service);
449     packet->packetType = RAD_ACCEPT_PACKET;
450     return 0;
451     }
452
453 // At this point we have an accountable service
454 // All other services got a password if allowed or rejected
455
456 if (!FindUser(&ui, (char *)packet->login))
457     {
458     packet->packetType = RAD_REJECT_PACKET;
459     printfd(__FILE__, "RADIUS::ProcessAuthPacket user '%s' not found\n", (char *)packet->login);
460     return 0;
461     }
462
463 if (ui->IsInetable())
464     {
465     packet->packetType = RAD_ACCEPT_PACKET;
466     }
467 else
468     {
469     packet->packetType = RAD_REJECT_PACKET;
470     }
471
472 packet->packetType = RAD_ACCEPT_PACKET;
473 return 0;
474 }
475 //-----------------------------------------------------------------------------
476 int RADIUS::ProcessPostAuthPacket(RAD_PACKET * packet)
477 {
478 user_iter ui;
479
480 if (!CanAcctService((char *)packet->service))
481     {
482
483     // There are no sense to check for allowed service
484     // It has allready checked at previous stage (authorization)
485
486     packet->packetType = RAD_ACCEPT_PACKET;
487     return 0;
488     }
489
490 if (!FindUser(&ui, (char *)packet->login))
491     {
492     packet->packetType = RAD_REJECT_PACKET;
493     printfd(__FILE__, "RADIUS::ProcessPostAuthPacket user '%s' not found\n", (char *)packet->login);
494     return 0;
495     }
496
497 // I think that only Framed-User services has sense to be accountable
498 // So we have to supply a Framed-IP
499
500 USER_IPS ips = ui->property.ips;
501 packet->packetType = RAD_ACCEPT_PACKET;
502
503 // Additional checking for Framed-User service
504
505 if (!strncmp((char *)packet->service, "Framed-User", RAD_SERVICE_LEN))
506     packet->ip = ips[0].ip;
507 else
508     packet->ip = 0;
509
510 return 0;
511 }
512 //-----------------------------------------------------------------------------
513 int RADIUS::ProcessAcctStartPacket(RAD_PACKET * packet)
514 {
515 user_iter ui;
516
517 if (!FindUser(&ui, (char *)packet->login))
518     {
519     packet->packetType = RAD_REJECT_PACKET;
520     printfd(__FILE__, "RADIUS::ProcessAcctStartPacket user '%s' not found\n", (char *)packet->login);
521     return 0;
522     }
523
524 // At this point we have to unauthorize user only if it is an accountable service
525
526 if (CanAcctService((char *)packet->service))
527     {
528     if (sessions.find((const char *)packet->sessid) != sessions.end())
529         {
530         printfd(__FILE__, "RADIUS::ProcessAcctStartPacket session already started!\n");
531         packet->packetType = RAD_REJECT_PACKET;
532         return -1;
533         }
534     USER_IPS ips = ui->property.ips;
535     if (ui->Authorize(ips[0].ip, "", 0xffFFffFF, this))
536         {
537         printfd(__FILE__, "RADIUS::ProcessAcctStartPacket cannot authorize user '%s'\n", packet->login);
538         packet->packetType = RAD_REJECT_PACKET;
539         return -1;
540         }
541     sessions[(const char *)packet->sessid].userName = (const char *)packet->login;
542     sessions[(const char *)packet->sessid].serviceType = (const char *)packet->service;
543     for_each(sessions.begin(), sessions.end(), SPrinter());
544     }
545 else
546     {
547     printfd(__FILE__, "RADIUS::ProcessAcctStartPacket service '%s' can not be accounted\n", (char *)packet->service);
548     }
549
550 packet->packetType = RAD_ACCEPT_PACKET;
551 return 0;
552 }
553 //-----------------------------------------------------------------------------
554 int RADIUS::ProcessAcctStopPacket(RAD_PACKET * packet)
555 {
556 map<string, RAD_SESSION>::iterator sid;
557
558 if ((sid = sessions.find((const char *)packet->sessid)) == sessions.end())
559     {
560     printfd(__FILE__, "RADIUS::ProcessAcctStopPacket session had not started yet\n");
561     packet->packetType = RAD_REJECT_PACKET;
562     return -1;
563     }
564
565 user_iter ui;
566
567 if (!FindUser(&ui, sid->second.userName))
568     {
569     packet->packetType = RAD_REJECT_PACKET;
570     printfd(__FILE__, "RADIUS::ProcessPostAuthPacket user '%s' not found\n", sid->second.userName.c_str());
571     return 0;
572     }
573
574 sessions.erase(sid);
575
576 ui->Unauthorize(this);
577
578 packet->packetType = RAD_ACCEPT_PACKET;
579 return 0;
580 }
581 //-----------------------------------------------------------------------------
582 int RADIUS::ProcessAcctUpdatePacket(RAD_PACKET * packet)
583 {
584 // Fake. May be use it later
585 packet->packetType = RAD_ACCEPT_PACKET;
586 return 0;
587 }
588 //-----------------------------------------------------------------------------
589 int RADIUS::ProcessAcctOtherPacket(RAD_PACKET * packet)
590 {
591 // Fake. May be use it later
592 packet->packetType = RAD_ACCEPT_PACKET;
593 return 0;
594 }
595 //-----------------------------------------------------------------------------
596 void RADIUS::PrintServices(const list<string> & svcs)
597 {
598     for_each(svcs.begin(), svcs.end(), Printer());
599 }
600 //-----------------------------------------------------------------------------
601 bool RADIUS::FindUser(user_iter * ui, const std::string & login) const
602 {
603 if (users->FindByName(login, ui))
604     {
605     return false;
606     }
607 return true;
608 }
609 //-----------------------------------------------------------------------------
610 bool RADIUS::CanAuthService(const std::string & svc) const
611 {
612     return find(authServices.begin(), authServices.end(), svc) != authServices.end();
613 }
614 //-----------------------------------------------------------------------------
615 bool RADIUS::CanAcctService(const std::string & svc) const
616 {
617     return find(acctServices.begin(), acctServices.end(), svc) != acctServices.end();
618 }
619 //-----------------------------------------------------------------------------
620 bool RADIUS::IsAllowedService(const std::string & svc) const
621 {
622     return CanAuthService(svc) || CanAcctService(svc);
623 }
624 //-----------------------------------------------------------------------------
625 bool RADIUS::WaitPackets(int sd) const
626 {
627 fd_set rfds;
628 FD_ZERO(&rfds);
629 FD_SET(sd, &rfds);
630
631 struct timeval tv;
632 tv.tv_sec = 0;
633 tv.tv_usec = 500000;
634
635 int res = select(sd + 1, &rfds, NULL, NULL, &tv);
636 if (res == -1) // Error
637     {
638     if (errno != EINTR)
639         {
640         printfd(__FILE__, "Error on select: '%s'\n", strerror(errno));
641         }
642     return false;
643     }
644
645 if (res == 0) // Timeout
646     {
647     return false;
648     }
649
650 return true;
651 }
652 //-----------------------------------------------------------------------------
653 inline
654 void InitEncrypt(BLOWFISH_CTX * ctx, const string & password)
655 {
656 unsigned char keyL[RAD_PASSWORD_LEN];  // Пароль для шифровки
657 memset(keyL, 0, RAD_PASSWORD_LEN);
658 strncpy((char *)keyL, password.c_str(), RAD_PASSWORD_LEN);
659 Blowfish_Init(ctx, keyL, RAD_PASSWORD_LEN);
660 }
661 //-----------------------------------------------------------------------------
662 inline
663 void Encrypt(BLOWFISH_CTX * ctx, char * dst, const char * src, int len8)
664 {
665 // len8 - длина в 8-ми байтовых блоках
666 if (dst != src)
667     memcpy(dst, src, len8 * 8);
668
669 for (int i = 0; i < len8; i++)
670     Blowfish_Encrypt(ctx, (uint32_t *)(dst + i*8), (uint32_t *)(dst + i*8 + 4));
671 }
672 //-----------------------------------------------------------------------------
673 inline
674 void Decrypt(BLOWFISH_CTX * ctx, char * dst, const char * src, int len8)
675 {
676 // len8 - длина в 8-ми байтовых блоках
677 if (dst != src)
678     memcpy(dst, src, len8 * 8);
679
680 for (int i = 0; i < len8; i++)
681     Blowfish_Decrypt(ctx, (uint32_t *)(dst + i*8), (uint32_t *)(dst + i*8 + 4));
682 }