update otp_hotp() to support 6,7,8,9 digit otp's
[freeradius.git] / src / modules / rlm_radutmp / rlm_radutmp2.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,2001,2002,2003,2004  The FreeRADIUS server project
21  */
22
23 #include        <freeradius-devel/autoconf.h>
24
25 #include        <sys/types.h>
26 #include        <stdio.h>
27 #include        <string.h>
28 #include        <stdlib.h>
29 #include        <unistd.h>
30 #include        <fcntl.h>
31 #include        <time.h>
32 #include        <errno.h>
33 #include        <limits.h>
34
35 #include "config.h"
36
37 #include        <freeradius-devel/radiusd.h>
38 #include        <freeradius-devel/radutmp.h>
39 #include        <freeradius-devel/modules.h>
40 #include        <freeradius-devel/rad_assert.h>
41
42 #define LOCK_LEN sizeof(struct radutmp)
43
44 static const char porttypes[] = "ASITX";
45
46 /*
47  *      Used for caching radutmp lookups in the accounting
48  *      component. The session (checksimul) component doesn't use it,
49  *      but probably should, though we're not sure how...
50  *
51  *      The intent here is to keep this structure as small as
52  *      possible, so that it doesn't take up too much memory.
53  */
54 typedef struct nas_port {
55         uint32_t                nas_address;
56         unsigned int            nas_port;
57         off_t                   offset;
58
59         struct nas_port         *next; /* for the free list */
60 } NAS_PORT;
61
62
63 /*
64  *      Per-file information.
65  *
66  *      Hmm... having multiple filenames managed by one instance
67  *      of the module makes it difficult for the module to do
68  *      simultaneous-use checking, without more code edits.
69  */
70 typedef struct radutmp_cache_t {
71         const char      *filename; /* for future reference */
72         time_t          last_used; /* for future reference */
73
74         rbtree_t        *nas_ports;
75         NAS_PORT        *free_offsets;
76         off_t           max_offset;
77         int             cached_file;
78         int             permission;
79 #ifdef HAVE_PTHREAD_H
80         pthread_mutex_t mutex;
81 #endif
82 } radutmp_cache_t;
83
84
85 /*
86  *      We cache the users, too, so that we only have to read radutmp
87  *      once.
88  */
89 typedef struct radutmp_simul_t {
90         char            login[sizeof(((struct radutmp *) NULL)->login) + 1];
91         int             simul_count;
92 } radutmp_simul_t;
93
94
95 /*
96  *      Data we store per module.
97  */
98 typedef struct rlm_radutmp_t {
99         char            *filename;
100         char            *username;
101         int             case_sensitive;
102         int             check_nas;
103         int             permission;
104         int             callerid_ok;
105
106         rbtree_t        *user_tree; /* for simultaneous-use */
107
108         /*
109          *      As the filenames can be dynamically translated,
110          *      we want to keep track of them in a separate data
111          *      structure, so that we can have per-file caches.
112          */
113         radutmp_cache_t cache;
114 } rlm_radutmp_t;
115
116 #ifndef HAVE_PTHREAD_H
117 /*
118  *      This is easier than ifdef's throughout the code.
119  */
120 #define pthread_mutex_init(_x, _y)
121 #define pthread_mutex_destroy(_x)
122 #define pthread_mutex_lock(_x)
123 #define pthread_mutex_unlock(_x)
124 #endif
125
126 static const CONF_PARSER module_config[] = {
127         { "filename", PW_TYPE_STRING_PTR,
128           offsetof(rlm_radutmp_t,filename), NULL,  RADUTMP },
129         { "username", PW_TYPE_STRING_PTR,
130           offsetof(rlm_radutmp_t,username), NULL,  "%{User-Name}"},
131         { "case_sensitive", PW_TYPE_BOOLEAN,
132           offsetof(rlm_radutmp_t,case_sensitive), NULL,  "yes"},
133         { "check_with_nas", PW_TYPE_BOOLEAN,
134           offsetof(rlm_radutmp_t,check_nas), NULL,  "yes"},
135         { "perm",     PW_TYPE_INTEGER,
136           offsetof(rlm_radutmp_t,permission), NULL,  "0644" },
137         { "callerid", PW_TYPE_BOOLEAN,
138           offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
139         { NULL, -1, 0, NULL, NULL }             /* end the list */
140 };
141
142
143 /*
144  *      NAS PORT cmp
145  */
146 static int nas_port_cmp(const void *a, const void *b)
147 {
148         const NAS_PORT *one = a;
149         const NAS_PORT *two = b;
150
151         if (one->nas_address < two->nas_address) return -1;
152         if (one->nas_address > two->nas_address) return +1;
153
154         if (one->nas_port < two->nas_port) return -1;
155         if (one->nas_port > two->nas_port) return +1;
156
157         return 0;
158 }
159
160
161 /*
162  *      Compare two user names.
163  */
164 static int user_cmp(const void *a, const void *b)
165 {
166         const radutmp_simul_t *one = a;
167         const radutmp_simul_t *two = b;
168
169         return strcmp(one->login, two->login);
170 }
171
172
173 /*
174  *      Compare two user names, case insensitive.
175  */
176 static int user_case_cmp(const void *a, const void *b)
177 {
178         const radutmp_simul_t *one = a;
179         const radutmp_simul_t *two = b;
180
181         return strcasecmp(one->login, two->login);
182 }
183
184
185 /*
186  *      Detach.
187  */
188 static int radutmp_detach(void *instance)
189 {
190         NAS_PORT        *this, *next;
191         rlm_radutmp_t *inst = instance;
192
193         rbtree_free(inst->cache.nas_ports);
194
195         for (this = inst->cache.free_offsets;
196              this != NULL;
197              this = next) {
198                 next = this->next;
199                 free(this);
200         }
201
202         if (inst->cache.filename) free(inst->cache.filename);
203
204         pthread_mutex_destroy(&(inst->cache.mutex));
205
206         if (inst->filename) free(inst->filename);
207         if (inst->username) free(inst->username);
208
209         rbtree_free(inst->user_tree);
210
211         free(inst);
212         return 0;
213 }
214
215
216 /*
217  *      Instantiate.
218  */
219 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
220 {
221         rlm_radutmp_t *inst;
222
223         inst = rad_malloc(sizeof(*inst));
224         if (!inst) {
225                 return -1;
226         }
227         memset(inst, 0, sizeof(*inst));
228
229         if (cf_section_parse(conf, inst, module_config)) {
230                 radutmp_detach(inst);
231                 return -1;
232         }
233
234         inst->cache.nas_ports = rbtree_create(nas_port_cmp, free, 0);
235         if (!inst->cache.nas_ports) {
236                 radlog(L_ERR, "rlm_radutmp: Failed to create nas tree");
237                 radutmp_detach(inst);
238                 return -1;
239         }
240
241         pthread_mutex_init(&(inst->cache.mutex), NULL);
242         inst->cache.permission = inst->permission;
243
244         if (inst->case_sensitive) {
245                 inst->user_tree = rbtree_create(user_cmp, free, 0);
246         } else {
247                 inst->user_tree = rbtree_create(user_case_cmp, free, 0);
248         }
249         if (!inst->user_tree) {
250                 radlog(L_ERR, "rlm_radutmp: Failed to create user tree");
251                 radutmp_detach(inst);
252                 return -1;
253         }
254
255         *instance = inst;
256         return 0;
257 }
258
259
260 /*
261  *      Reset the cached entries.
262  */
263 static int cache_reset(rlm_radutmp_t *inst, radutmp_cache_t *cache)
264 {
265         NAS_PORT *this, *next;
266         
267         /*
268          *      Cache is already reset, do nothing.
269          */
270         if ((rbtree_num_elements(cache->nas_ports) == 0) &&
271             (cache->free_offsets == NULL)) {
272                 DEBUG2("  rlm_radutmp: Not resetting the cache");
273                 return 1;
274         }
275         DEBUG2("  rlm_radutmp: Resetting the cache");
276
277         pthread_mutex_lock(&cache->mutex);
278
279         rbtree_free(inst->user_tree);
280
281         rbtree_free(cache->nas_ports);
282         
283         for (this = cache->free_offsets;
284              this != NULL;
285              this = next) {
286                 next = this->next;
287                 free(this);
288         }
289         cache->free_offsets = NULL;
290
291         /*
292          *      Re-create the caches.
293          */
294         cache->nas_ports = rbtree_create(nas_port_cmp, free, 0);
295         if (!cache->nas_ports) {
296                 pthread_mutex_unlock(&cache->mutex);
297                 radlog(L_ERR, "rlm_radutmp: No memory");
298                 return 0;
299         }
300         
301         cache->max_offset = 0;
302         
303         cache->cached_file = 1;
304
305         if (inst->case_sensitive) {
306                 inst->user_tree = rbtree_create(user_cmp, free, 0);
307         } else {
308                 inst->user_tree = rbtree_create(user_case_cmp, free, 0);
309         }
310         if (!inst->user_tree) {
311                 pthread_mutex_unlock(&cache->mutex);
312                 radlog(L_ERR, "rlm_radutmp: No memory");
313                 return 0;
314         }
315
316         pthread_mutex_unlock(&cache->mutex);
317
318         return 1;
319 }
320
321
322 /*
323  *      Compare two offsets in a tree.
324  */
325 static int offset_cmp(const void *a, const void *b)
326 {
327         const NAS_PORT *one = a;
328         const NAS_PORT *two = b;
329
330         if (one->offset < two->offset) return -1;
331         if (one->offset > two->offset) return +1;
332
333         return 0;
334 }
335
336
337 /*
338  *      Data structure to use when walking the trees, for zap.
339  */
340 typedef struct offset_walk_t {
341         rlm_radutmp_t   *inst;
342         radutmp_cache_t *cache;
343         rbtree_t        *offset_tree;
344         uint32_t        nas_address;
345         int             fd;
346         time_t          now;
347 } offset_walk_t;
348
349
350 /*
351  *      Walk over the cache, finding entries with the matching NAS IP address.
352  */
353 static int nas_port_walk(void *context, void *data)
354 {
355         offset_walk_t   *walk = context;
356         NAS_PORT        *nas_port = data;
357
358         /*
359          *      Doesn't match, keep going.
360          */
361         if (walk->nas_address != nas_port->nas_address) return 0;
362
363         /*
364          *      Insert it into the offset tree, for later deletion.
365          */
366         if (rbtree_insert(walk->offset_tree, nas_port) != 1) {
367                 DEBUG2("  rlm_radumtp: Insertion failed in nas port walk.");
368                 return 1;
369         }
370
371         return 0;
372 }
373
374
375 /*
376  *      Walk through the offset tree, operating on the cache
377  */
378 static int offset_walk(void *context, void *data)
379 {
380         offset_walk_t   *walk = context;
381         NAS_PORT        *nas_port = data;
382         struct radutmp  utmp;
383         radutmp_simul_t *user, myUser;
384
385         /*
386          *      Seek to the entry, and possibly re-write it.
387          */
388         if (lseek(walk->fd, nas_port->offset, SEEK_SET) < 0) {
389                 rad_assert(0 == 1);
390         }
391
392         if (read(walk->fd, &utmp, sizeof(utmp)) != sizeof(utmp)) {
393                 rad_assert(0 == 1);
394         }
395         
396         /*
397          *      If the entry in the file is NEWER than the reboot
398          *      packet, don't re-write it, and don't delete it.
399          */
400         if (utmp.time > walk->now) {
401                 return 0;
402         }
403
404         utmp.type = P_IDLE;
405         utmp.time = walk->now;
406
407         if (lseek(walk->fd, -(off_t)sizeof(utmp), SEEK_CUR) < 0) {
408                 radlog(L_ERR, "rlm_radutmp: offset_walk: failed in lseek: %s",
409                        strerror(errno));
410                 return 1;
411         }
412
413         write(walk->fd, &utmp, sizeof(utmp));
414
415         strNcpy(myUser.login, utmp.login, sizeof(myUser.login));
416         user = rbtree_finddata(walk->inst->user_tree, &myUser);
417         rad_assert(user != NULL);
418         rad_assert(user->simul_count > 0);
419         user->simul_count--;
420         if (user->simul_count == 0) {
421                 rbtree_deletebydata(walk->inst->user_tree, user);
422         }
423
424         if (rbtree_deletebydata(walk->cache->nas_ports, nas_port) == 0) {
425                 radlog(L_ERR, "rlm_radutmp: Failed to delete entry from cache");
426                 return 1;
427         }
428
429         /*
430          *      Insert the entry into the free list.
431          */
432         nas_port->next = walk->cache->free_offsets;
433         walk->cache->free_offsets = nas_port;
434
435         return 0;
436 }
437
438
439 /*
440  *      Zap all users on a NAS from the radutmp file.
441  */
442 static int radutmp_zap(rlm_radutmp_t *inst,
443                        radutmp_cache_t *cache,
444                        uint32_t nas_address,
445                        time_t now)
446 {
447         int             rcode;
448         rbtree_t        *offset_tree;
449         offset_walk_t   walk;
450
451         rad_assert(now != 0);
452
453         /*
454          *      If there's nothing in the file, do nothing,
455          *      but truncate the file, just to be safe.
456          */
457         if (rbtree_num_elements(cache->nas_ports) == 0) {
458                 truncate(cache->filename, (off_t) 0);
459                 DEBUG2("  rlm_radutmp: No entries in file.  Quenching zap.");
460                 return 1;
461         }
462
463         /*
464          *      Create the offset tree, as we want to delete utmp
465          *      entries starting from the start of the file, and we
466          *      can't delete nodes from an rbtree while we're walking
467          *      it.
468          */
469         offset_tree = rbtree_create(offset_cmp, NULL, 0);
470         if (!offset_tree) {
471                 radlog(L_ERR, "rlm_radutmp: Out of memory");
472                 return 0;
473         }
474         
475         pthread_mutex_lock(&cache->mutex);
476
477         /*
478          *      Walk through the cache, finding entries for this NAS,
479          *      and add those entries to the offset tree.
480          */
481         memset(&walk, 0, sizeof(walk));
482         walk.inst = inst;
483         walk.offset_tree = offset_tree;
484         walk.nas_address = nas_address;
485         rcode = rbtree_walk(cache->nas_ports, PreOrder, nas_port_walk, &walk);
486         if (rcode != 0) {
487                 pthread_mutex_unlock(&cache->mutex);
488                 rbtree_free(offset_tree);
489                 radlog(L_ERR, "rlm_radutmp: Failed walking the cache.");
490                 return 0;
491         }
492
493         /*
494          *      If both trees have the same number of elements, then
495          *      don't do anything special, as UDP packets may be
496          *      received out of order, by several seconds.  The
497          *      "offset_walk" routine MAY NOT delete the entries, if
498          *      it sees that the entries in the file are newer than
499          *      the reboot packet.
500          */
501
502         /*
503          *      If there's nothing to do, don't do anything.
504          */
505         if (rbtree_num_elements(offset_tree) == 0) {
506                 DEBUG2("  rlm_radutmp: NAS IP %08x has no users recorded in file %s.",
507                        htonl(nas_address), cache->filename);
508                 pthread_mutex_unlock(&cache->mutex);
509                 rbtree_free(offset_tree);
510                 return 1;
511         }
512
513         /*
514          *      Open the file, to re-write only a few of the entries.
515          */
516         walk.fd = open(cache->filename, O_RDWR);
517         if (walk.fd < 0) {
518                 pthread_mutex_unlock(&cache->mutex);
519                 rbtree_free(offset_tree);
520                 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
521                        cache->filename, strerror(errno));
522                 return 0;
523         }
524
525         /*
526          *      Lock the utmp file, prefer lockf() over flock().
527          *
528          *      FIXME: maybe we want to lock per-record?
529          */
530         rad_lockfd(walk.fd, LOCK_LEN);
531
532         /*
533          *      Walk through the offset tree, from start to finish,
534          *      deleting entries from the NAS tree, adding them to
535          *      the "free offset" cache, and lseek'ing to that offset
536          *      in the file, and clearing out the data.
537          */
538         walk.cache = cache;
539         walk.now = now;
540         rcode = rbtree_walk(offset_tree, InOrder, offset_walk, &walk);
541         rbtree_free(offset_tree);
542         if (rcode != 0) {
543                 radlog(L_ERR, "rlm_radutmp: Failed walking the offsets.");
544                 return 0;
545         }
546
547         close(walk.fd); /* and implicitly release the locks */
548
549         /*
550          *      Just to clean up the file.  If it's empty,
551          *      nuke everything.
552          */
553         if (rbtree_num_elements(cache->nas_ports) == 0) {
554                 NAS_PORT        *this, *next; /* too many copies of code */
555                 
556                 for (this = inst->cache.free_offsets;
557                      this != NULL;
558                      this = next) {
559                         next = this->next;
560                         free(this);
561                 }
562                 
563                 truncate(cache->filename, 0);
564                 rad_assert(rbtree_num_elements(inst->user_tree) == 0);
565         }
566
567         pthread_mutex_unlock(&cache->mutex);
568         
569         return 1;
570 }
571
572
573 /*
574  *      Read a file, to cache all of its entries.
575  */
576 static int cache_file(rlm_radutmp_t *inst, radutmp_cache_t *cache)
577 {
578         int             fd;
579         int             read_size;
580         struct          stat buf;
581         struct          radutmp utmp;
582         NAS_PORT        **tail;
583
584         rad_assert(cache->max_offset == 0);
585         rad_assert(cache->free_offsets == NULL);
586
587         /*
588          *      Doesn't exist, we're fine.
589          */
590         if (stat(cache->filename, &buf) < 0) {
591                 if (errno == ENOENT) {
592                         cache->cached_file = 1;
593                         return 0;
594                 }
595                 radlog(L_ERR, "rlm_radutmp: Cannot stat %s: %s",
596                        cache->filename, strerror(errno));
597                 return 1;
598         }
599
600         /*
601          *      Nothing's there, we're OK.
602          */
603         if (buf.st_size == 0) {
604                 cache->cached_file = 1;
605                 return 0;
606         }
607
608         /*
609          *      Don't let others much around with our data.
610          */
611         pthread_mutex_lock(&cache->mutex);
612
613         /*
614          *      Read the file and cache it's entries.
615          */
616         fd = open(cache->filename, O_RDONLY, cache->permission);
617         if (fd < 0) {
618                 pthread_mutex_unlock(&cache->mutex);
619                 radlog(L_ERR, "rlm_radutmp: Error opening %s: %s",
620                        cache->filename, strerror(errno));
621                 return 1;
622         }
623
624         /*
625          *      Insert free entries into the tail, so that entries
626          *      get used from the start.
627          */
628         tail = &(cache->free_offsets);
629
630         /*
631          *      Don't lock the file, as we're only reading it.
632          */
633         do {
634                 read_size = read(fd, &utmp, sizeof(utmp));
635
636                 /*
637                  *      Read one record.
638                  */
639                 if (read_size == sizeof(utmp)) {
640                         radutmp_simul_t *user, myUser;
641                         NAS_PORT *nas_port = rad_malloc(sizeof(*nas_port));
642
643                         memset(nas_port, 0, sizeof(nas_port));
644                         nas_port->offset = cache->max_offset;
645                         cache->max_offset += sizeof(utmp);
646
647                         /*
648                          *      Idle.  Add it to the list of free
649                          *      offsets.
650                          */
651                         if (utmp.type == P_IDLE) {
652                                 *tail = nas_port;
653                                 tail = &(nas_port->next);
654                                 continue;
655                         }
656
657                         /*
658                          *      It's a login record, 
659                          */
660                         nas_port->nas_address = utmp.nas_address;
661                         nas_port->nas_port = utmp.nas_port;
662
663                         if (!rbtree_insert(cache->nas_ports, nas_port)) {
664                                 rad_assert(0 == 1);
665                         }
666
667                         /*
668                          *      Adds a trailing \0, so myUser.login has
669                          *      an extra char allocated..
670                          */
671                         strNcpy(myUser.login, utmp.login, sizeof(myUser.login));
672                         user = rbtree_finddata(inst->user_tree, &myUser);
673                         if (user) {
674                                 user->simul_count++;
675                         } else {
676                                 /*
677                                  *      Allocate new entry, and add it
678                                  *      to the tree.
679                                  */
680                                 user = rad_malloc(sizeof(user));
681                                 strNcpy(user->login, utmp.login,
682                                         sizeof(user->login));
683                                 user->simul_count = 1;
684
685                                 if (!rbtree_insert(inst->user_tree, user)) {
686                                         rad_assert(0 == 1);
687                                 }
688                         }
689                         continue;
690                 }
691                 
692                 /*
693                  *      We've read a partial record.  WTF?
694                  */
695                 if (read_size != 0) {
696                         pthread_mutex_unlock(&cache->mutex);
697                         close(fd);
698                         radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
699                                cache->filename);
700                         return 1;
701                 }
702
703                 /*
704                  *      Read nothing, stop.
705                  */
706         } while (read_size != 0);
707
708         pthread_mutex_unlock(&cache->mutex);
709         close(fd);              /* and release the lock. */
710         cache->cached_file = 1;
711
712         return 0;
713 }
714
715
716 /*
717  *      Store logins in the RADIUS utmp file.
718  */
719 static int radutmp_accounting(void *instance, REQUEST *request)
720 {
721         rlm_radutmp_t   *inst = instance;
722         struct radutmp  utmp, u;
723         VALUE_PAIR      *vp;
724         int             status = -1;
725         uint32_t        nas_address = 0;
726         uint32_t        framed_address = 0;
727         int             protocol = -1;
728         int             fd;
729         int             port_seen = 0;
730         char            buffer[256];
731         char            filename[1024];
732         char            ip_name[32]; /* 255.255.255.255 */
733         const char      *nas;
734         NAS_PORT        *nas_port, myPort;
735         radutmp_cache_t *cache;
736         int             read_size;
737         rbnode_t        *node;
738
739         /*
740          *      Which type is this.
741          */
742         if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
743                 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
744                 return RLM_MODULE_NOOP;
745         }
746         status = vp->lvalue;
747
748         /*
749          *      Look for weird reboot packets.
750          *
751          *      ComOS (up to and including 3.5.1b20) does not send
752          *      standard PW_STATUS_ACCOUNTING_* messages.
753          *
754          *      Check for:  o no Acct-Session-Time, or time of 0
755          *                  o Acct-Session-Id of "00000000".
756          *
757          *      We could also check for NAS-Port, that attribute
758          *      should NOT be present (but we don't right now).
759          */
760         if ((status != PW_STATUS_ACCOUNTING_ON) &&
761             (status != PW_STATUS_ACCOUNTING_OFF)) do {
762                 int check1 = 0;
763                 int check2 = 0;
764
765                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
766                      == NULL || vp->lvalue == 0)
767                         check1 = 1;
768                 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
769                      != NULL && vp->length == 8 &&
770                      memcmp(vp->vp_strvalue, "00000000", 8) == 0)
771                         check2 = 1;
772                 if (check1 == 0 || check2 == 0) {
773 #if 0 /* Cisco sometimes sends START records without username. */
774                         radlog(L_ERR, "rlm_radutmp: no username in record");
775                         return RLM_MODULE_FAIL;
776 #else
777                         break;
778 #endif
779                 }
780                 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
781                 if (status == PW_STATUS_STOP)
782                         status = PW_STATUS_ACCOUNTING_OFF;
783                 if (status == PW_STATUS_START)
784                         status = PW_STATUS_ACCOUNTING_ON;
785         } while(0);
786
787         memset(&utmp, 0, sizeof(utmp));
788         utmp.porttype = 'A';
789
790         /*
791          *      First, find the interesting attributes.
792          */
793         for (vp = request->packet->vps; vp; vp = vp->next) {
794                 switch (vp->attribute) {
795                         case PW_LOGIN_IP_HOST:
796                         case PW_FRAMED_IP_ADDRESS:
797                                 framed_address = vp->lvalue;
798                                 utmp.framed_address = vp->lvalue;
799                                 break;
800                         case PW_FRAMED_PROTOCOL:
801                                 protocol = vp->lvalue;
802                                 break;
803                         case PW_NAS_IP_ADDRESS:
804                                 nas_address = vp->lvalue;
805                                 utmp.nas_address = vp->lvalue;
806                                 break;
807                         case PW_NAS_PORT:
808                                 utmp.nas_port = vp->lvalue;
809                                 port_seen = 1;
810                                 break;
811                         case PW_ACCT_DELAY_TIME:
812                                 utmp.delay = vp->lvalue;
813                                 break;
814                         case PW_ACCT_SESSION_ID:
815                                 /*
816                                  *      If it's too big, only use the
817                                  *      last bit.
818                                  */
819                                 if (vp->length > sizeof(utmp.session_id)) {
820                                         int length = vp->length - sizeof(utmp.session_id);
821
822                                         /*
823                                          *      Ascend is br0ken - it
824                                          *      adds a \0 to the end
825                                          *      of any string.
826                                          *      Compensate.
827                                          */
828                                         if (vp->vp_strvalue[vp->length - 1] == 0) {
829                                                 length--;
830                                         }
831
832                                         memcpy(utmp.session_id,
833                                               vp->vp_strvalue + length,
834                                               sizeof(utmp.session_id));
835                                 } else {
836                                         memset(utmp.session_id, 0,
837                                                sizeof(utmp.session_id));
838                                         memcpy(utmp.session_id,
839                                                vp->vp_strvalue,
840                                                vp->length);
841                                 }
842                                 break;
843                         case PW_NAS_PORT_TYPE:
844                                 if (vp->lvalue <= 4)
845                                         utmp.porttype = porttypes[vp->lvalue];
846                                 break;
847                         case PW_CALLING_STATION_ID:
848                                 if(inst->callerid_ok)
849                                         strNcpy(utmp.caller_id,
850                                                 (char *)vp->vp_strvalue,
851                                                 sizeof(utmp.caller_id));
852                                 break;
853                 }
854         }
855
856         /*
857          *      If we didn't find out the NAS address, use the
858          *      originator's IP address.
859          */
860         if (nas_address == 0) {
861                 nas_address = request->packet->src_ipaddr;
862                 utmp.nas_address = nas_address;
863                 nas = client_name(nas_address); /* MUST be a valid client */
864
865         } else {                /* might be a client, might not be. */
866                 RADCLIENT *cl;
867
868                 /*
869                  *      Hack like 'client_name()', but with sane
870                  *      fall-back.
871                  */
872                 cl = client_find(nas_address);
873                 if (cl) {
874                         if (cl->shortname && cl->shortname[0]) {
875                                 nas = cl->shortname;
876                         } else {
877                                 nas = cl->longname;
878                         }
879                 } else {
880                         /*
881                          *      The NAS isn't a client, it's behind
882                          *      a proxy server.  In that case, just
883                          *      get the IP address.
884                          */
885                         nas = ip_ntoa(ip_name, nas_address);
886                 }
887         }
888
889         /*
890          *      Set the protocol field.
891          */
892         if (protocol == PW_PPP)
893                 utmp.proto = 'P';
894         else if (protocol == PW_SLIP)
895                 utmp.proto = 'S';
896         else
897                 utmp.proto = 'T';
898
899         utmp.time = request->timestamp - utmp.delay;
900
901         /*
902          *      Get the utmp filename, via xlat.
903          */
904         radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
905
906         /*
907          *      Future: look up filename in filename tree, to get
908          *      radutmp_cache_t pointer
909          */
910         cache = &inst->cache;
911
912         /*
913          *      For now, double-check the filename, to be sure it isn't
914          *      changing.
915          */
916         if (!cache->filename) {
917                 cache->filename = strdup(filename);
918                 rad_assert(cache->filename != NULL);
919
920         } else if (strcmp(cache->filename, filename) != 0) {
921                 radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
922                 return RLM_MODULE_FAIL;
923         }
924
925         /*
926          *      If the lookup failed, create a new one, and add it
927          *      to the filename tree, and cache the file, as below.
928          */
929
930         /*
931          *      For aging, in the future.
932          */
933         cache->last_used = request->timestamp;
934
935         /*
936          *      If we haven't already read the file, then read the
937          *      entire file, in order to cache its entries.
938          */
939         if (!cache->cached_file) {
940                 cache_file(inst, cache);
941         }
942
943         /*
944          *      See if this was a reboot.
945          *
946          *      Hmm... we may not want to zap all of the users when
947          *      the NAS comes up, because of issues with receiving
948          *      UDP packets out of order.
949          */
950         if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
951                 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
952                        nas);
953                 if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
954                         rad_assert(0 == 1);
955                 }
956                 return RLM_MODULE_OK;
957         }
958
959         if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
960                 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
961                        nas);
962                 if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
963                         rad_assert(0 == 1);
964                 }
965                 return RLM_MODULE_OK;
966         }
967
968         /*
969          *      If we don't know this type of entry, then pretend we
970          *      succeeded.
971          */
972         if (status != PW_STATUS_START &&
973             status != PW_STATUS_STOP &&
974             status != PW_STATUS_ALIVE) {
975                 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d, ignoring it.",
976                        nas, utmp.nas_port, status);
977                 return RLM_MODULE_NOOP;
978         }
979
980         /*
981          *      Perhaps we don't want to store this record into
982          *      radutmp. We skip records:
983          *
984          *      - without a NAS-Port (telnet / tcp access)
985          *      - with the username "!root" (console admin login)
986          */
987         if (!port_seen) {
988                 DEBUG2("  rlm_radutmp: No NAS-Port in the packet.  Cannot do anything.");
989                 DEBUG2("  rlm_radumtp: WARNING: checkrad will probably not work!");
990                 return RLM_MODULE_NOOP;
991         }
992
993         /*
994          *      Translate the User-Name attribute, or whatever else
995          *      they told us to use.
996          */
997         *buffer = '\0';
998         radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
999
1000         /*
1001          *      Don't log certain things...
1002          */
1003         if (strcmp(buffer, "!root") == 0) {
1004                 DEBUG2("  rlm_radutmp: Not recording administrative user");
1005
1006                 return RLM_MODULE_NOOP;
1007         }
1008         strNcpy(utmp.login, buffer, RUT_NAMESIZE);
1009
1010         /*
1011          *      First, try to open the file.  If it doesn't exist,
1012          *      nuke the existing caches, and try to create it.
1013          *
1014          *      FIXME: Create any intermediate directories, as
1015          *      appropriate.  See rlm_detail.
1016          */
1017         fd = open(cache->filename, O_RDWR, inst->permission);
1018         if (fd < 0) {
1019                 if (errno == ENOENT) {
1020                         DEBUG2("  rlm_radutmp: File %s doesn't exist, creating it.", cache->filename);
1021                         if (!cache_reset(inst, cache)) return RLM_MODULE_FAIL;
1022                         
1023                         /*
1024                          *      Try to create the file.
1025                          */
1026                         fd = open(cache->filename, O_RDWR | O_CREAT,
1027                                   inst->permission);
1028                 }
1029         } else {                /* exists, but may be empty */
1030                 struct stat buf;
1031
1032                 /*
1033                  *      If the file is empty, reset the cache.
1034                  */
1035                 if ((stat(cache->filename, &buf) == 0) &&
1036                     (buf.st_size == 0) &&
1037                     (!cache_reset(inst, cache))) {
1038                         return RLM_MODULE_FAIL;
1039                 }
1040                 DEBUG2("  rlm_radutmp: File %s was truncated.  Resetting cache.",
1041                        cache->filename);
1042         }
1043
1044         /*
1045          *      Error from creation, or error other than ENOENT: die.
1046          */
1047         if (fd < 0) {
1048                 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
1049                        cache->filename, strerror(errno));
1050                 return RLM_MODULE_FAIL;
1051         }
1052
1053         /*
1054          *      OK.  Now that we've prepared everything we want to do,
1055          *      let's see if we've cached the entry.
1056          */
1057         myPort.nas_address = utmp.nas_address;
1058         myPort.nas_port = utmp.nas_port;
1059
1060         pthread_mutex_lock(&cache->mutex);
1061         node = rbtree_find(cache->nas_ports, &myPort);
1062         pthread_mutex_unlock(&cache->mutex);
1063
1064         if (node) {
1065                 nas_port = rbtree_node2data(cache->nas_ports, node);
1066 #if 0
1067
1068                 /*
1069                  *      stat the file, and get excited if it's been
1070                  *      truncated.
1071                  *
1072                  *      i.e wipe out the cache, and re-read the file.
1073                  */
1074
1075                 /*
1076                  *      Now find the new entry.
1077                  */
1078                 pthread_mutex_lock(&cache->mutex);
1079                 node = rbtree_find(cache->nas_ports, &myPort);
1080                 pthread_mutex_unlock(&cache->mutex);
1081 #endif
1082         }
1083
1084         if (!node) {
1085                 radutmp_simul_t *user;
1086
1087                 /*
1088                  *      Not found in the cache, and we're trying to
1089                  *      delete an existing record: ignore it.
1090                  */
1091                 if (status == PW_STATUS_STOP) {
1092                         DEBUG2("  rlm_radumtp: Logout entry for NAS %s port %u with no Login: ignoring it.",
1093                                nas, utmp.nas_port);
1094                         return RLM_MODULE_NOOP;
1095                 }
1096
1097                 pthread_mutex_lock(&cache->mutex);
1098
1099                 /*
1100                  *      It's a START or ALIVE.  Try to find a free
1101                  *      offset where we can store the new entry, or
1102                  *      create one, if one doesn't already exist.
1103                  */
1104                 if (!cache->free_offsets) {
1105                         cache->free_offsets = rad_malloc(sizeof(NAS_PORT));
1106                         memset(cache->free_offsets, 0,
1107                                sizeof(*(cache->free_offsets)));
1108                         cache->free_offsets->offset = cache->max_offset;
1109                         cache->max_offset += sizeof(u);
1110                 }
1111
1112                 /*
1113                  *      Grab the offset, and put it into the various
1114                  *      caches.
1115                  */
1116                 nas_port = cache->free_offsets;
1117                 cache->free_offsets = nas_port->next;
1118
1119                 nas_port->nas_address = nas_address;
1120                 nas_port->nas_port = utmp.nas_port;
1121
1122                 if (!rbtree_insert(cache->nas_ports, nas_port)) {
1123                         rad_assert(0 == 1);
1124                 }
1125
1126                 /*
1127                  *      Allocate new entry, and add it
1128                  *      to the tree.
1129                  */
1130                 user = rad_malloc(sizeof(user));
1131                 strNcpy(user->login, utmp.login,
1132                         sizeof(user->login));
1133                 user->simul_count = 1;
1134                 
1135                 if (!rbtree_insert(inst->user_tree, user)) {
1136                         rad_assert(0 == 1);
1137                 }
1138
1139                 pthread_mutex_unlock(&cache->mutex);
1140
1141         }
1142                 
1143         /*
1144          *      Entry was found, or newly created in the cache.
1145          *      Seek to the place in the file.
1146          */
1147         lseek(fd, nas_port->offset, SEEK_SET);
1148
1149         /*
1150          *      Lock the utmp file, prefer lockf() over flock().
1151          */
1152         rad_lockfd(fd, LOCK_LEN);
1153
1154         /*
1155          *      If it WAS found in the cache, double-check it against
1156          *      what is in the file.
1157          */
1158         if (node) {
1159                 /*
1160                  *      If we didn't read anything, then this entry
1161                  *      doesn't exist.
1162                  *
1163                  *      Similarly, if the entry in the file doesn't
1164                  *      match what we recall, then nuke the cache
1165                  *      entry.
1166                  */
1167                 read_size = read(fd, &u, sizeof(u));
1168                 if ((read_size < 0) ||
1169                     ((read_size > 0) && (read_size  != sizeof(u)))) {
1170                         /*
1171                          *      Bad read, or bad record.
1172                          */
1173                         radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
1174                                cache->filename);
1175                         close(fd);
1176                         return RLM_MODULE_FAIL;
1177                 }
1178
1179                 rad_assert(read_size != 0);
1180
1181                 /*
1182                  *      We've read a record, go poke at it.
1183                  */
1184                 if (read_size > 0) {
1185                         /*
1186                          *      If these aren't true, then
1187                          *
1188                          *      a) we have cached a "logout" entry,
1189                          *         which we don't do.
1190                          *
1191                          *      b) we have cached the wrong NAS address
1192                          *
1193                          *      c) we have cached the wrong NAS port.
1194                          */
1195                         rad_assert(u.type == P_LOGIN);
1196                         rad_assert(u.nas_address == utmp.nas_address);
1197                         rad_assert(u.nas_port == utmp.nas_port);
1198                         
1199                         /*
1200                          *      An update for the same session.
1201                          */
1202                         if (strncmp(utmp.session_id, u.session_id,
1203                                     sizeof(u.session_id)) == 0) {
1204
1205                                 /*
1206                                  *      It's a duplicate start, so we
1207                                  *      don't bother writing it.
1208                                  */
1209                                 if (status == PW_STATUS_START) {
1210                                         DEBUG2("  rlm_radutmp: Login entry for NAS %s port %u duplicate, ignoring it.",
1211                                                nas, u.nas_port);
1212                                         close(fd);
1213                                         return RLM_MODULE_OK;
1214
1215
1216                                 /*
1217                                  *      ALIVE for this session, keep the
1218                                  *      original login time.
1219                                  */
1220                                 } else if (status == PW_STATUS_ALIVE) {
1221                                         utmp.time = u.time;
1222
1223                                 /*
1224                                  *      Stop: delete it from our cache.
1225                                  */
1226                                 } else if (status == PW_STATUS_STOP) {
1227                                         radutmp_simul_t *user, myUser;
1228
1229                                         pthread_mutex_lock(&cache->mutex);
1230                                         rbtree_deletebydata(cache->nas_ports,
1231                                                             nas_port);
1232
1233                                         strNcpy(myUser.login,
1234                                                 u.login, sizeof(myUser.login));
1235                                         user = rbtree_finddata(inst->user_tree,
1236                                                                &myUser);
1237                                         rad_assert(user != NULL);
1238                                         rad_assert(user->simul_count > 0);
1239
1240                                         user->simul_count--;
1241                                         if (user->simul_count == 0) {
1242                                                 rbtree_deletebydata(inst->user_tree, user);
1243                                         }
1244
1245                                         pthread_mutex_unlock(&cache->mutex);
1246
1247                                 } else {
1248                                         /*
1249                                          *      We don't know how to
1250                                          *      handle this.
1251                                          */
1252                                         rad_assert(0 == 1);
1253                                 }
1254
1255                         } else { /* session ID doesn't match */
1256                                 /*
1257                                  *      STOP for the right NAS & port,
1258                                  *      but the Acct-Session-Id is
1259                                  *      different.  This means that
1260                                  *      we missed the original "stop",
1261                                  *      and a new "start".
1262                                  */
1263                                 if (status == PW_STATUS_STOP) {
1264                                         radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has old Acct-Session-ID, ignoring it.",
1265                                                nas, u.nas_port);
1266                                         close(fd);
1267                                         return RLM_MODULE_OK;
1268                                 }
1269                         } /* checked session ID's */
1270                 }  /* else we haven't read anything from the file. */
1271         } /* else the entry wasn't cached, but could have been inserted */
1272
1273         /*
1274          *      Hmm... we may have received a start or alive packet
1275          *      AFTER a stop or nas-down, in that case, we want to
1276          *      discard the new packet.  However, the original code
1277          *      could over-write an idle record with a new login
1278          *      record for another NAS && port, so we won't worry
1279          *      about this case too much.
1280          */
1281
1282         /*
1283          *      Seek to where the entry is, and write it blindly.
1284          */
1285         lseek(fd, nas_port->offset, SEEK_SET); /* FIXME: err */
1286
1287         if (status != PW_STATUS_STOP) {
1288                 utmp.type = P_LOGIN;
1289                 rad_assert(nas_port != NULL); /* it WAS cached */
1290         } else {
1291                 /* FIXME: maybe assert that the entry was deleted... */
1292                 memcpy(&utmp, &u, sizeof(utmp));
1293                 utmp.type = P_IDLE;
1294         }
1295
1296         write(fd, &utmp, sizeof(utmp)); /* FIXME: err */
1297
1298         close(fd);      /* and implicitly release the locks */
1299
1300         return RLM_MODULE_OK;
1301 }
1302
1303 /*
1304  *      See if a user is already logged in. Sets request->simul_count
1305  *      to the current session count for this user and sets
1306  *      request->simul_mpp to 2 if it looks like a multilink attempt
1307  *      based on the requested IP address, otherwise leaves
1308  *      request->simul_mpp alone.
1309  *
1310  *      Check twice. If on the first pass the user exceeds his
1311  *      max. number of logins, do a second pass and validate all
1312  *      logins by querying the terminal server (using eg. SNMP).
1313  */
1314 static int radutmp_checksimul(void *instance, REQUEST *request)
1315 {
1316         struct radutmp  u;
1317         int             fd;
1318         VALUE_PAIR      *vp;
1319         uint32_t        ipno = 0;
1320         char            *call_num = NULL;
1321         int             rcode;
1322         rlm_radutmp_t   *inst = instance;
1323         char            login[256];
1324         char            filename[1024];
1325         radutmp_cache_t *cache;
1326         radutmp_simul_t *user, myUser;
1327
1328         /*
1329          *      Get the filename, via xlat.
1330          */
1331         radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
1332
1333         /*
1334          *      Future: look up filename in filename tree, to get
1335          *      radutmp_cache_t pointer
1336          */
1337         cache = &inst->cache;
1338
1339         /*
1340          *      For now, double-check the filename, to be sure it isn't
1341          *      changing.
1342          */
1343         if (!cache->filename) {
1344                 cache->filename = strdup(filename);
1345                 rad_assert(cache->filename != NULL);
1346
1347         } else if (strcmp(cache->filename, filename) != 0) {
1348                 radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
1349                 return RLM_MODULE_FAIL;
1350         }
1351
1352         *login = '\0';
1353         radius_xlat(login, sizeof(login), inst->username, request, NULL);
1354         if (!*login) {
1355                 return RLM_MODULE_NOOP;
1356         }
1357
1358         /*
1359          *      WTF?  This is probably wrong... we probably want to
1360          *      be able to check users across multiple session accounting
1361          *      methods.
1362          */
1363         request->simul_count = 0;
1364
1365         strNcpy(myUser.login, login, sizeof(myUser.login));
1366         pthread_mutex_lock(&inst->cache.mutex);
1367         user = rbtree_finddata(inst->user_tree, &myUser);
1368         if (user) request->simul_count = user->simul_count;
1369         user = NULL;            /* someone else may delete it */
1370         pthread_mutex_unlock(&inst->cache.mutex);
1371
1372         /*
1373          *      The number of users logged in is OK,
1374          *      OR, we've been told to not check the NAS.
1375          */
1376         if ((request->simul_count < request->simul_max) ||
1377             !inst->check_nas) {
1378                 return RLM_MODULE_OK;
1379         }
1380
1381         /*
1382          *      The user is logged in at least N times, and
1383          *      we're told to check the NAS.  In that case,
1384          *      we've got to read the file, and check each
1385          *      NAS port by hand.
1386          */
1387         if ((fd = open(cache->filename, O_RDWR)) < 0) {
1388                 /*
1389                  *      If the file doesn't exist, then no users
1390                  *      are logged in.
1391                  */
1392                 if (errno == ENOENT) {
1393                         request->simul_count = 0;
1394                         return RLM_MODULE_OK;
1395                 }
1396
1397                 /*
1398                  *      Error accessing the file.
1399                  */
1400                 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
1401                        cache->filename, strerror(errno));
1402                 return RLM_MODULE_FAIL;
1403         }
1404
1405         /*
1406          *      Setup some stuff, like for MPP detection.
1407          */
1408         if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1409                 ipno = vp->lvalue;
1410         if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1411                 call_num = vp->vp_strvalue;
1412
1413         /*
1414          *      lock the file while reading/writing.
1415          */
1416         rad_lockfd(fd, LOCK_LEN);
1417
1418         /*
1419          *      FIXME: If we get a 'Start' for a user/nas/port which is
1420          *      listed, but for which we did NOT get a 'Stop', then
1421          *      it's not a duplicate session.  This happens with
1422          *      static IP's like DSL.
1423          */
1424         request->simul_count = 0;
1425         while (read(fd, &u, sizeof(u)) == sizeof(u)) {
1426                 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
1427                      (!inst->case_sensitive &&
1428                       (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
1429                     (u.type == P_LOGIN)) {
1430                         char session_id[sizeof(u.session_id) + 1];
1431                         char utmp_login[sizeof(u.login) + 1];
1432
1433                         strNcpy(session_id, u.session_id, sizeof(session_id));
1434
1435                         /*
1436                          *      The login name MAY fill the whole field,
1437                          *      and thus won't be zero-filled.
1438                          *
1439                          *      Note that we take the user name from
1440                          *      the utmp file, as that's the canonical
1441                          *      form.  The 'login' variable may contain
1442                          *      a string which is an upper/lowercase
1443                          *      version of u.login.  When we call the
1444                          *      routine to check the terminal server,
1445                          *      the NAS may be case sensitive.
1446                          *
1447                          *      e.g. We ask if "bob" is using a port,
1448                          *      and the NAS says "no", because "BOB"
1449                          *      is using the port.
1450                          */
1451                         strNcpy(utmp_login, u.login, sizeof(u.login));
1452
1453                         /*
1454                          *      rad_check_ts may take seconds
1455                          *      to return, and we don't want
1456                          *      to block everyone else while
1457                          *      that's happening.  */
1458                         rad_unlockfd(fd, LOCK_LEN);
1459                         rcode = rad_check_ts(u.nas_address, u.nas_port,
1460                                              utmp_login, session_id);
1461                         rad_lockfd(fd, LOCK_LEN);
1462
1463                         if (rcode == 0) {
1464                                 /*
1465                                  *      Stale record - zap it.
1466                                  *
1467                                  *      Hmm... this ends up calling
1468                                  *      the accounting section
1469                                  *      recursively...
1470                                  */
1471                                 session_zap(request, u.nas_address,
1472                                             u.nas_port, login, session_id,
1473                                             u.framed_address, u.proto,0);
1474                         }
1475                         else if (rcode == 1) {
1476                                 /*
1477                                  *      User is still logged in.
1478                                  */
1479                                 ++request->simul_count;
1480
1481                                 /*
1482                                  *      Does it look like a MPP attempt?
1483                                  */
1484                                 if (strchr("SCPA", u.proto) &&
1485                                     ipno && u.framed_address == ipno)
1486                                         request->simul_mpp = 2;
1487                                 else if (strchr("SCPA", u.proto) && call_num &&
1488                                         !strncmp(u.caller_id,call_num,16))
1489                                         request->simul_mpp = 2;
1490                         }
1491                         else {
1492                                 /*
1493                                  *      Failed to check the terminal
1494                                  *      server for duplicate logins:
1495                                  *      Return an error.
1496                                  */
1497                                 close(fd);
1498                                 radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
1499                                 return RLM_MODULE_FAIL;
1500                         }
1501                 }
1502         }
1503         close(fd);              /* and implicitly release the locks */
1504
1505         return RLM_MODULE_OK;
1506 }
1507
1508 /* globally exported name */
1509 module_t rlm_radutmp = {
1510   "radutmp",
1511   0,                            /* type: reserved */
1512   NULL,                         /* initialization */
1513   radutmp_instantiate,          /* instantiation */
1514   {
1515           NULL,                 /* authentication */
1516           NULL,                 /* authorization */
1517           NULL,                 /* preaccounting */
1518           radutmp_accounting,   /* accounting */
1519           radutmp_checksimul,   /* checksimul */
1520           NULL,                 /* pre-proxy */
1521           NULL,                 /* post-proxy */
1522           NULL                  /* post-auth */
1523   },
1524   radutmp_detach,               /* detach */
1525   NULL,                         /* destroy */
1526 };
1527