The patch below fixes a problem that Simultaneous_Use from limiting
[freeradius.git] / src / modules / rlm_radutmp / rlm_radutmp.c
1 /*
2  * rlm_radutmp.c        
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * FIXME add copyrights
22  */
23
24 #include        "autoconf.h"
25
26 #include        <sys/types.h>
27 #include        <stdio.h>
28 #include        <string.h>
29 #include        <stdlib.h>
30 #include        <unistd.h>
31 #include        <fcntl.h>
32 #include        <time.h>
33 #include        <errno.h>
34 #include        <limits.h>
35
36 #include "config.h"
37
38 #include        "radiusd.h"
39 #include        "radutmp.h"
40 #include        "modules.h"
41
42 #define LOCK_LEN sizeof(struct radutmp)
43
44 static const char porttypes[] = "ASITX";
45
46 /*
47  *      Internal wrapper for locking, to minimize the number of ifdef's
48  *      in the source.
49  *
50  *      Lock the utmp file, prefer lockf() over flock()
51  */
52 static void radutmp_lock(int fd)
53 {
54 #if defined(F_LOCK) && !defined(BSD)
55         (void)lockf(fd, F_LOCK, LOCK_LEN);
56 #else
57         (void)flock(fd, LOCK_EX);
58 #endif
59 }
60
61 /*
62  *      Internal wrapper for unlocking, to minimize the number of ifdef's
63  *      in the source.
64  *
65  *      Unlock the utmp file, prefer lockf() over flock()
66  */
67 static void radutmp_unlock(int fd)
68 {
69 #if defined(F_LOCK) && !defined(BSD)
70         (void)lockf(fd, F_ULOCK, LOCK_LEN);
71 #else
72         (void)flock(fd, LOCK_UN);
73 #endif
74 }
75
76 /*
77  *      used for caching radutmp lookups in the accounting component. The
78  *      session (checksimul) component doesn't use it, but probably should.
79  */
80 typedef struct nas_port {
81         uint32_t                nasaddr;
82         int                     port;
83         off_t                   offset;
84         struct nas_port         *next;
85 } NAS_PORT;
86
87 struct radutmp_instance {
88   NAS_PORT *nas_port_list;
89   char *radutmp_fn;
90   char *username;
91   int permission;
92   int callerid_ok;
93 };
94
95 static CONF_PARSER module_config[] = {
96   { "filename", PW_TYPE_STRING_PTR,
97     offsetof(struct radutmp_instance,radutmp_fn), NULL,  RADUTMP },
98   { "username", PW_TYPE_STRING_PTR,
99     offsetof(struct radutmp_instance,username), NULL,  "%{User-Name}"},
100   { "perm",     PW_TYPE_INTEGER,
101     offsetof(struct radutmp_instance,permission), NULL,  "0644" },
102   { "callerid", PW_TYPE_BOOLEAN,
103     offsetof(struct radutmp_instance,callerid_ok), NULL, "no" },
104   { NULL, -1, 0, NULL, NULL }           /* end the list */
105 };
106
107 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
108 {
109         struct radutmp_instance *inst;
110         inst = rad_malloc(sizeof(*inst));
111         if (cf_section_parse(conf, inst, module_config)) {
112                 free(inst);
113                 return -1;
114         }
115         inst->nas_port_list = NULL;
116         *instance = inst;
117         return 0;
118 }
119
120
121 /*
122  *      Detach.
123  */
124 static int radutmp_detach(void *instance)
125 {
126         NAS_PORT *p, *next;
127         struct radutmp_instance *inst = instance;
128
129         for(p=inst->nas_port_list ; p ; p=next) {
130                 next=p->next;
131                 free(p);
132         }
133         free(inst->radutmp_fn);
134         free(inst);
135         return 0;
136 }
137
138 /*
139  *      Zap all users on a NAS from the radutmp file.
140  */
141 static int radutmp_zap(struct radutmp_instance *inst, uint32_t nasaddr, time_t t)
142 {
143         struct radutmp  u;
144         int             fd;
145
146         if (t == 0) time(&t);
147
148         fd = open(inst->radutmp_fn, O_RDWR);
149         if (fd >= 0) {
150                 /*
151                  *      Lock the utmp file, prefer lockf() over flock().
152                  */
153                 radutmp_lock(fd);
154
155                 /*
156                  *      Find the entry for this NAS / portno combination.
157                  */
158                 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
159                         if ((nasaddr != 0 && nasaddr != u.nas_address) ||
160                               u.type != P_LOGIN)
161                                 continue;
162                         /*
163                          *      Match. Zap it.
164                          */
165                         if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
166                                 radlog(L_ERR, "Accounting: radutmp_zap: "
167                                               "negative lseek!\n");
168                                 lseek(fd, (off_t)0, SEEK_SET);
169                         }
170                         u.type = P_IDLE;
171                         u.time = t;
172                         write(fd, &u, sizeof(u));
173                 }
174                 close(fd);
175         } else {
176                 radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
177         }
178
179         return 0;
180 }
181
182 /*
183  *      Lookup a NAS_PORT in the nas_port_list
184  */
185 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, int port)
186 {
187         NAS_PORT        *cl;
188
189         for(cl = nas_port_list; cl; cl = cl->next)
190                 if (nasaddr == cl->nasaddr &&
191                         port == cl->port)
192                         break;
193         return cl;
194 }
195
196
197 /*
198  *      Store logins in the RADIUS utmp file.
199  */
200 static int radutmp_accounting(void *instance, REQUEST *request)
201 {
202         struct radutmp  ut, u;
203         VALUE_PAIR      *vp;
204         int             rb_record = 0;
205         int             status = -1;
206         uint32_t        nas_address = 0;
207         uint32_t        framed_address = 0;
208         int             protocol = -1;
209         time_t          t;
210         int             fd;
211         int             just_an_update = 0;
212         int             port_seen = 0;
213         int             nas_port_type = 0;
214         int             off;
215         struct radutmp_instance *inst = instance;
216         char            buffer[256];
217
218         /*
219          *      Which type is this.
220          */
221         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
222                 radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
223                 return RLM_MODULE_NOOP;
224         }
225         status = vp->lvalue;
226         if (status == PW_STATUS_ACCOUNTING_ON ||
227             status == PW_STATUS_ACCOUNTING_OFF) rb_record = 1;
228
229         /*
230          *      Translate the User-Name attribute, or whatever else
231          *      they told us to use.
232          */
233         *buffer = '\0';
234         radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
235
236         if (!rb_record &&
237             (*buffer != '\0')) do {
238                 int check1 = 0;
239                 int check2 = 0;
240
241                 /*
242                  *      ComOS (up to and including 3.5.1b20) does not send
243                  *      standard PW_STATUS_ACCOUNTING_XXX messages.
244                  *
245                  *      Check for:  o no Acct-Session-Time, or time of 0
246                  *                  o Acct-Session-Id of "00000000".
247                  *
248                  *      We could also check for NAS-Port, that attribute
249                  *      should NOT be present (but we don't right now).
250                  */
251                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
252                      == NULL || vp->lvalue == 0)
253                         check1 = 1;
254                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
255                      != NULL && vp->length == 8 &&
256                      memcmp(vp->strvalue, "00000000", 8) == 0)
257                         check2 = 1;
258                 if (check1 == 0 || check2 == 0) {
259 #if 0 /* Cisco sometimes sends START records without username. */
260                         radlog(L_ERR, "Accounting: no username in record");
261                         return RLM_MODULE_FAIL;
262 #else
263                         break;
264 #endif
265                 }
266                 radlog(L_INFO, "Accounting: converting reboot records.");
267                 if (status == PW_STATUS_STOP)
268                         status = PW_STATUS_ACCOUNTING_OFF;
269                 if (status == PW_STATUS_START)
270                         status = PW_STATUS_ACCOUNTING_ON;
271                 rb_record = 1;
272         } while(0);
273
274         time(&t);
275         memset(&ut, 0, sizeof(ut));
276         ut.porttype = 'A';
277
278         /*
279          *  Copy the previous translated user name.
280          */
281         strncpy(ut.login, buffer, RUT_NAMESIZE);
282
283         /*
284          *      First, find the interesting attributes.
285          */
286         for (vp = request->packet->vps; vp; vp = vp->next) {
287                 switch (vp->attribute) {
288                         case PW_LOGIN_IP_HOST:
289                         case PW_FRAMED_IP_ADDRESS:
290                                 framed_address = vp->lvalue;
291                                 ut.framed_address = vp->lvalue;
292                                 break;
293                         case PW_FRAMED_PROTOCOL:
294                                 protocol = vp->lvalue;
295                                 break;
296                         case PW_NAS_IP_ADDRESS:
297                                 nas_address = vp->lvalue;
298                                 ut.nas_address = vp->lvalue;
299                                 break;
300                         case PW_NAS_PORT_ID:
301                                 ut.nas_port = vp->lvalue;
302                                 port_seen = 1;
303                                 break;
304                         case PW_ACCT_DELAY_TIME:
305                                 ut.delay = vp->lvalue;
306                                 break;
307                         case PW_ACCT_SESSION_ID:
308                                 /*
309                                  *      If length > 8, only store the
310                                  *      last 8 bytes.
311                                  */
312                                 off = vp->length - sizeof(ut.session_id);
313                                 /*
314                                  *      Ascend is br0ken - it adds a \0
315                                  *      to the end of any string.
316                                  *      Compensate.
317                                  */
318                                 if (vp->length > 0 &&
319                                     vp->strvalue[vp->length - 1] == 0)
320                                         off--;
321                                 if (off < 0) off = 0;
322                                 memcpy(ut.session_id, vp->strvalue + off,
323                                         sizeof(ut.session_id));
324                                 break;
325                         case PW_NAS_PORT_TYPE:
326                                 if (vp->lvalue >= 0 && vp->lvalue <= 4)
327                                         ut.porttype = porttypes[vp->lvalue];
328                                 nas_port_type = vp->lvalue;
329                                 break;
330                         case PW_CALLING_STATION_ID:
331                                 if(inst->callerid_ok)
332                                         strNcpy(ut.caller_id,
333                                                 (char *)vp->strvalue,
334                                                 sizeof(ut.caller_id));
335                                 break;
336                 }
337         }
338
339         /*
340          *      If we didn't find out the NAS address, use the
341          *      originator's IP address.
342          */
343         if (nas_address == 0) {
344                 nas_address = request->packet->src_ipaddr;
345                 ut.nas_address = nas_address;
346         }
347
348         if (protocol == PW_PPP)
349                 ut.proto = 'P';
350         else if (protocol == PW_SLIP)
351                 ut.proto = 'S';
352         else
353                 ut.proto = 'T';
354         ut.time = t - ut.delay;
355
356         /*
357          *      See if this was a portmaster reboot.
358          */
359         if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
360                 radlog(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
361                         nas_name(nas_address));
362                 radutmp_zap(inst, nas_address, ut.time);
363                 return RLM_MODULE_OK;
364         }
365         if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
366                 radlog(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
367                         nas_name(nas_address));
368                 radutmp_zap(inst, nas_address, ut.time);
369                 return RLM_MODULE_OK;
370         }
371
372         /*
373          *      If we don't know this type of entry pretend we succeeded.
374          */
375         if (status != PW_STATUS_START &&
376             status != PW_STATUS_STOP &&
377             status != PW_STATUS_ALIVE) {
378                 radlog(L_ERR, "NAS %s port %d unknown packet type %d)",
379                         nas_name(nas_address), ut.nas_port, status);
380                 return RLM_MODULE_NOOP;
381         }
382
383         /*
384          *      Perhaps we don't want to store this record into
385          *      radutmp. We skip records:
386          *
387          *      - without a NAS-Port-Id (telnet / tcp access)
388          *      - with the username "!root" (console admin login)
389          */
390         if (!port_seen || strncmp(ut.login, "!root", RUT_NAMESIZE) == 0)
391                 return RLM_MODULE_NOOP;
392
393         /*
394          *      Enter into the radutmp file.
395          */
396         fd = open(inst->radutmp_fn, O_RDWR|O_CREAT, inst->permission);
397         if (fd >= 0) {
398                 NAS_PORT *cache;
399                 int r;
400
401                 /*
402                  *      Lock the utmp file, prefer lockf() over flock().
403                  */
404                 radutmp_lock(fd);
405
406                 /*
407                  *      Find the entry for this NAS / portno combination.
408                  */
409                 if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
410                                            ut.nas_port)) != NULL)
411                         lseek(fd, (off_t)cache->offset, SEEK_SET);
412
413                 r = 0;
414                 off = 0;
415                 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
416                         off += sizeof(u);
417                         if (u.nas_address != ut.nas_address ||
418                             u.nas_port    != ut.nas_port)
419                                 continue;
420
421                         if (status == PW_STATUS_STOP &&
422                             strncmp(ut.session_id, u.session_id,
423                              sizeof(u.session_id)) != 0) {
424                                 /*
425                                  *      Don't complain if this is not a
426                                  *      login record (some clients can
427                                  *      send _only_ logout records).
428                                  */
429                                 if (u.type == P_LOGIN)
430                                         radlog(L_ERR,
431                 "Accounting: logout: entry for NAS %s port %d has wrong ID",
432                                         nas_name(nas_address), u.nas_port);
433                                 r = -1;
434                                 break;
435                         }
436
437                         if (status == PW_STATUS_START &&
438                             strncmp(ut.session_id, u.session_id,
439                              sizeof(u.session_id)) == 0  &&
440                             u.time >= ut.time) {
441                                 if (u.type == P_LOGIN) {
442                                         radlog(L_INFO,
443                 "Accounting: login: entry for NAS %s port %d duplicate",
444                                         nas_name(nas_address), u.nas_port);
445                                         r = -1;
446                                         break;
447                                 }
448                                 radlog(L_ERR,
449                 "Accounting: login: entry for NAS %s port %d wrong order",
450                                 nas_name(nas_address), u.nas_port);
451                                 r = -1;
452                                 break;
453                         }
454
455                         /*
456                          *      FIXME: the ALIVE record could need
457                          *      some more checking, but anyway I'd
458                          *      rather rewrite this mess -- miquels.
459                          */
460                         if (status == PW_STATUS_ALIVE &&
461                             strncmp(ut.session_id, u.session_id,
462                              sizeof(u.session_id)) == 0  &&
463                             u.type == P_LOGIN) {
464                                 /*
465                                  *      Keep the original login time.
466                                  */
467                                 ut.time = u.time;
468                                 if (u.login[0] != 0)
469                                         just_an_update = 1;
470                         }
471
472                         if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
473                                 radlog(L_ERR, "Accounting: negative lseek!\n");
474                                 lseek(fd, (off_t)0, SEEK_SET);
475                                 off = 0;
476                         } else
477                                 off -= sizeof(u);
478                         r = 1;
479                         break;
480                 }
481
482                 if (r >= 0 &&  (status == PW_STATUS_START ||
483                                 status == PW_STATUS_ALIVE)) {
484                         if (cache == NULL) {
485                            cache = rad_malloc(sizeof(NAS_PORT));
486                            cache->nasaddr = ut.nas_address;
487                            cache->port = ut.nas_port;
488                            cache->offset = off;
489                            cache->next = inst->nas_port_list;
490                            inst->nas_port_list = cache;
491                         }
492                         ut.type = P_LOGIN;
493                         write(fd, &ut, sizeof(u));
494                 }
495                 if (status == PW_STATUS_STOP) {
496                         if (r > 0) {
497                                 u.type = P_IDLE;
498                                 u.time = ut.time;
499                                 u.delay = ut.delay;
500                                 write(fd, &u, sizeof(u));
501                         } else if (r == 0) {
502                                 radlog(L_ERR,
503                 "Accounting: logout: login entry for NAS %s port %d not found",
504                                 nas_name(nas_address), ut.nas_port);
505                                 r = -1;
506                         }
507                 }
508                 close(fd);
509         } else {
510                 radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
511                 return RLM_MODULE_FAIL;
512         }
513
514         return RLM_MODULE_OK;
515 }
516
517 /*
518  *      See if a user is already logged in. Sets request->simul_count to the
519  *      current session count for this user and sets request->simul_mpp to 2
520  *      if it looks like a multilink attempt based on the requested IP
521  *      address, otherwise leaves request->simul_mpp alone.
522  *
523  *      Check twice. If on the first pass the user exceeds his
524  *      max. number of logins, do a second pass and validate all
525  *      logins by querying the terminal server (using eg. SNMP).
526  */
527 static int radutmp_checksimul(void *instance, REQUEST *request)
528 {
529         struct radutmp  u;
530         int             fd;
531         VALUE_PAIR      *vp;
532         uint32_t        ipno = 0;
533         char            *call_num = NULL;
534         int             rcode;
535         struct radutmp_instance *inst = instance;
536         char            login[256];
537
538         if ((fd = open(inst->radutmp_fn, O_RDWR)) < 0) {
539                 if(errno!=ENOENT)
540                         return RLM_MODULE_FAIL;
541                 request->simul_count=0;
542                 return RLM_MODULE_OK;
543         }
544
545         *login = '\0';
546         radius_xlat(login, sizeof(login), inst->username, request, NULL);
547         if(*login == '\0') 
548                         return RLM_MODULE_FAIL;
549
550         request->simul_count = 0;
551         while(read(fd, &u, sizeof(u)) == sizeof(u)) {
552                 if (strncmp(login, u.login, RUT_NAMESIZE) == 0
553                     && u.type == P_LOGIN)
554                         ++request->simul_count;
555         }
556
557         if(request->simul_count < request->simul_max) {
558                 close(fd);
559                 return RLM_MODULE_OK;
560         }
561         lseek(fd, (off_t)0, SEEK_SET);
562
563         /*
564          *      Setup some stuff, like for MPP detection.
565          */
566         if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
567                 ipno = vp->lvalue;
568         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
569                 call_num = vp->strvalue;        
570
571         /*
572          *      lockf() the file while reading/writing.
573          */
574                 radutmp_lock(fd);
575
576         request->simul_count = 0;
577         while (read(fd, &u, sizeof(u)) == sizeof(u)) {
578                 if (strncmp(login, u.login, RUT_NAMESIZE) == 0
579                     && u.type == P_LOGIN) {
580                         char session_id[sizeof u.session_id+1];
581                         strNcpy(session_id, u.session_id, sizeof session_id);
582
583                         /*
584                          *      rad_check_ts may take seconds to return,
585                          *      and we don't want to block everyone else
586                          *      while that's happening.
587                          */
588                         radutmp_unlock(fd);
589                         rcode = rad_check_ts(u.nas_address, u.nas_port, login, 
590                                              session_id);
591                         radutmp_lock(fd);
592
593                         if (rcode == 1) {
594                                 ++request->simul_count;
595                                 /*
596                                  *      Does it look like a MPP attempt?
597                                  */
598                                 if (strchr("SCPA", u.proto) &&
599                                     ipno && u.framed_address == ipno)
600                                         request->simul_mpp = 2;
601                                 else if (strchr("SCPA", u.proto) && call_num &&
602                                         !strncmp(u.caller_id,call_num,16))
603                                         request->simul_mpp = 2;
604                         }
605                         else {
606                                 /*
607                                  *      False record - zap it.
608                                  */
609
610                                 session_zap(u.nas_address, u.nas_port, login,
611                                             session_id, u.framed_address,
612                                             u.proto, 0);
613                         }
614                 }
615         }
616         close(fd);
617
618         return RLM_MODULE_OK;
619 }
620
621 /* globally exported name */
622 module_t rlm_radutmp = {
623   "radutmp",
624   0,                            /* type: reserved */
625   NULL,                         /* initialization */
626   radutmp_instantiate,          /* instantiation */
627   {
628           NULL,                 /* authentication */
629           NULL,                 /* authorization */
630           NULL,                 /* preaccounting */
631           radutmp_accounting,   /* accounting */
632           radutmp_checksimul    /* checksimul */
633   },
634   radutmp_detach,               /* detach */
635   NULL,                         /* destroy */
636 };
637