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