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.
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.
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
20 * Copyright 2000,2001,2002,2003,2004,2006 The FreeRADIUS server project
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/radutmp.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/rad_assert.h>
36 #define LOCK_LEN sizeof(struct radutmp)
38 static const char porttypes[] = "ASITX";
41 * Used for caching radutmp lookups in the accounting
42 * component. The session (checksimul) component doesn't use it,
43 * but probably should, though we're not sure how...
45 * The intent here is to keep this structure as small as
46 * possible, so that it doesn't take up too much memory.
48 typedef struct nas_port {
50 unsigned int nas_port;
53 struct nas_port *next; /* for the free list */
58 * Per-file information.
60 * Hmm... having multiple filenames managed by one instance
61 * of the module makes it difficult for the module to do
62 * simultaneous-use checking, without more code edits.
64 typedef struct radutmp_cache_t {
65 const char *filename; /* for future reference */
66 time_t last_used; /* for future reference */
69 NAS_PORT *free_offsets;
74 pthread_mutex_t mutex;
80 * We cache the users, too, so that we only have to read radutmp
83 typedef struct radutmp_simul_t {
84 char login[sizeof(((struct radutmp *) NULL)->login) + 1];
90 * Data we store per module.
92 typedef struct rlm_radutmp_t {
100 rbtree_t *user_tree; /* for simultaneous-use */
103 * As the filenames can be dynamically translated,
104 * we want to keep track of them in a separate data
105 * structure, so that we can have per-file caches.
107 radutmp_cache_t cache;
110 #ifndef HAVE_PTHREAD_H
112 * This is easier than ifdef's throughout the code.
114 #define pthread_mutex_init(_x, _y)
115 #define pthread_mutex_destroy(_x)
116 #define pthread_mutex_lock(_x)
117 #define pthread_mutex_unlock(_x)
120 static const CONF_PARSER module_config[] = {
121 { "filename", PW_TYPE_STRING_PTR,
122 offsetof(rlm_radutmp_t,filename), NULL, RADUTMP },
123 { "username", PW_TYPE_STRING_PTR,
124 offsetof(rlm_radutmp_t,username), NULL, "%{User-Name}"},
125 { "case_sensitive", PW_TYPE_BOOLEAN,
126 offsetof(rlm_radutmp_t,case_sensitive), NULL, "yes"},
127 { "check_with_nas", PW_TYPE_BOOLEAN,
128 offsetof(rlm_radutmp_t,check_nas), NULL, "yes"},
129 { "perm", PW_TYPE_INTEGER,
130 offsetof(rlm_radutmp_t,permission), NULL, "0644" },
131 { "callerid", PW_TYPE_BOOLEAN,
132 offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
133 { NULL, -1, 0, NULL, NULL } /* end the list */
140 static int nas_port_cmp(const void *a, const void *b)
142 const NAS_PORT *one = a;
143 const NAS_PORT *two = b;
145 if (one->nas_address < two->nas_address) return -1;
146 if (one->nas_address > two->nas_address) return +1;
148 if (one->nas_port < two->nas_port) return -1;
149 if (one->nas_port > two->nas_port) return +1;
156 * Compare two user names.
158 static int user_cmp(const void *a, const void *b)
160 const radutmp_simul_t *one = a;
161 const radutmp_simul_t *two = b;
163 return strcmp(one->login, two->login);
168 * Compare two user names, case insensitive.
170 static int user_case_cmp(const void *a, const void *b)
172 const radutmp_simul_t *one = a;
173 const radutmp_simul_t *two = b;
175 return strcasecmp(one->login, two->login);
182 static int radutmp_detach(void *instance)
184 NAS_PORT *this, *next;
185 rlm_radutmp_t *inst = instance;
187 rbtree_free(inst->cache.nas_ports);
189 for (this = inst->cache.free_offsets;
196 if (inst->cache.filename) free(inst->cache.filename);
198 pthread_mutex_destroy(&(inst->cache.mutex));
201 rbtree_free(inst->user_tree);
211 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
215 inst = rad_malloc(sizeof(*inst));
219 memset(inst, 0, sizeof(*inst));
221 if (cf_section_parse(conf, inst, module_config)) {
222 radutmp_detach(inst);
226 inst->cache.nas_ports = rbtree_create(nas_port_cmp, free, 0);
227 if (!inst->cache.nas_ports) {
228 radlog(L_ERR, "rlm_radutmp: Failed to create nas tree");
229 radutmp_detach(inst);
233 pthread_mutex_init(&(inst->cache.mutex), NULL);
234 inst->cache.permission = inst->permission;
236 if (inst->case_sensitive) {
237 inst->user_tree = rbtree_create(user_cmp, free, 0);
239 inst->user_tree = rbtree_create(user_case_cmp, free, 0);
241 if (!inst->user_tree) {
242 radlog(L_ERR, "rlm_radutmp: Failed to create user tree");
243 radutmp_detach(inst);
253 * Reset the cached entries.
255 static int cache_reset(rlm_radutmp_t *inst, radutmp_cache_t *cache)
257 NAS_PORT *this, *next;
260 * Cache is already reset, do nothing.
262 if ((rbtree_num_elements(cache->nas_ports) == 0) &&
263 (cache->free_offsets == NULL)) {
264 DEBUG2(" rlm_radutmp: Not resetting the cache");
267 DEBUG2(" rlm_radutmp: Resetting the cache");
269 pthread_mutex_lock(&cache->mutex);
271 rbtree_free(inst->user_tree);
273 rbtree_free(cache->nas_ports);
275 for (this = cache->free_offsets;
281 cache->free_offsets = NULL;
284 * Re-create the caches.
286 cache->nas_ports = rbtree_create(nas_port_cmp, free, 0);
287 if (!cache->nas_ports) {
288 pthread_mutex_unlock(&cache->mutex);
289 radlog(L_ERR, "rlm_radutmp: No memory");
293 cache->max_offset = 0;
295 cache->cached_file = 1;
297 if (inst->case_sensitive) {
298 inst->user_tree = rbtree_create(user_cmp, free, 0);
300 inst->user_tree = rbtree_create(user_case_cmp, free, 0);
302 if (!inst->user_tree) {
303 pthread_mutex_unlock(&cache->mutex);
304 radlog(L_ERR, "rlm_radutmp: No memory");
308 pthread_mutex_unlock(&cache->mutex);
315 * Compare two offsets in a tree.
317 static int offset_cmp(const void *a, const void *b)
319 const NAS_PORT *one = a;
320 const NAS_PORT *two = b;
322 if (one->offset < two->offset) return -1;
323 if (one->offset > two->offset) return +1;
330 * Data structure to use when walking the trees, for zap.
332 typedef struct offset_walk_t {
334 radutmp_cache_t *cache;
335 rbtree_t *offset_tree;
336 uint32_t nas_address;
343 * Walk over the cache, finding entries with the matching NAS IP address.
345 static int nas_port_walk(void *context, void *data)
347 offset_walk_t *walk = context;
348 NAS_PORT *nas_port = data;
351 * Doesn't match, keep going.
353 if (walk->nas_address != nas_port->nas_address) return 0;
356 * Insert it into the offset tree, for later deletion.
358 if (rbtree_insert(walk->offset_tree, nas_port) != 1) {
359 DEBUG2(" rlm_radumtp: Insertion failed in nas port walk.");
368 * Walk through the offset tree, operating on the cache
370 static int offset_walk(void *context, void *data)
372 offset_walk_t *walk = context;
373 NAS_PORT *nas_port = data;
375 radutmp_simul_t *user, myUser;
378 * Seek to the entry, and possibly re-write it.
380 if (lseek(walk->fd, nas_port->offset, SEEK_SET) < 0) {
384 if (read(walk->fd, &utmp, sizeof(utmp)) != sizeof(utmp)) {
389 * If the entry in the file is NEWER than the reboot
390 * packet, don't re-write it, and don't delete it.
392 if (utmp.time > walk->now) {
397 utmp.time = walk->now;
399 if (lseek(walk->fd, -(off_t)sizeof(utmp), SEEK_CUR) < 0) {
400 radlog(L_ERR, "rlm_radutmp: offset_walk: failed in lseek: %s",
405 write(walk->fd, &utmp, sizeof(utmp));
407 strlcpy(myUser.login, utmp.login, sizeof(myUser.login));
408 user = rbtree_finddata(walk->inst->user_tree, &myUser);
409 rad_assert(user != NULL);
410 rad_assert(user->simul_count > 0);
412 if (user->simul_count == 0) {
413 rbtree_deletebydata(walk->inst->user_tree, user);
416 if (rbtree_deletebydata(walk->cache->nas_ports, nas_port) == 0) {
417 radlog(L_ERR, "rlm_radutmp: Failed to delete entry from cache");
422 * Insert the entry into the free list.
424 nas_port->next = walk->cache->free_offsets;
425 walk->cache->free_offsets = nas_port;
432 * Zap all users on a NAS from the radutmp file.
434 static int radutmp_zap(rlm_radutmp_t *inst,
435 radutmp_cache_t *cache,
436 uint32_t nas_address,
440 rbtree_t *offset_tree;
443 rad_assert(now != 0);
446 * If there's nothing in the file, do nothing,
447 * but truncate the file, just to be safe.
449 if (rbtree_num_elements(cache->nas_ports) == 0) {
450 truncate(cache->filename, (off_t) 0);
451 DEBUG2(" rlm_radutmp: No entries in file. Quenching zap.");
456 * Create the offset tree, as we want to delete utmp
457 * entries starting from the start of the file, and we
458 * can't delete nodes from an rbtree while we're walking
461 offset_tree = rbtree_create(offset_cmp, NULL, 0);
463 radlog(L_ERR, "rlm_radutmp: Out of memory");
467 pthread_mutex_lock(&cache->mutex);
470 * Walk through the cache, finding entries for this NAS,
471 * and add those entries to the offset tree.
473 memset(&walk, 0, sizeof(walk));
475 walk.offset_tree = offset_tree;
476 walk.nas_address = nas_address;
477 rcode = rbtree_walk(cache->nas_ports, PreOrder, nas_port_walk, &walk);
479 pthread_mutex_unlock(&cache->mutex);
480 rbtree_free(offset_tree);
481 radlog(L_ERR, "rlm_radutmp: Failed walking the cache.");
486 * If both trees have the same number of elements, then
487 * don't do anything special, as UDP packets may be
488 * received out of order, by several seconds. The
489 * "offset_walk" routine MAY NOT delete the entries, if
490 * it sees that the entries in the file are newer than
495 * If there's nothing to do, don't do anything.
497 if (rbtree_num_elements(offset_tree) == 0) {
498 DEBUG2(" rlm_radutmp: NAS IP %08x has no users recorded in file %s.",
499 htonl(nas_address), cache->filename);
500 pthread_mutex_unlock(&cache->mutex);
501 rbtree_free(offset_tree);
506 * Open the file, to re-write only a few of the entries.
508 walk.fd = open(cache->filename, O_RDWR);
510 pthread_mutex_unlock(&cache->mutex);
511 rbtree_free(offset_tree);
512 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
513 cache->filename, strerror(errno));
518 * Lock the utmp file, prefer lockf() over flock().
520 * FIXME: maybe we want to lock per-record?
522 rad_lockfd(walk.fd, LOCK_LEN);
525 * Walk through the offset tree, from start to finish,
526 * deleting entries from the NAS tree, adding them to
527 * the "free offset" cache, and lseek'ing to that offset
528 * in the file, and clearing out the data.
532 rcode = rbtree_walk(offset_tree, InOrder, offset_walk, &walk);
533 rbtree_free(offset_tree);
535 radlog(L_ERR, "rlm_radutmp: Failed walking the offsets.");
539 close(walk.fd); /* and implicitly release the locks */
542 * Just to clean up the file. If it's empty,
545 if (rbtree_num_elements(cache->nas_ports) == 0) {
546 NAS_PORT *this, *next; /* too many copies of code */
548 for (this = inst->cache.free_offsets;
555 truncate(cache->filename, 0);
556 rad_assert(rbtree_num_elements(inst->user_tree) == 0);
559 pthread_mutex_unlock(&cache->mutex);
566 * Read a file, to cache all of its entries.
568 static int cache_file(rlm_radutmp_t *inst, radutmp_cache_t *cache)
576 rad_assert(cache->max_offset == 0);
577 rad_assert(cache->free_offsets == NULL);
580 * Doesn't exist, we're fine.
582 if (stat(cache->filename, &buf) < 0) {
583 if (errno == ENOENT) {
584 cache->cached_file = 1;
587 radlog(L_ERR, "rlm_radutmp: Cannot stat %s: %s",
588 cache->filename, strerror(errno));
593 * Nothing's there, we're OK.
595 if (buf.st_size == 0) {
596 cache->cached_file = 1;
601 * Don't let others much around with our data.
603 pthread_mutex_lock(&cache->mutex);
606 * Read the file and cache it's entries.
608 fd = open(cache->filename, O_RDONLY, cache->permission);
610 pthread_mutex_unlock(&cache->mutex);
611 radlog(L_ERR, "rlm_radutmp: Error opening %s: %s",
612 cache->filename, strerror(errno));
617 * Insert free entries into the tail, so that entries
618 * get used from the start.
620 tail = &(cache->free_offsets);
623 * Don't lock the file, as we're only reading it.
626 read_size = read(fd, &utmp, sizeof(utmp));
631 if (read_size == sizeof(utmp)) {
632 radutmp_simul_t *user, myUser;
633 NAS_PORT *nas_port = rad_malloc(sizeof(*nas_port));
635 memset(nas_port, 0, sizeof(nas_port));
636 nas_port->offset = cache->max_offset;
637 cache->max_offset += sizeof(utmp);
640 * Idle. Add it to the list of free
643 if (utmp.type == P_IDLE) {
645 tail = &(nas_port->next);
650 * It's a login record,
652 nas_port->nas_address = utmp.nas_address;
653 nas_port->nas_port = utmp.nas_port;
655 if (!rbtree_insert(cache->nas_ports, nas_port)) {
660 * Adds a trailing \0, so myUser.login has
661 * an extra char allocated..
663 strlcpy(myUser.login, utmp.login, sizeof(myUser.login));
664 user = rbtree_finddata(inst->user_tree, &myUser);
669 * Allocate new entry, and add it
672 user = rad_malloc(sizeof(user));
673 strlcpy(user->login, utmp.login,
674 sizeof(user->login));
675 user->simul_count = 1;
677 if (!rbtree_insert(inst->user_tree, user)) {
685 * We've read a partial record. WTF?
687 if (read_size != 0) {
688 pthread_mutex_unlock(&cache->mutex);
690 radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
696 * Read nothing, stop.
698 } while (read_size != 0);
700 pthread_mutex_unlock(&cache->mutex);
701 close(fd); /* and release the lock. */
702 cache->cached_file = 1;
709 * Store logins in the RADIUS utmp file.
711 static int radutmp_accounting(void *instance, REQUEST *request)
713 rlm_radutmp_t *inst = instance;
714 struct radutmp utmp, u;
717 uint32_t nas_address = 0;
718 uint32_t framed_address = 0;
724 char ip_name[32]; /* 255.255.255.255 */
726 NAS_PORT *nas_port, myPort;
727 radutmp_cache_t *cache;
732 * Which type is this.
734 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
735 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
736 return RLM_MODULE_NOOP;
738 status = vp->vp_integer;
741 * Look for weird reboot packets.
743 * ComOS (up to and including 3.5.1b20) does not send
744 * standard PW_STATUS_ACCOUNTING_* messages.
746 * Check for: o no Acct-Session-Time, or time of 0
747 * o Acct-Session-Id of "00000000".
749 * We could also check for NAS-Port, that attribute
750 * should NOT be present (but we don't right now).
752 if ((status != PW_STATUS_ACCOUNTING_ON) &&
753 (status != PW_STATUS_ACCOUNTING_OFF)) do {
757 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
758 == NULL || vp->vp_date == 0)
760 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
761 != NULL && vp->length == 8 &&
762 memcmp(vp->vp_strvalue, "00000000", 8) == 0)
764 if (check1 == 0 || check2 == 0) {
765 #if 0 /* Cisco sometimes sends START records without username. */
766 radlog(L_ERR, "rlm_radutmp: no username in record");
767 return RLM_MODULE_FAIL;
772 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
773 if (status == PW_STATUS_STOP)
774 status = PW_STATUS_ACCOUNTING_OFF;
775 if (status == PW_STATUS_START)
776 status = PW_STATUS_ACCOUNTING_ON;
779 memset(&utmp, 0, sizeof(utmp));
783 * First, find the interesting attributes.
785 for (vp = request->packet->vps; vp; vp = vp->next) {
786 switch (vp->attribute) {
787 case PW_LOGIN_IP_HOST:
788 case PW_FRAMED_IP_ADDRESS:
789 framed_address = vp->vp_ipaddr;
790 utmp.framed_address = vp->vp_ipaddr;
792 case PW_FRAMED_PROTOCOL:
793 protocol = vp->vp_integer;
795 case PW_NAS_IP_ADDRESS:
796 nas_address = vp->vp_ipaddr;
797 utmp.nas_address = vp->vp_ipaddr;
800 utmp.nas_port = vp->vp_integer;
803 case PW_ACCT_DELAY_TIME:
804 utmp.delay = vp->vp_integer;
806 case PW_ACCT_SESSION_ID:
808 * If it's too big, only use the
811 if (vp->length > sizeof(utmp.session_id)) {
812 int length = vp->length - sizeof(utmp.session_id);
815 * Ascend is br0ken - it
816 * adds a \0 to the end
820 if (vp->vp_strvalue[vp->length - 1] == 0) {
824 memcpy(utmp.session_id,
825 vp->vp_strvalue + length,
826 sizeof(utmp.session_id));
828 memset(utmp.session_id, 0,
829 sizeof(utmp.session_id));
830 memcpy(utmp.session_id,
835 case PW_NAS_PORT_TYPE:
836 if (vp->vp_integer <= 4)
837 utmp.porttype = porttypes[vp->vp_integer];
839 case PW_CALLING_STATION_ID:
840 if(inst->callerid_ok)
841 strlcpy(utmp.caller_id,
842 (char *)vp->vp_strvalue,
843 sizeof(utmp.caller_id));
849 * If we didn't find out the NAS address, use the
850 * originator's IP address.
852 if (nas_address == 0) {
853 nas_address = request->packet->src_ipaddr;
854 utmp.nas_address = nas_address;
855 nas = client_name(nas_address); /* MUST be a valid client */
857 } else { /* might be a client, might not be. */
861 * Hack like 'client_name()', but with sane
864 cl = client_find(nas_address);
866 if (cl->shortname && cl->shortname[0]) {
873 * The NAS isn't a client, it's behind
874 * a proxy server. In that case, just
875 * get the IP address.
877 nas = ip_ntoa(ip_name, nas_address);
882 * Set the protocol field.
884 if (protocol == PW_PPP)
886 else if (protocol == PW_SLIP)
891 utmp.time = request->timestamp - utmp.delay;
894 * Get the utmp filename, via xlat.
896 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
899 * Future: look up filename in filename tree, to get
900 * radutmp_cache_t pointer
902 cache = &inst->cache;
905 * For now, double-check the filename, to be sure it isn't
908 if (!cache->filename) {
909 cache->filename = strdup(filename);
910 rad_assert(cache->filename != NULL);
912 } else if (strcmp(cache->filename, filename) != 0) {
913 radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
914 return RLM_MODULE_FAIL;
918 * If the lookup failed, create a new one, and add it
919 * to the filename tree, and cache the file, as below.
923 * For aging, in the future.
925 cache->last_used = request->timestamp;
928 * If we haven't already read the file, then read the
929 * entire file, in order to cache its entries.
931 if (!cache->cached_file) {
932 cache_file(inst, cache);
936 * See if this was a reboot.
938 * Hmm... we may not want to zap all of the users when
939 * the NAS comes up, because of issues with receiving
940 * UDP packets out of order.
942 if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
943 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
945 if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
948 return RLM_MODULE_OK;
951 if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
952 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
954 if (!radutmp_zap(inst, cache, nas_address, utmp.time)) {
957 return RLM_MODULE_OK;
961 * If we don't know this type of entry, then pretend we
964 if (status != PW_STATUS_START &&
965 status != PW_STATUS_STOP &&
966 status != PW_STATUS_ALIVE) {
967 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d, ignoring it.",
968 nas, utmp.nas_port, status);
969 return RLM_MODULE_NOOP;
973 * Perhaps we don't want to store this record into
974 * radutmp. We skip records:
976 * - without a NAS-Port (telnet / tcp access)
977 * - with the username "!root" (console admin login)
980 DEBUG2(" rlm_radutmp: No NAS-Port in the packet. Cannot do anything.");
981 DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!");
982 return RLM_MODULE_NOOP;
986 * Translate the User-Name attribute, or whatever else
987 * they told us to use.
990 radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
993 * Don't log certain things...
995 if (strcmp(buffer, "!root") == 0) {
996 DEBUG2(" rlm_radutmp: Not recording administrative user");
998 return RLM_MODULE_NOOP;
1000 strlcpy(utmp.login, buffer, RUT_NAMESIZE);
1003 * First, try to open the file. If it doesn't exist,
1004 * nuke the existing caches, and try to create it.
1006 * FIXME: Create any intermediate directories, as
1007 * appropriate. See rlm_detail.
1009 fd = open(cache->filename, O_RDWR, inst->permission);
1011 if (errno == ENOENT) {
1012 DEBUG2(" rlm_radutmp: File %s doesn't exist, creating it.", cache->filename);
1013 if (!cache_reset(inst, cache)) return RLM_MODULE_FAIL;
1016 * Try to create the file.
1018 fd = open(cache->filename, O_RDWR | O_CREAT,
1021 } else { /* exists, but may be empty */
1025 * If the file is empty, reset the cache.
1027 if ((stat(cache->filename, &buf) == 0) &&
1028 (buf.st_size == 0) &&
1029 (!cache_reset(inst, cache))) {
1030 return RLM_MODULE_FAIL;
1032 DEBUG2(" rlm_radutmp: File %s was truncated. Resetting cache.",
1037 * Error from creation, or error other than ENOENT: die.
1040 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
1041 cache->filename, strerror(errno));
1042 return RLM_MODULE_FAIL;
1046 * OK. Now that we've prepared everything we want to do,
1047 * let's see if we've cached the entry.
1049 myPort.nas_address = utmp.nas_address;
1050 myPort.nas_port = utmp.nas_port;
1052 pthread_mutex_lock(&cache->mutex);
1053 node = rbtree_find(cache->nas_ports, &myPort);
1054 pthread_mutex_unlock(&cache->mutex);
1057 nas_port = rbtree_node2data(cache->nas_ports, node);
1061 * stat the file, and get excited if it's been
1064 * i.e wipe out the cache, and re-read the file.
1068 * Now find the new entry.
1070 pthread_mutex_lock(&cache->mutex);
1071 node = rbtree_find(cache->nas_ports, &myPort);
1072 pthread_mutex_unlock(&cache->mutex);
1077 radutmp_simul_t *user;
1080 * Not found in the cache, and we're trying to
1081 * delete an existing record: ignore it.
1083 if (status == PW_STATUS_STOP) {
1084 DEBUG2(" rlm_radumtp: Logout entry for NAS %s port %u with no Login: ignoring it.",
1085 nas, utmp.nas_port);
1086 return RLM_MODULE_NOOP;
1089 pthread_mutex_lock(&cache->mutex);
1092 * It's a START or ALIVE. Try to find a free
1093 * offset where we can store the new entry, or
1094 * create one, if one doesn't already exist.
1096 if (!cache->free_offsets) {
1097 cache->free_offsets = rad_malloc(sizeof(NAS_PORT));
1098 memset(cache->free_offsets, 0,
1099 sizeof(*(cache->free_offsets)));
1100 cache->free_offsets->offset = cache->max_offset;
1101 cache->max_offset += sizeof(u);
1105 * Grab the offset, and put it into the various
1108 nas_port = cache->free_offsets;
1109 cache->free_offsets = nas_port->next;
1111 nas_port->nas_address = nas_address;
1112 nas_port->nas_port = utmp.nas_port;
1114 if (!rbtree_insert(cache->nas_ports, nas_port)) {
1119 * Allocate new entry, and add it
1122 user = rad_malloc(sizeof(user));
1123 strlcpy(user->login, utmp.login,
1124 sizeof(user->login));
1125 user->simul_count = 1;
1127 if (!rbtree_insert(inst->user_tree, user)) {
1131 pthread_mutex_unlock(&cache->mutex);
1136 * Entry was found, or newly created in the cache.
1137 * Seek to the place in the file.
1139 lseek(fd, nas_port->offset, SEEK_SET);
1142 * Lock the utmp file, prefer lockf() over flock().
1144 rad_lockfd(fd, LOCK_LEN);
1147 * If it WAS found in the cache, double-check it against
1148 * what is in the file.
1152 * If we didn't read anything, then this entry
1155 * Similarly, if the entry in the file doesn't
1156 * match what we recall, then nuke the cache
1159 read_size = read(fd, &u, sizeof(u));
1160 if ((read_size < 0) ||
1161 ((read_size > 0) && (read_size != sizeof(u)))) {
1163 * Bad read, or bad record.
1165 radlog(L_ERR, "rlm_radutmp: Badly formed file %s",
1168 return RLM_MODULE_FAIL;
1171 rad_assert(read_size != 0);
1174 * We've read a record, go poke at it.
1176 if (read_size > 0) {
1178 * If these aren't true, then
1180 * a) we have cached a "logout" entry,
1181 * which we don't do.
1183 * b) we have cached the wrong NAS address
1185 * c) we have cached the wrong NAS port.
1187 rad_assert(u.type == P_LOGIN);
1188 rad_assert(u.nas_address == utmp.nas_address);
1189 rad_assert(u.nas_port == utmp.nas_port);
1192 * An update for the same session.
1194 if (strncmp(utmp.session_id, u.session_id,
1195 sizeof(u.session_id)) == 0) {
1198 * It's a duplicate start, so we
1199 * don't bother writing it.
1201 if (status == PW_STATUS_START) {
1202 DEBUG2(" rlm_radutmp: Login entry for NAS %s port %u duplicate, ignoring it.",
1205 return RLM_MODULE_OK;
1209 * ALIVE for this session, keep the
1210 * original login time.
1212 } else if (status == PW_STATUS_ALIVE) {
1216 * Stop: delete it from our cache.
1218 } else if (status == PW_STATUS_STOP) {
1219 radutmp_simul_t *user, myUser;
1221 pthread_mutex_lock(&cache->mutex);
1222 rbtree_deletebydata(cache->nas_ports,
1225 strlcpy(myUser.login,
1226 u.login, sizeof(myUser.login));
1227 user = rbtree_finddata(inst->user_tree,
1229 rad_assert(user != NULL);
1230 rad_assert(user->simul_count > 0);
1232 user->simul_count--;
1233 if (user->simul_count == 0) {
1234 rbtree_deletebydata(inst->user_tree, user);
1237 pthread_mutex_unlock(&cache->mutex);
1241 * We don't know how to
1247 } else { /* session ID doesn't match */
1249 * STOP for the right NAS & port,
1250 * but the Acct-Session-Id is
1251 * different. This means that
1252 * we missed the original "stop",
1253 * and a new "start".
1255 if (status == PW_STATUS_STOP) {
1256 radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has old Acct-Session-ID, ignoring it.",
1259 return RLM_MODULE_OK;
1261 } /* checked session ID's */
1262 } /* else we haven't read anything from the file. */
1263 } /* else the entry wasn't cached, but could have been inserted */
1266 * Hmm... we may have received a start or alive packet
1267 * AFTER a stop or nas-down, in that case, we want to
1268 * discard the new packet. However, the original code
1269 * could over-write an idle record with a new login
1270 * record for another NAS && port, so we won't worry
1271 * about this case too much.
1275 * Seek to where the entry is, and write it blindly.
1277 lseek(fd, nas_port->offset, SEEK_SET); /* FIXME: err */
1279 if (status != PW_STATUS_STOP) {
1280 utmp.type = P_LOGIN;
1281 rad_assert(nas_port != NULL); /* it WAS cached */
1283 /* FIXME: maybe assert that the entry was deleted... */
1284 memcpy(&utmp, &u, sizeof(utmp));
1288 write(fd, &utmp, sizeof(utmp)); /* FIXME: err */
1290 close(fd); /* and implicitly release the locks */
1292 return RLM_MODULE_OK;
1296 * See if a user is already logged in. Sets request->simul_count
1297 * to the current session count for this user and sets
1298 * request->simul_mpp to 2 if it looks like a multilink attempt
1299 * based on the requested IP address, otherwise leaves
1300 * request->simul_mpp alone.
1302 * Check twice. If on the first pass the user exceeds his
1303 * max. number of logins, do a second pass and validate all
1304 * logins by querying the terminal server (using eg. SNMP).
1306 static int radutmp_checksimul(void *instance, REQUEST *request)
1312 char *call_num = NULL;
1314 rlm_radutmp_t *inst = instance;
1316 char filename[1024];
1317 radutmp_cache_t *cache;
1318 radutmp_simul_t *user, myUser;
1321 * Get the filename, via xlat.
1323 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
1326 * Future: look up filename in filename tree, to get
1327 * radutmp_cache_t pointer
1329 cache = &inst->cache;
1332 * For now, double-check the filename, to be sure it isn't
1335 if (!cache->filename) {
1336 cache->filename = strdup(filename);
1337 rad_assert(cache->filename != NULL);
1339 } else if (strcmp(cache->filename, filename) != 0) {
1340 radlog(L_ERR, "rlm_radutmp: We do not support dynamically named files.");
1341 return RLM_MODULE_FAIL;
1345 radius_xlat(login, sizeof(login), inst->username, request, NULL);
1347 return RLM_MODULE_NOOP;
1351 * WTF? This is probably wrong... we probably want to
1352 * be able to check users across multiple session accounting
1355 request->simul_count = 0;
1357 strlcpy(myUser.login, login, sizeof(myUser.login));
1358 pthread_mutex_lock(&inst->cache.mutex);
1359 user = rbtree_finddata(inst->user_tree, &myUser);
1360 if (user) request->simul_count = user->simul_count;
1361 user = NULL; /* someone else may delete it */
1362 pthread_mutex_unlock(&inst->cache.mutex);
1365 * The number of users logged in is OK,
1366 * OR, we've been told to not check the NAS.
1368 if ((request->simul_count < request->simul_max) ||
1370 return RLM_MODULE_OK;
1374 * The user is logged in at least N times, and
1375 * we're told to check the NAS. In that case,
1376 * we've got to read the file, and check each
1379 if ((fd = open(cache->filename, O_RDWR)) < 0) {
1381 * If the file doesn't exist, then no users
1384 if (errno == ENOENT) {
1385 request->simul_count = 0;
1386 return RLM_MODULE_OK;
1390 * Error accessing the file.
1392 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
1393 cache->filename, strerror(errno));
1394 return RLM_MODULE_FAIL;
1398 * Setup some stuff, like for MPP detection.
1400 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
1401 ipno = vp->vp_ipaddr;
1402 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
1403 call_num = vp->vp_strvalue;
1406 * lock the file while reading/writing.
1408 rad_lockfd(fd, LOCK_LEN);
1411 * FIXME: If we get a 'Start' for a user/nas/port which is
1412 * listed, but for which we did NOT get a 'Stop', then
1413 * it's not a duplicate session. This happens with
1414 * static IP's like DSL.
1416 request->simul_count = 0;
1417 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
1418 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
1419 (!inst->case_sensitive &&
1420 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
1421 (u.type == P_LOGIN)) {
1422 char session_id[sizeof(u.session_id) + 1];
1423 char utmp_login[sizeof(u.login) + 1];
1425 strlcpy(session_id, u.session_id, sizeof(session_id));
1428 * The login name MAY fill the whole field,
1429 * and thus won't be zero-filled.
1431 * Note that we take the user name from
1432 * the utmp file, as that's the canonical
1433 * form. The 'login' variable may contain
1434 * a string which is an upper/lowercase
1435 * version of u.login. When we call the
1436 * routine to check the terminal server,
1437 * the NAS may be case sensitive.
1439 * e.g. We ask if "bob" is using a port,
1440 * and the NAS says "no", because "BOB"
1441 * is using the port.
1443 strlcpy(utmp_login, u.login, sizeof(u.login));
1446 * rad_check_ts may take seconds
1447 * to return, and we don't want
1448 * to block everyone else while
1449 * that's happening. */
1450 rad_unlockfd(fd, LOCK_LEN);
1451 rcode = rad_check_ts(u.nas_address, u.nas_port,
1452 utmp_login, session_id);
1453 rad_lockfd(fd, LOCK_LEN);
1457 * Stale record - zap it.
1459 * Hmm... this ends up calling
1460 * the accounting section
1463 session_zap(request, u.nas_address,
1464 u.nas_port, login, session_id,
1465 u.framed_address, u.proto,0);
1467 else if (rcode == 1) {
1469 * User is still logged in.
1471 ++request->simul_count;
1474 * Does it look like a MPP attempt?
1476 if (strchr("SCPA", u.proto) &&
1477 ipno && u.framed_address == ipno)
1478 request->simul_mpp = 2;
1479 else if (strchr("SCPA", u.proto) && call_num &&
1480 !strncmp(u.caller_id,call_num,16))
1481 request->simul_mpp = 2;
1485 * Failed to check the terminal
1486 * server for duplicate logins:
1490 radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
1491 return RLM_MODULE_FAIL;
1495 close(fd); /* and implicitly release the locks */
1497 return RLM_MODULE_OK;
1500 /* globally exported name */
1501 module_t rlm_radutmp = {
1503 0, /* type: reserved */
1504 NULL, /* initialization */
1505 radutmp_instantiate, /* instantiation */
1507 NULL, /* authentication */
1508 NULL, /* authorization */
1509 NULL, /* preaccounting */
1510 radutmp_accounting, /* accounting */
1511 radutmp_checksimul, /* checksimul */
1512 NULL, /* pre-proxy */
1513 NULL, /* post-proxy */
1514 NULL /* post-auth */
1516 radutmp_detach, /* detach */