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/radiusd.h>
28 #include <freeradius-devel/radutmp.h>
29 #include <freeradius-devel/modules.h>
30 #include <freeradius-devel/rad_assert.h>
37 #define LOCK_LEN sizeof(struct radutmp)
39 static const char porttypes[] = "ASITX";
42 * used for caching radutmp lookups in the accounting component. The
43 * session (checksimul) component doesn't use it, but probably should.
45 typedef struct nas_port {
49 struct nas_port *next;
52 typedef struct rlm_radutmp_t {
53 NAS_PORT *nas_port_list;
62 static const CONF_PARSER module_config[] = {
63 { "filename", PW_TYPE_STRING_PTR,
64 offsetof(rlm_radutmp_t,filename), NULL, RADUTMP },
65 { "username", PW_TYPE_STRING_PTR,
66 offsetof(rlm_radutmp_t,username), NULL, "%{User-Name}"},
67 { "case_sensitive", PW_TYPE_BOOLEAN,
68 offsetof(rlm_radutmp_t,case_sensitive), NULL, "yes"},
69 { "check_with_nas", PW_TYPE_BOOLEAN,
70 offsetof(rlm_radutmp_t,check_nas), NULL, "yes"},
71 { "perm", PW_TYPE_INTEGER,
72 offsetof(rlm_radutmp_t,permission), NULL, "0644" },
73 { "callerid", PW_TYPE_BOOLEAN,
74 offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
75 { NULL, -1, 0, NULL, NULL } /* end the list */
78 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
82 inst = rad_malloc(sizeof(*inst));
86 memset(inst, 0, sizeof(*inst));
88 if (cf_section_parse(conf, inst, module_config)) {
93 inst->nas_port_list = NULL;
102 static int radutmp_detach(void *instance)
105 rlm_radutmp_t *inst = instance;
107 for (p = inst->nas_port_list ; p ; p=next) {
116 * Zap all users on a NAS from the radutmp file.
118 static int radutmp_zap(UNUSED rlm_radutmp_t *inst,
119 const char *filename,
126 if (t == 0) time(&t);
128 fd = open(filename, O_RDWR);
130 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
131 filename, strerror(errno));
132 return RLM_MODULE_FAIL;
136 * Lock the utmp file, prefer lockf() over flock().
138 rad_lockfd(fd, LOCK_LEN);
141 * Find the entry for this NAS / portno combination.
143 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
144 if ((nasaddr != 0 && nasaddr != u.nas_address) ||
150 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
151 radlog(L_ERR, "rlm_radutmp: radutmp_zap: negative lseek!");
152 lseek(fd, (off_t)0, SEEK_SET);
156 write(fd, &u, sizeof(u));
158 close(fd); /* and implicitely release the locks */
164 * Lookup a NAS_PORT in the nas_port_list
166 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
170 for(cl = nas_port_list; cl; cl = cl->next)
171 if (nasaddr == cl->nasaddr &&
178 #ifdef WITH_ACCOUNTING
180 * Store logins in the RADIUS utmp file.
182 static int radutmp_accounting(void *instance, REQUEST *request)
184 struct radutmp ut, u;
192 rlm_radutmp_t *inst = instance;
195 char ip_name[32]; /* 255.255.255.255 */
200 if (request->packet->src_ipaddr.af != AF_INET) {
201 DEBUG("rlm_radutmp: IPv6 not supported!");
202 return RLM_MODULE_NOOP;
206 * Which type is this.
208 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0)) == NULL) {
209 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
210 return RLM_MODULE_NOOP;
212 status = vp->vp_integer;
215 * Look for weird reboot packets.
217 * ComOS (up to and including 3.5.1b20) does not send
218 * standard PW_STATUS_ACCOUNTING_XXX messages.
220 * Check for: o no Acct-Session-Time, or time of 0
221 * o Acct-Session-Id of "00000000".
223 * We could also check for NAS-Port, that attribute
224 * should NOT be present (but we don't right now).
226 if ((status != PW_STATUS_ACCOUNTING_ON) &&
227 (status != PW_STATUS_ACCOUNTING_OFF)) do {
231 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0))
232 == NULL || vp->vp_date == 0)
234 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID, 0))
235 != NULL && vp->length == 8 &&
236 memcmp(vp->vp_strvalue, "00000000", 8) == 0)
238 if (check1 == 0 || check2 == 0) {
239 #if 0 /* Cisco sometimes sends START records without username. */
240 radlog(L_ERR, "rlm_radutmp: no username in record");
241 return RLM_MODULE_FAIL;
246 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
247 if (status == PW_STATUS_STOP)
248 status = PW_STATUS_ACCOUNTING_OFF;
249 if (status == PW_STATUS_START)
250 status = PW_STATUS_ACCOUNTING_ON;
254 memset(&ut, 0, sizeof(ut));
256 ut.nas_address = htonl(INADDR_NONE);
259 * First, find the interesting attributes.
261 for (vp = request->packet->vps; vp; vp = vp->next) {
262 switch (vp->attribute) {
263 case PW_LOGIN_IP_HOST:
264 case PW_FRAMED_IP_ADDRESS:
265 ut.framed_address = vp->vp_ipaddr;
267 case PW_FRAMED_PROTOCOL:
268 protocol = vp->vp_integer;
270 case PW_NAS_IP_ADDRESS:
271 ut.nas_address = vp->vp_ipaddr;
274 ut.nas_port = vp->vp_integer;
277 case PW_ACCT_DELAY_TIME:
278 ut.delay = vp->vp_integer;
280 case PW_ACCT_SESSION_ID:
282 * If length > 8, only store the
285 off = vp->length - sizeof(ut.session_id);
287 * Ascend is br0ken - it adds a \0
288 * to the end of any string.
291 if (vp->length > 0 &&
292 vp->vp_strvalue[vp->length - 1] == 0)
294 if (off < 0) off = 0;
295 memcpy(ut.session_id, vp->vp_strvalue + off,
296 sizeof(ut.session_id));
298 case PW_NAS_PORT_TYPE:
299 if (vp->vp_integer <= 4)
300 ut.porttype = porttypes[vp->vp_integer];
302 case PW_CALLING_STATION_ID:
303 if(inst->callerid_ok)
304 strlcpy(ut.caller_id,
305 (char *)vp->vp_strvalue,
306 sizeof(ut.caller_id));
312 * If we didn't find out the NAS address, use the
313 * originator's IP address.
315 if (ut.nas_address == htonl(INADDR_NONE)) {
316 ut.nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
317 nas = request->client->shortname;
319 } else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == ut.nas_address) { /* might be a client, might not be. */
320 nas = request->client->shortname;
324 * The NAS isn't a client, it's behind
325 * a proxy server. In that case, just
326 * get the IP address.
328 nas = ip_ntoa(ip_name, ut.nas_address);
332 * Set the protocol field.
334 if (protocol == PW_PPP)
336 else if (protocol == PW_SLIP)
340 ut.time = t - ut.delay;
343 * Get the utmp filename, via xlat.
345 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
348 * See if this was a reboot.
350 * Hmm... we may not want to zap all of the users when
351 * the NAS comes up, because of issues with receiving
352 * UDP packets out of order.
354 if (status == PW_STATUS_ACCOUNTING_ON &&
355 (ut.nas_address != htonl(INADDR_NONE))) {
356 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
358 radutmp_zap(inst, filename, ut.nas_address, ut.time);
359 return RLM_MODULE_OK;
362 if (status == PW_STATUS_ACCOUNTING_OFF &&
363 (ut.nas_address != htonl(INADDR_NONE))) {
364 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
366 radutmp_zap(inst, filename, ut.nas_address, ut.time);
367 return RLM_MODULE_OK;
371 * If we don't know this type of entry pretend we succeeded.
373 if (status != PW_STATUS_START &&
374 status != PW_STATUS_STOP &&
375 status != PW_STATUS_ALIVE) {
376 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d)",
377 nas, ut.nas_port, status);
378 return RLM_MODULE_NOOP;
382 * Translate the User-Name attribute, or whatever else
383 * they told us to use.
386 radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
389 * Copy the previous translated user name.
391 strlcpy(ut.login, buffer, RUT_NAMESIZE);
394 * Perhaps we don't want to store this record into
395 * radutmp. We skip records:
397 * - without a NAS-Port (telnet / tcp access)
398 * - with the username "!root" (console admin login)
401 DEBUG2(" rlm_radutmp: No NAS-Port seen. Cannot do anything.");
402 DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!");
403 return RLM_MODULE_NOOP;
406 if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
407 DEBUG2(" rlm_radutmp: Not recording administrative user");
409 return RLM_MODULE_NOOP;
413 * Enter into the radutmp file.
415 fd = open(filename, O_RDWR|O_CREAT, inst->permission);
417 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
418 filename, strerror(errno));
419 return RLM_MODULE_FAIL;
423 * Lock the utmp file, prefer lockf() over flock().
425 rad_lockfd(fd, LOCK_LEN);
428 * Find the entry for this NAS / portno combination.
430 if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
431 ut.nas_port)) != NULL) {
432 lseek(fd, (off_t)cache->offset, SEEK_SET);
437 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
439 if (u.nas_address != ut.nas_address ||
440 u.nas_port != ut.nas_port)
444 * Don't compare stop records to unused entries.
446 if (status == PW_STATUS_STOP &&
451 if (status == PW_STATUS_STOP &&
452 strncmp(ut.session_id, u.session_id,
453 sizeof(u.session_id)) != 0) {
455 * Don't complain if this is not a
456 * login record (some clients can
457 * send _only_ logout records).
459 if (u.type == P_LOGIN)
460 radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has wrong ID",
466 if (status == PW_STATUS_START &&
467 strncmp(ut.session_id, u.session_id,
468 sizeof(u.session_id)) == 0 &&
470 if (u.type == P_LOGIN) {
471 radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
476 radlog(L_ERR, "rlm_radutmp: Login entry for NAS %s port %u wrong order",
483 * FIXME: the ALIVE record could need
484 * some more checking, but anyway I'd
485 * rather rewrite this mess -- miquels.
487 if (status == PW_STATUS_ALIVE &&
488 strncmp(ut.session_id, u.session_id,
489 sizeof(u.session_id)) == 0 &&
492 * Keep the original login time.
497 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
498 radlog(L_ERR, "rlm_radutmp: negative lseek!");
499 lseek(fd, (off_t)0, SEEK_SET);
505 } /* read the file until we find a match */
508 * Found the entry, do start/update it with
509 * the information from the packet.
511 if (r >= 0 && (status == PW_STATUS_START ||
512 status == PW_STATUS_ALIVE)) {
514 * Remember where the entry was, because it's
515 * easier than searching through the entire file.
518 cache = rad_malloc(sizeof(NAS_PORT));
519 cache->nasaddr = ut.nas_address;
520 cache->port = ut.nas_port;
522 cache->next = inst->nas_port_list;
523 inst->nas_port_list = cache;
527 write(fd, &ut, sizeof(u));
531 * The user has logged off, delete the entry by
532 * re-writing it in place.
534 if (status == PW_STATUS_STOP) {
539 write(fd, &u, sizeof(u));
541 radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
545 close(fd); /* and implicitely release the locks */
547 return RLM_MODULE_OK;
551 #ifdef WITH_SESSION_MGMT
553 * See if a user is already logged in. Sets request->simul_count to the
554 * current session count for this user and sets request->simul_mpp to 2
555 * if it looks like a multilink attempt based on the requested IP
556 * address, otherwise leaves request->simul_mpp alone.
558 * Check twice. If on the first pass the user exceeds his
559 * max. number of logins, do a second pass and validate all
560 * logins by querying the terminal server (using eg. SNMP).
562 static int radutmp_checksimul(void *instance, REQUEST *request)
568 char *call_num = NULL;
570 rlm_radutmp_t *inst = instance;
575 * Get the filename, via xlat.
577 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
579 if ((fd = open(filename, O_RDWR)) < 0) {
581 * If the file doesn't exist, then no users
584 if (errno == ENOENT) {
585 request->simul_count=0;
586 return RLM_MODULE_OK;
590 * Error accessing the file.
592 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
593 filename, strerror(errno));
594 return RLM_MODULE_FAIL;
598 radius_xlat(login, sizeof(login), inst->username, request, NULL);
600 return RLM_MODULE_NOOP;
604 * WTF? This is probably wrong... we probably want to
605 * be able to check users across multiple session accounting
608 request->simul_count = 0;
611 * Loop over utmp, counting how many people MAY be logged in.
613 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
614 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
615 (!inst->case_sensitive &&
616 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
617 (u.type == P_LOGIN)) {
618 ++request->simul_count;
623 * The number of users logged in is OK,
624 * OR, we've been told to not check the NAS.
626 if ((request->simul_count < request->simul_max) ||
629 return RLM_MODULE_OK;
631 lseek(fd, (off_t)0, SEEK_SET);
634 * Setup some stuff, like for MPP detection.
636 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0)) != NULL)
637 ipno = vp->vp_ipaddr;
638 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0)) != NULL)
639 call_num = vp->vp_strvalue;
642 * lock the file while reading/writing.
644 rad_lockfd(fd, LOCK_LEN);
647 * FIXME: If we get a 'Start' for a user/nas/port which is
648 * listed, but for which we did NOT get a 'Stop', then
649 * it's not a duplicate session. This happens with
650 * static IP's like DSL.
652 request->simul_count = 0;
653 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
654 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
655 (!inst->case_sensitive &&
656 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
657 (u.type == P_LOGIN)) {
658 char session_id[sizeof(u.session_id) + 1];
659 char utmp_login[sizeof(u.login) + 1];
661 strlcpy(session_id, u.session_id, sizeof(session_id));
664 * The login name MAY fill the whole field,
665 * and thus won't be zero-filled.
667 * Note that we take the user name from
668 * the utmp file, as that's the canonical
669 * form. The 'login' variable may contain
670 * a string which is an upper/lowercase
671 * version of u.login. When we call the
672 * routine to check the terminal server,
673 * the NAS may be case sensitive.
675 * e.g. We ask if "bob" is using a port,
676 * and the NAS says "no", because "BOB"
679 strlcpy(utmp_login, u.login, sizeof(u.login));
682 * rad_check_ts may take seconds
683 * to return, and we don't want
684 * to block everyone else while
685 * that's happening. */
686 rad_unlockfd(fd, LOCK_LEN);
687 rcode = rad_check_ts(u.nas_address, u.nas_port,
688 utmp_login, session_id);
689 rad_lockfd(fd, LOCK_LEN);
693 * Stale record - zap it.
695 session_zap(request, u.nas_address,
696 u.nas_port, login, session_id,
697 u.framed_address, u.proto,0);
699 else if (rcode == 1) {
701 * User is still logged in.
703 ++request->simul_count;
706 * Does it look like a MPP attempt?
708 if (strchr("SCPA", u.proto) &&
709 ipno && u.framed_address == ipno)
710 request->simul_mpp = 2;
711 else if (strchr("SCPA", u.proto) && call_num &&
712 !strncmp(u.caller_id,call_num,16))
713 request->simul_mpp = 2;
717 * Failed to check the terminal
718 * server for duplicate logins:
722 radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
723 return RLM_MODULE_FAIL;
727 close(fd); /* and implicitely release the locks */
729 return RLM_MODULE_OK;
733 /* globally exported name */
734 module_t rlm_radutmp = {
737 RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
738 radutmp_instantiate, /* instantiation */
739 radutmp_detach, /* detach */
741 NULL, /* authentication */
742 NULL, /* authorization */
743 NULL, /* preaccounting */
744 #ifdef WITH_ACCOUNTING
745 radutmp_accounting, /* accounting */
749 #ifdef WITH_SESSION_MGMT
750 radutmp_checksimul, /* checksimul */
754 NULL, /* pre-proxy */
755 NULL, /* post-proxy */