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