Removed all free(inst->foo) where "foo" comes from parsing
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * FIXME add copyrights
22  */
23
24 #include        <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include        <freeradius-devel/autoconf.h>
28
29 #include        <sys/types.h>
30 #include        <stdio.h>
31 #include        <string.h>
32 #include        <stdlib.h>
33 #include        <unistd.h>
34 #include        <fcntl.h>
35 #include        <time.h>
36 #include        <errno.h>
37 #include        <limits.h>
38
39 #include "config.h"
40
41 #include        <freeradius-devel/radiusd.h>
42 #include        <freeradius-devel/radutmp.h>
43 #include        <freeradius-devel/modules.h>
44 #include        <freeradius-devel/rad_assert.h>
45
46 #define LOCK_LEN sizeof(struct radutmp)
47
48 static const char porttypes[] = "ASITX";
49
50 /*
51  *      used for caching radutmp lookups in the accounting component. The
52  *      session (checksimul) component doesn't use it, but probably should.
53  */
54 typedef struct nas_port {
55         uint32_t                nasaddr;
56         unsigned int    port;
57         off_t                   offset;
58         struct nas_port         *next;
59 } NAS_PORT;
60
61 typedef struct rlm_radutmp_t {
62         NAS_PORT        *nas_port_list;
63         char            *filename;
64         char            *username;
65         int             case_sensitive;
66         int             check_nas;
67         int             permission;
68         int             callerid_ok;
69 } rlm_radutmp_t;
70
71 static const CONF_PARSER module_config[] = {
72         { "filename", PW_TYPE_STRING_PTR,
73           offsetof(rlm_radutmp_t,filename), NULL,  RADUTMP },
74         { "username", PW_TYPE_STRING_PTR,
75           offsetof(rlm_radutmp_t,username), NULL,  "%{User-Name}"},
76         { "case_sensitive", PW_TYPE_BOOLEAN,
77           offsetof(rlm_radutmp_t,case_sensitive), NULL,  "yes"},
78         { "check_with_nas", PW_TYPE_BOOLEAN,
79           offsetof(rlm_radutmp_t,check_nas), NULL,  "yes"},
80         { "perm",     PW_TYPE_INTEGER,
81           offsetof(rlm_radutmp_t,permission), NULL,  "0644" },
82         { "callerid", PW_TYPE_BOOLEAN,
83           offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
84         { NULL, -1, 0, NULL, NULL }             /* end the list */
85 };
86
87 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
88 {
89         rlm_radutmp_t *inst;
90
91         inst = rad_malloc(sizeof(*inst));
92         if (!inst) {
93                 return -1;
94         }
95         memset(inst, 0, sizeof(*inst));
96
97         if (cf_section_parse(conf, inst, module_config)) {
98                 free(inst);
99                 return -1;
100         }
101
102         inst->nas_port_list = NULL;
103         *instance = inst;
104         return 0;
105 }
106
107
108 /*
109  *      Detach.
110  */
111 static int radutmp_detach(void *instance)
112 {
113         NAS_PORT *p, *next;
114         rlm_radutmp_t *inst = instance;
115
116         for (p = inst->nas_port_list ; p ; p=next) {
117                 next = p->next;
118                 free(p);
119         }
120         free(inst);
121         return 0;
122 }
123
124 /*
125  *      Zap all users on a NAS from the radutmp file.
126  */
127 static int radutmp_zap(rlm_radutmp_t *inst,
128                        const char *filename,
129                        uint32_t nasaddr,
130                        time_t t)
131 {
132         struct radutmp  u;
133         int             fd;
134
135         if (t == 0) time(&t);
136
137         fd = open(filename, O_RDWR);
138         if (fd < 0) {
139                 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
140                        filename, strerror(errno));
141                 return RLM_MODULE_FAIL;
142         }
143
144         /*
145          *      Lock the utmp file, prefer lockf() over flock().
146          */
147         rad_lockfd(fd, LOCK_LEN);
148
149         /*
150          *      Find the entry for this NAS / portno combination.
151          */
152         while (read(fd, &u, sizeof(u)) == sizeof(u)) {
153           if ((nasaddr != 0 && nasaddr != u.nas_address) ||
154               u.type != P_LOGIN)
155             continue;
156           /*
157            *    Match. Zap it.
158            */
159           if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
160             radlog(L_ERR, "rlm_radutmp: radutmp_zap: negative lseek!");
161             lseek(fd, (off_t)0, SEEK_SET);
162           }
163           u.type = P_IDLE;
164           u.time = t;
165           write(fd, &u, sizeof(u));
166         }
167         close(fd);      /* and implicitely release the locks */
168
169         return 0;
170 }
171
172 /*
173  *      Lookup a NAS_PORT in the nas_port_list
174  */
175 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
176 {
177         NAS_PORT        *cl;
178
179         for(cl = nas_port_list; cl; cl = cl->next)
180                 if (nasaddr == cl->nasaddr &&
181                         port == cl->port)
182                         break;
183         return cl;
184 }
185
186
187 /*
188  *      Store logins in the RADIUS utmp file.
189  */
190 static int radutmp_accounting(void *instance, REQUEST *request)
191 {
192         struct radutmp  ut, u;
193         VALUE_PAIR      *vp;
194         int             status = -1;
195         uint32_t        nas_address = 0;
196         uint32_t        framed_address = 0;
197         int             protocol = -1;
198         time_t          t;
199         int             fd;
200         int             just_an_update = 0;
201         int             port_seen = 0;
202         int             nas_port_type = 0;
203         int             off;
204         rlm_radutmp_t   *inst = instance;
205         char            buffer[256];
206         char            filename[1024];
207         char            ip_name[32]; /* 255.255.255.255 */
208         const char      *nas;
209         NAS_PORT        *cache;
210         int             r;
211
212         if (request->packet->src_ipaddr.af != AF_INET) {
213                 DEBUG("rlm_radutmp: IPv6 not supported!");
214                 return RLM_MODULE_NOOP;
215         }
216
217         /*
218          *      Which type is this.
219          */
220         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
221                 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
222                 return RLM_MODULE_NOOP;
223         }
224         status = vp->lvalue;
225
226         /*
227          *      Look for weird reboot packets.
228          *
229          *      ComOS (up to and including 3.5.1b20) does not send
230          *      standard PW_STATUS_ACCOUNTING_XXX messages.
231          *
232          *      Check for:  o no Acct-Session-Time, or time of 0
233          *                  o Acct-Session-Id of "00000000".
234          *
235          *      We could also check for NAS-Port, that attribute
236          *      should NOT be present (but we don't right now).
237          */
238         if ((status != PW_STATUS_ACCOUNTING_ON) &&
239             (status != PW_STATUS_ACCOUNTING_OFF)) do {
240                 int check1 = 0;
241                 int check2 = 0;
242
243                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
244                      == NULL || vp->lvalue == 0)
245                         check1 = 1;
246                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
247                      != NULL && vp->length == 8 &&
248                      memcmp(vp->vp_strvalue, "00000000", 8) == 0)
249                         check2 = 1;
250                 if (check1 == 0 || check2 == 0) {
251 #if 0 /* Cisco sometimes sends START records without username. */
252                         radlog(L_ERR, "rlm_radutmp: no username in record");
253                         return RLM_MODULE_FAIL;
254 #else
255                         break;
256 #endif
257                 }
258                 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
259                 if (status == PW_STATUS_STOP)
260                         status = PW_STATUS_ACCOUNTING_OFF;
261                 if (status == PW_STATUS_START)
262                         status = PW_STATUS_ACCOUNTING_ON;
263         } while(0);
264
265         time(&t);
266         memset(&ut, 0, sizeof(ut));
267         ut.porttype = 'A';
268
269         /*
270          *      First, find the interesting attributes.
271          */
272         for (vp = request->packet->vps; vp; vp = vp->next) {
273                 switch (vp->attribute) {
274                         case PW_LOGIN_IP_HOST:
275                         case PW_FRAMED_IP_ADDRESS:
276                                 framed_address = vp->lvalue;
277                                 ut.framed_address = vp->lvalue;
278                                 break;
279                         case PW_FRAMED_PROTOCOL:
280                                 protocol = vp->lvalue;
281                                 break;
282                         case PW_NAS_IP_ADDRESS:
283                                 nas_address = vp->lvalue;
284                                 ut.nas_address = vp->lvalue;
285                                 break;
286                         case PW_NAS_PORT:
287                                 ut.nas_port = vp->lvalue;
288                                 port_seen = 1;
289                                 break;
290                         case PW_ACCT_DELAY_TIME:
291                                 ut.delay = vp->lvalue;
292                                 break;
293                         case PW_ACCT_SESSION_ID:
294                                 /*
295                                  *      If length > 8, only store the
296                                  *      last 8 bytes.
297                                  */
298                                 off = vp->length - sizeof(ut.session_id);
299                                 /*
300                                  *      Ascend is br0ken - it adds a \0
301                                  *      to the end of any string.
302                                  *      Compensate.
303                                  */
304                                 if (vp->length > 0 &&
305                                     vp->vp_strvalue[vp->length - 1] == 0)
306                                         off--;
307                                 if (off < 0) off = 0;
308                                 memcpy(ut.session_id, vp->vp_strvalue + off,
309                                         sizeof(ut.session_id));
310                                 break;
311                         case PW_NAS_PORT_TYPE:
312                                 if (vp->lvalue <= 4)
313                                         ut.porttype = porttypes[vp->lvalue];
314                                 nas_port_type = vp->lvalue;
315                                 break;
316                         case PW_CALLING_STATION_ID:
317                                 if(inst->callerid_ok)
318                                         strlcpy(ut.caller_id,
319                                                 (char *)vp->vp_strvalue,
320                                                 sizeof(ut.caller_id));
321                                 break;
322                 }
323         }
324
325         /*
326          *      If we didn't find out the NAS address, use the
327          *      originator's IP address.
328          */
329         if (nas_address == 0) {
330                 nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
331                 ut.nas_address = nas_address;
332                 nas = client_name_old(&request->packet->src_ipaddr); /* MUST be a valid client */
333
334         } else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == nas_address) {          /* might be a client, might not be. */
335                 RADCLIENT *cl;
336                 
337                 /*
338                  *      Hack like 'client_name()', but with sane
339                  *      fall-back.
340                  */
341                 cl = client_find_old(&request->packet->src_ipaddr);
342                 if (!cl) rad_assert(0 == 1); /* WTF? */
343                 if (cl->shortname && cl->shortname[0]) {
344                         nas = cl->shortname;
345                 } else {
346                         nas = cl->longname;
347                 }
348         } else {
349                 /*
350                  *      The NAS isn't a client, it's behind
351                  *      a proxy server.  In that case, just
352                  *      get the IP address.
353                  */
354                 nas = ip_ntoa(ip_name, nas_address);
355         }
356
357         /*
358          *      Set the protocol field.
359          */
360         if (protocol == PW_PPP)
361                 ut.proto = 'P';
362         else if (protocol == PW_SLIP)
363                 ut.proto = 'S';
364         else
365                 ut.proto = 'T';
366         ut.time = t - ut.delay;
367
368         /*
369          *      Get the utmp filename, via xlat.
370          */
371         radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
372
373         /*
374          *      See if this was a reboot.
375          *
376          *      Hmm... we may not want to zap all of the users when
377          *      the NAS comes up, because of issues with receiving
378          *      UDP packets out of order.
379          */
380         if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
381                 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
382                        nas);
383                 radutmp_zap(inst, filename, nas_address, ut.time);
384                 return RLM_MODULE_OK;
385         }
386
387         if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
388                 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
389                        nas);
390                 radutmp_zap(inst, filename, nas_address, ut.time);
391                 return RLM_MODULE_OK;
392         }
393
394         /*
395          *      If we don't know this type of entry pretend we succeeded.
396          */
397         if (status != PW_STATUS_START &&
398             status != PW_STATUS_STOP &&
399             status != PW_STATUS_ALIVE) {
400                 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d)",
401                        nas, ut.nas_port, status);
402                 return RLM_MODULE_NOOP;
403         }
404
405         /*
406          *      Translate the User-Name attribute, or whatever else
407          *      they told us to use.
408          */
409         *buffer = '\0';
410         radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
411
412         /*
413          *  Copy the previous translated user name.
414          */
415         strlcpy(ut.login, buffer, RUT_NAMESIZE);
416
417         /*
418          *      Perhaps we don't want to store this record into
419          *      radutmp. We skip records:
420          *
421          *      - without a NAS-Port (telnet / tcp access)
422          *      - with the username "!root" (console admin login)
423          */
424         if (!port_seen) {
425                 DEBUG2("  rlm_radutmp: No NAS-Port seen.  Cannot do anything.");
426                 DEBUG2("  rlm_radumtp: WARNING: checkrad will probably not work!");
427                 return RLM_MODULE_NOOP;
428         }
429
430         if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
431                 DEBUG2("  rlm_radutmp: Not recording administrative user");
432
433                 return RLM_MODULE_NOOP;
434         }
435
436         /*
437          *      Enter into the radutmp file.
438          */
439         fd = open(filename, O_RDWR|O_CREAT, inst->permission);
440         if (fd < 0) {
441                 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
442                        filename, strerror(errno));
443                 return RLM_MODULE_FAIL;
444         }
445
446         /*
447          *      Lock the utmp file, prefer lockf() over flock().
448          */
449         rad_lockfd(fd, LOCK_LEN);
450
451         /*
452          *      Find the entry for this NAS / portno combination.
453          */
454         if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
455                                    ut.nas_port)) != NULL) {
456                 lseek(fd, (off_t)cache->offset, SEEK_SET);
457         }
458
459         r = 0;
460         off = 0;
461         while (read(fd, &u, sizeof(u)) == sizeof(u)) {
462                 off += sizeof(u);
463                 if (u.nas_address != ut.nas_address ||
464                     u.nas_port    != ut.nas_port)
465                         continue;
466
467                 /*
468                  *      Don't compare stop records to unused entries.
469                  */
470                 if (status == PW_STATUS_STOP &&
471                     u.type == P_IDLE) {
472                         continue;
473                 }
474
475                 if (status == PW_STATUS_STOP &&
476                     strncmp(ut.session_id, u.session_id,
477                             sizeof(u.session_id)) != 0) {
478                         /*
479                          *      Don't complain if this is not a
480                          *      login record (some clients can
481                          *      send _only_ logout records).
482                          */
483                         if (u.type == P_LOGIN)
484                                 radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has wrong ID",
485                                        nas, u.nas_port);
486                         r = -1;
487                         break;
488                 }
489
490                 if (status == PW_STATUS_START &&
491                     strncmp(ut.session_id, u.session_id,
492                             sizeof(u.session_id)) == 0  &&
493                     u.time >= ut.time) {
494                         if (u.type == P_LOGIN) {
495                                 radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
496                                        nas, u.nas_port);
497                                 r = -1;
498                                 break;
499                         }
500                         radlog(L_ERR, "rlm_radutmp: Login entry for NAS %s port %u wrong order",
501                                nas, u.nas_port);
502                         r = -1;
503                         break;
504                 }
505
506                 /*
507                  *      FIXME: the ALIVE record could need
508                  *      some more checking, but anyway I'd
509                  *      rather rewrite this mess -- miquels.
510                  */
511                 if (status == PW_STATUS_ALIVE &&
512                     strncmp(ut.session_id, u.session_id,
513                             sizeof(u.session_id)) == 0  &&
514                     u.type == P_LOGIN) {
515                         /*
516                          *      Keep the original login time.
517                          */
518                         ut.time = u.time;
519                         if (u.login[0] != 0)
520                                 just_an_update = 1;
521                 }
522
523                 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
524                         radlog(L_ERR, "rlm_radutmp: negative lseek!");
525                         lseek(fd, (off_t)0, SEEK_SET);
526                         off = 0;
527                 } else
528                         off -= sizeof(u);
529                 r = 1;
530                 break;
531         } /* read the file until we find a match */
532
533         /*
534          *      Found the entry, do start/update it with
535          *      the information from the packet.
536          */
537         if (r >= 0 &&  (status == PW_STATUS_START ||
538                         status == PW_STATUS_ALIVE)) {
539                 /*
540                  *      Remember where the entry was, because it's
541                  *      easier than searching through the entire file.
542                  */
543                 if (cache == NULL) {
544                         cache = rad_malloc(sizeof(NAS_PORT));
545                         cache->nasaddr = ut.nas_address;
546                         cache->port = ut.nas_port;
547                         cache->offset = off;
548                         cache->next = inst->nas_port_list;
549                         inst->nas_port_list = cache;
550                 }
551
552                 ut.type = P_LOGIN;
553                 write(fd, &ut, sizeof(u));
554         }
555
556         /*
557          *      The user has logged off, delete the entry by
558          *      re-writing it in place.
559          */
560         if (status == PW_STATUS_STOP) {
561                 if (r > 0) {
562                         u.type = P_IDLE;
563                         u.time = ut.time;
564                         u.delay = ut.delay;
565                         write(fd, &u, sizeof(u));
566                 } else if (r == 0) {
567                         radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
568                                nas, ut.nas_port);
569                         r = -1;
570                 }
571         }
572         close(fd);      /* and implicitely release the locks */
573
574         return RLM_MODULE_OK;
575 }
576
577 /*
578  *      See if a user is already logged in. Sets request->simul_count to the
579  *      current session count for this user and sets request->simul_mpp to 2
580  *      if it looks like a multilink attempt based on the requested IP
581  *      address, otherwise leaves request->simul_mpp alone.
582  *
583  *      Check twice. If on the first pass the user exceeds his
584  *      max. number of logins, do a second pass and validate all
585  *      logins by querying the terminal server (using eg. SNMP).
586  */
587 static int radutmp_checksimul(void *instance, REQUEST *request)
588 {
589         struct radutmp  u;
590         int             fd;
591         VALUE_PAIR      *vp;
592         uint32_t        ipno = 0;
593         char            *call_num = NULL;
594         int             rcode;
595         rlm_radutmp_t   *inst = instance;
596         char            login[256];
597         char            filename[1024];
598
599         /*
600          *      Get the filename, via xlat.
601          */
602         radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
603
604         if ((fd = open(filename, O_RDWR)) < 0) {
605                 /*
606                  *      If the file doesn't exist, then no users
607                  *      are logged in.
608                  */
609                 if (errno == ENOENT) {
610                         request->simul_count=0;
611                         return RLM_MODULE_OK;
612                 }
613
614                 /*
615                  *      Error accessing the file.
616                  */
617                 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
618                        filename, strerror(errno));
619                 return RLM_MODULE_FAIL;
620         }
621
622         *login = '\0';
623         radius_xlat(login, sizeof(login), inst->username, request, NULL);
624         if (!*login) {
625                 return RLM_MODULE_NOOP;
626         }
627
628         /*
629          *      WTF?  This is probably wrong... we probably want to
630          *      be able to check users across multiple session accounting
631          *      methods.
632          */
633         request->simul_count = 0;
634
635         /*
636          *      Loop over utmp, counting how many people MAY be logged in.
637          */
638         while (read(fd, &u, sizeof(u)) == sizeof(u)) {
639                 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
640                      (!inst->case_sensitive &&
641                       (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
642                     (u.type == P_LOGIN)) {
643                         ++request->simul_count;
644                 }
645         }
646
647         /*
648          *      The number of users logged in is OK,
649          *      OR, we've been told to not check the NAS.
650          */
651         if ((request->simul_count < request->simul_max) ||
652             !inst->check_nas) {
653                 close(fd);
654                 return RLM_MODULE_OK;
655         }
656         lseek(fd, (off_t)0, SEEK_SET);
657
658         /*
659          *      Setup some stuff, like for MPP detection.
660          */
661         if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
662                 ipno = vp->lvalue;
663         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
664                 call_num = vp->vp_strvalue;
665
666         /*
667          *      lock the file while reading/writing.
668          */
669         rad_lockfd(fd, LOCK_LEN);
670
671         /*
672          *      FIXME: If we get a 'Start' for a user/nas/port which is
673          *      listed, but for which we did NOT get a 'Stop', then
674          *      it's not a duplicate session.  This happens with
675          *      static IP's like DSL.
676          */
677         request->simul_count = 0;
678         while (read(fd, &u, sizeof(u)) == sizeof(u)) {
679                 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
680                      (!inst->case_sensitive &&
681                       (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
682                     (u.type == P_LOGIN)) {
683                         char session_id[sizeof(u.session_id) + 1];
684                         char utmp_login[sizeof(u.login) + 1];
685
686                         strlcpy(session_id, u.session_id, sizeof(session_id));
687
688                         /*
689                          *      The login name MAY fill the whole field,
690                          *      and thus won't be zero-filled.
691                          *
692                          *      Note that we take the user name from
693                          *      the utmp file, as that's the canonical
694                          *      form.  The 'login' variable may contain
695                          *      a string which is an upper/lowercase
696                          *      version of u.login.  When we call the
697                          *      routine to check the terminal server,
698                          *      the NAS may be case sensitive.
699                          *
700                          *      e.g. We ask if "bob" is using a port,
701                          *      and the NAS says "no", because "BOB"
702                          *      is using the port.
703                          */
704                         strlcpy(utmp_login, u.login, sizeof(u.login));
705
706                         /*
707                          *      rad_check_ts may take seconds
708                          *      to return, and we don't want
709                          *      to block everyone else while
710                          *      that's happening.  */
711                         rad_unlockfd(fd, LOCK_LEN);
712                         rcode = rad_check_ts(u.nas_address, u.nas_port,
713                                              utmp_login, session_id);
714                         rad_lockfd(fd, LOCK_LEN);
715
716                         if (rcode == 0) {
717                                 /*
718                                  *      Stale record - zap it.
719                                  */
720                                 session_zap(request, u.nas_address,
721                                             u.nas_port, login, session_id,
722                                             u.framed_address, u.proto,0);
723                         }
724                         else if (rcode == 1) {
725                                 /*
726                                  *      User is still logged in.
727                                  */
728                                 ++request->simul_count;
729
730                                 /*
731                                  *      Does it look like a MPP attempt?
732                                  */
733                                 if (strchr("SCPA", u.proto) &&
734                                     ipno && u.framed_address == ipno)
735                                         request->simul_mpp = 2;
736                                 else if (strchr("SCPA", u.proto) && call_num &&
737                                         !strncmp(u.caller_id,call_num,16))
738                                         request->simul_mpp = 2;
739                         }
740                         else {
741                                 /*
742                                  *      Failed to check the terminal
743                                  *      server for duplicate logins:
744                                  *      Return an error.
745                                  */
746                                 close(fd);
747                                 radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
748                                 return RLM_MODULE_FAIL;
749                         }
750                 }
751         }
752         close(fd);              /* and implicitely release the locks */
753
754         return RLM_MODULE_OK;
755 }
756
757 /* globally exported name */
758 module_t rlm_radutmp = {
759         RLM_MODULE_INIT,
760         "radutmp",
761         0,                            /* type: reserved */
762         radutmp_instantiate,          /* instantiation */
763         radutmp_detach,               /* detach */
764         {
765                 NULL,                 /* authentication */
766                 NULL,                 /* authorization */
767                 NULL,                 /* preaccounting */
768                 radutmp_accounting,   /* accounting */
769                 radutmp_checksimul,     /* checksimul */
770                 NULL,                   /* pre-proxy */
771                 NULL,                   /* post-proxy */
772                 NULL                    /* post-auth */
773         },
774 };
775