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,2006 The FreeRADIUS server project
21 * FIXME add copyrights
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/autoconf.h>
29 #include <sys/types.h>
41 #include <freeradius-devel/radiusd.h>
42 #include <freeradius-devel/radutmp.h>
43 #include <freeradius-devel/modules.h>
44 #include <freeradius-devel/rad_assert.h>
46 #define LOCK_LEN sizeof(struct radutmp)
48 static const char porttypes[] = "ASITX";
51 * used for caching radutmp lookups in the accounting component. The
52 * session (checksimul) component doesn't use it, but probably should.
54 typedef struct nas_port {
58 struct nas_port *next;
61 typedef struct rlm_radutmp_t {
62 NAS_PORT *nas_port_list;
71 static const CONF_PARSER module_config[] = {
72 { "filename", PW_TYPE_STRING_PTR,
73 offsetof(rlm_radutmp_t,filename), NULL, RADUTMP },
74 { "username", PW_TYPE_STRING_PTR,
75 offsetof(rlm_radutmp_t,username), NULL, "%{User-Name}"},
76 { "case_sensitive", PW_TYPE_BOOLEAN,
77 offsetof(rlm_radutmp_t,case_sensitive), NULL, "yes"},
78 { "check_with_nas", PW_TYPE_BOOLEAN,
79 offsetof(rlm_radutmp_t,check_nas), NULL, "yes"},
80 { "perm", PW_TYPE_INTEGER,
81 offsetof(rlm_radutmp_t,permission), NULL, "0644" },
82 { "callerid", PW_TYPE_BOOLEAN,
83 offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
84 { NULL, -1, 0, NULL, NULL } /* end the list */
87 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
91 inst = rad_malloc(sizeof(*inst));
95 memset(inst, 0, sizeof(*inst));
97 if (cf_section_parse(conf, inst, module_config)) {
102 inst->nas_port_list = NULL;
111 static int radutmp_detach(void *instance)
114 rlm_radutmp_t *inst = instance;
116 for (p = inst->nas_port_list ; p ; p=next) {
125 * Zap all users on a NAS from the radutmp file.
127 static int radutmp_zap(rlm_radutmp_t *inst,
128 const char *filename,
135 if (t == 0) time(&t);
137 fd = open(filename, O_RDWR);
139 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
140 filename, strerror(errno));
141 return RLM_MODULE_FAIL;
145 * Lock the utmp file, prefer lockf() over flock().
147 rad_lockfd(fd, LOCK_LEN);
150 * Find the entry for this NAS / portno combination.
152 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
153 if ((nasaddr != 0 && nasaddr != u.nas_address) ||
159 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
160 radlog(L_ERR, "rlm_radutmp: radutmp_zap: negative lseek!");
161 lseek(fd, (off_t)0, SEEK_SET);
165 write(fd, &u, sizeof(u));
167 close(fd); /* and implicitely release the locks */
173 * Lookup a NAS_PORT in the nas_port_list
175 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
179 for(cl = nas_port_list; cl; cl = cl->next)
180 if (nasaddr == cl->nasaddr &&
188 * Store logins in the RADIUS utmp file.
190 static int radutmp_accounting(void *instance, REQUEST *request)
192 struct radutmp ut, u;
195 uint32_t nas_address = 0;
196 uint32_t framed_address = 0;
200 int just_an_update = 0;
202 int nas_port_type = 0;
204 rlm_radutmp_t *inst = instance;
207 char ip_name[32]; /* 255.255.255.255 */
212 if (request->packet->src_ipaddr.af != AF_INET) {
213 DEBUG("rlm_radutmp: IPv6 not supported!");
214 return RLM_MODULE_NOOP;
218 * Which type is this.
220 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
221 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
222 return RLM_MODULE_NOOP;
227 * Look for weird reboot packets.
229 * ComOS (up to and including 3.5.1b20) does not send
230 * standard PW_STATUS_ACCOUNTING_XXX messages.
232 * Check for: o no Acct-Session-Time, or time of 0
233 * o Acct-Session-Id of "00000000".
235 * We could also check for NAS-Port, that attribute
236 * should NOT be present (but we don't right now).
238 if ((status != PW_STATUS_ACCOUNTING_ON) &&
239 (status != PW_STATUS_ACCOUNTING_OFF)) do {
243 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
244 == NULL || vp->lvalue == 0)
246 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
247 != NULL && vp->length == 8 &&
248 memcmp(vp->vp_strvalue, "00000000", 8) == 0)
250 if (check1 == 0 || check2 == 0) {
251 #if 0 /* Cisco sometimes sends START records without username. */
252 radlog(L_ERR, "rlm_radutmp: no username in record");
253 return RLM_MODULE_FAIL;
258 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
259 if (status == PW_STATUS_STOP)
260 status = PW_STATUS_ACCOUNTING_OFF;
261 if (status == PW_STATUS_START)
262 status = PW_STATUS_ACCOUNTING_ON;
266 memset(&ut, 0, sizeof(ut));
270 * First, find the interesting attributes.
272 for (vp = request->packet->vps; vp; vp = vp->next) {
273 switch (vp->attribute) {
274 case PW_LOGIN_IP_HOST:
275 case PW_FRAMED_IP_ADDRESS:
276 framed_address = vp->lvalue;
277 ut.framed_address = vp->lvalue;
279 case PW_FRAMED_PROTOCOL:
280 protocol = vp->lvalue;
282 case PW_NAS_IP_ADDRESS:
283 nas_address = vp->lvalue;
284 ut.nas_address = vp->lvalue;
287 ut.nas_port = vp->lvalue;
290 case PW_ACCT_DELAY_TIME:
291 ut.delay = vp->lvalue;
293 case PW_ACCT_SESSION_ID:
295 * If length > 8, only store the
298 off = vp->length - sizeof(ut.session_id);
300 * Ascend is br0ken - it adds a \0
301 * to the end of any string.
304 if (vp->length > 0 &&
305 vp->vp_strvalue[vp->length - 1] == 0)
307 if (off < 0) off = 0;
308 memcpy(ut.session_id, vp->vp_strvalue + off,
309 sizeof(ut.session_id));
311 case PW_NAS_PORT_TYPE:
313 ut.porttype = porttypes[vp->lvalue];
314 nas_port_type = vp->lvalue;
316 case PW_CALLING_STATION_ID:
317 if(inst->callerid_ok)
318 strlcpy(ut.caller_id,
319 (char *)vp->vp_strvalue,
320 sizeof(ut.caller_id));
326 * If we didn't find out the NAS address, use the
327 * originator's IP address.
329 if (nas_address == 0) {
330 nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
331 ut.nas_address = nas_address;
332 nas = client_name_old(&request->packet->src_ipaddr); /* MUST be a valid client */
334 } else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == nas_address) { /* might be a client, might not be. */
338 * Hack like 'client_name()', but with sane
341 cl = client_find_old(&request->packet->src_ipaddr);
342 if (!cl) rad_assert(0 == 1); /* WTF? */
343 if (cl->shortname && cl->shortname[0]) {
350 * The NAS isn't a client, it's behind
351 * a proxy server. In that case, just
352 * get the IP address.
354 nas = ip_ntoa(ip_name, nas_address);
358 * Set the protocol field.
360 if (protocol == PW_PPP)
362 else if (protocol == PW_SLIP)
366 ut.time = t - ut.delay;
369 * Get the utmp filename, via xlat.
371 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
374 * See if this was a reboot.
376 * Hmm... we may not want to zap all of the users when
377 * the NAS comes up, because of issues with receiving
378 * UDP packets out of order.
380 if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
381 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
383 radutmp_zap(inst, filename, nas_address, ut.time);
384 return RLM_MODULE_OK;
387 if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
388 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
390 radutmp_zap(inst, filename, nas_address, ut.time);
391 return RLM_MODULE_OK;
395 * If we don't know this type of entry pretend we succeeded.
397 if (status != PW_STATUS_START &&
398 status != PW_STATUS_STOP &&
399 status != PW_STATUS_ALIVE) {
400 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d)",
401 nas, ut.nas_port, status);
402 return RLM_MODULE_NOOP;
406 * Translate the User-Name attribute, or whatever else
407 * they told us to use.
410 radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
413 * Copy the previous translated user name.
415 strlcpy(ut.login, buffer, RUT_NAMESIZE);
418 * Perhaps we don't want to store this record into
419 * radutmp. We skip records:
421 * - without a NAS-Port (telnet / tcp access)
422 * - with the username "!root" (console admin login)
425 DEBUG2(" rlm_radutmp: No NAS-Port seen. Cannot do anything.");
426 DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!");
427 return RLM_MODULE_NOOP;
430 if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
431 DEBUG2(" rlm_radutmp: Not recording administrative user");
433 return RLM_MODULE_NOOP;
437 * Enter into the radutmp file.
439 fd = open(filename, O_RDWR|O_CREAT, inst->permission);
441 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
442 filename, strerror(errno));
443 return RLM_MODULE_FAIL;
447 * Lock the utmp file, prefer lockf() over flock().
449 rad_lockfd(fd, LOCK_LEN);
452 * Find the entry for this NAS / portno combination.
454 if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
455 ut.nas_port)) != NULL) {
456 lseek(fd, (off_t)cache->offset, SEEK_SET);
461 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
463 if (u.nas_address != ut.nas_address ||
464 u.nas_port != ut.nas_port)
468 * Don't compare stop records to unused entries.
470 if (status == PW_STATUS_STOP &&
475 if (status == PW_STATUS_STOP &&
476 strncmp(ut.session_id, u.session_id,
477 sizeof(u.session_id)) != 0) {
479 * Don't complain if this is not a
480 * login record (some clients can
481 * send _only_ logout records).
483 if (u.type == P_LOGIN)
484 radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has wrong ID",
490 if (status == PW_STATUS_START &&
491 strncmp(ut.session_id, u.session_id,
492 sizeof(u.session_id)) == 0 &&
494 if (u.type == P_LOGIN) {
495 radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
500 radlog(L_ERR, "rlm_radutmp: Login entry for NAS %s port %u wrong order",
507 * FIXME: the ALIVE record could need
508 * some more checking, but anyway I'd
509 * rather rewrite this mess -- miquels.
511 if (status == PW_STATUS_ALIVE &&
512 strncmp(ut.session_id, u.session_id,
513 sizeof(u.session_id)) == 0 &&
516 * Keep the original login time.
523 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
524 radlog(L_ERR, "rlm_radutmp: negative lseek!");
525 lseek(fd, (off_t)0, SEEK_SET);
531 } /* read the file until we find a match */
534 * Found the entry, do start/update it with
535 * the information from the packet.
537 if (r >= 0 && (status == PW_STATUS_START ||
538 status == PW_STATUS_ALIVE)) {
540 * Remember where the entry was, because it's
541 * easier than searching through the entire file.
544 cache = rad_malloc(sizeof(NAS_PORT));
545 cache->nasaddr = ut.nas_address;
546 cache->port = ut.nas_port;
548 cache->next = inst->nas_port_list;
549 inst->nas_port_list = cache;
553 write(fd, &ut, sizeof(u));
557 * The user has logged off, delete the entry by
558 * re-writing it in place.
560 if (status == PW_STATUS_STOP) {
565 write(fd, &u, sizeof(u));
567 radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
572 close(fd); /* and implicitely release the locks */
574 return RLM_MODULE_OK;
578 * See if a user is already logged in. Sets request->simul_count to the
579 * current session count for this user and sets request->simul_mpp to 2
580 * if it looks like a multilink attempt based on the requested IP
581 * address, otherwise leaves request->simul_mpp alone.
583 * Check twice. If on the first pass the user exceeds his
584 * max. number of logins, do a second pass and validate all
585 * logins by querying the terminal server (using eg. SNMP).
587 static int radutmp_checksimul(void *instance, REQUEST *request)
593 char *call_num = NULL;
595 rlm_radutmp_t *inst = instance;
600 * Get the filename, via xlat.
602 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
604 if ((fd = open(filename, O_RDWR)) < 0) {
606 * If the file doesn't exist, then no users
609 if (errno == ENOENT) {
610 request->simul_count=0;
611 return RLM_MODULE_OK;
615 * Error accessing the file.
617 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
618 filename, strerror(errno));
619 return RLM_MODULE_FAIL;
623 radius_xlat(login, sizeof(login), inst->username, request, NULL);
625 return RLM_MODULE_NOOP;
629 * WTF? This is probably wrong... we probably want to
630 * be able to check users across multiple session accounting
633 request->simul_count = 0;
636 * Loop over utmp, counting how many people MAY be logged in.
638 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
639 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
640 (!inst->case_sensitive &&
641 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
642 (u.type == P_LOGIN)) {
643 ++request->simul_count;
648 * The number of users logged in is OK,
649 * OR, we've been told to not check the NAS.
651 if ((request->simul_count < request->simul_max) ||
654 return RLM_MODULE_OK;
656 lseek(fd, (off_t)0, SEEK_SET);
659 * Setup some stuff, like for MPP detection.
661 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
663 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
664 call_num = vp->vp_strvalue;
667 * lock the file while reading/writing.
669 rad_lockfd(fd, LOCK_LEN);
672 * FIXME: If we get a 'Start' for a user/nas/port which is
673 * listed, but for which we did NOT get a 'Stop', then
674 * it's not a duplicate session. This happens with
675 * static IP's like DSL.
677 request->simul_count = 0;
678 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
679 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
680 (!inst->case_sensitive &&
681 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
682 (u.type == P_LOGIN)) {
683 char session_id[sizeof(u.session_id) + 1];
684 char utmp_login[sizeof(u.login) + 1];
686 strlcpy(session_id, u.session_id, sizeof(session_id));
689 * The login name MAY fill the whole field,
690 * and thus won't be zero-filled.
692 * Note that we take the user name from
693 * the utmp file, as that's the canonical
694 * form. The 'login' variable may contain
695 * a string which is an upper/lowercase
696 * version of u.login. When we call the
697 * routine to check the terminal server,
698 * the NAS may be case sensitive.
700 * e.g. We ask if "bob" is using a port,
701 * and the NAS says "no", because "BOB"
704 strlcpy(utmp_login, u.login, sizeof(u.login));
707 * rad_check_ts may take seconds
708 * to return, and we don't want
709 * to block everyone else while
710 * that's happening. */
711 rad_unlockfd(fd, LOCK_LEN);
712 rcode = rad_check_ts(u.nas_address, u.nas_port,
713 utmp_login, session_id);
714 rad_lockfd(fd, LOCK_LEN);
718 * Stale record - zap it.
720 session_zap(request, u.nas_address,
721 u.nas_port, login, session_id,
722 u.framed_address, u.proto,0);
724 else if (rcode == 1) {
726 * User is still logged in.
728 ++request->simul_count;
731 * Does it look like a MPP attempt?
733 if (strchr("SCPA", u.proto) &&
734 ipno && u.framed_address == ipno)
735 request->simul_mpp = 2;
736 else if (strchr("SCPA", u.proto) && call_num &&
737 !strncmp(u.caller_id,call_num,16))
738 request->simul_mpp = 2;
742 * Failed to check the terminal
743 * server for duplicate logins:
747 radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
748 return RLM_MODULE_FAIL;
752 close(fd); /* and implicitely release the locks */
754 return RLM_MODULE_OK;
757 /* globally exported name */
758 module_t rlm_radutmp = {
761 0, /* type: reserved */
762 radutmp_instantiate, /* instantiation */
763 radutmp_detach, /* detach */
765 NULL, /* authentication */
766 NULL, /* authorization */
767 NULL, /* preaccounting */
768 radutmp_accounting, /* accounting */
769 radutmp_checksimul, /* checksimul */
770 NULL, /* pre-proxy */
771 NULL, /* post-proxy */