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