2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2 if the
4 * License as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief Tracks sessions.
21 * @copyright 2000-2013 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 component. The
42 * session (checksimul) component doesn't use it, but probably should.
44 typedef struct nas_port {
48 struct nas_port *next;
51 typedef struct rlm_radutmp_t {
52 NAS_PORT *nas_port_list;
61 static const CONF_PARSER module_config[] = {
62 { "filename", PW_TYPE_STRING_PTR,
63 offsetof(rlm_radutmp_t,filename), NULL, RADUTMP },
64 { "username", PW_TYPE_STRING_PTR,
65 offsetof(rlm_radutmp_t,username), NULL, "%{User-Name}"},
66 { "case_sensitive", PW_TYPE_BOOLEAN,
67 offsetof(rlm_radutmp_t,case_sensitive), NULL, "yes"},
68 { "check_with_nas", PW_TYPE_BOOLEAN,
69 offsetof(rlm_radutmp_t,check_nas), NULL, "yes"},
70 { "perm", PW_TYPE_INTEGER,
71 offsetof(rlm_radutmp_t,permission), NULL, "0644" },
72 { "callerid", PW_TYPE_BOOLEAN,
73 offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
74 { NULL, -1, 0, NULL, NULL } /* end the list */
77 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
81 *instance = inst = talloc_zero(conf, rlm_radutmp_t);
84 if (cf_section_parse(conf, inst, module_config)) {
88 inst->nas_port_list = NULL;
93 * Zap all users on a NAS from the radutmp file.
95 static int radutmp_zap(UNUSED rlm_radutmp_t *inst,
103 if (t == 0) time(&t);
105 fd = open(filename, O_RDWR);
107 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
108 filename, strerror(errno));
109 return RLM_MODULE_FAIL;
113 * Lock the utmp file, prefer lockf() over flock().
115 if (rad_lockfd(fd, LOCK_LEN) < 0)
117 radlog(L_ERR, "rlm_radutmp: Failed to acquire lock on file %s:"
118 " %s", filename, strerror(errno));
122 return RLM_MODULE_FAIL;
126 * Find the entry for this NAS / portno combination.
128 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
129 if ((nasaddr != 0 && nasaddr != u.nas_address) ||
135 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
136 radlog(L_ERR, "rlm_radutmp: radutmp_zap: negative lseek!");
137 lseek(fd, (off_t)0, SEEK_SET);
141 write(fd, &u, sizeof(u));
143 close(fd); /* and implicitely release the locks */
149 * Lookup a NAS_PORT in the nas_port_list
151 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
155 for(cl = nas_port_list; cl; cl = cl->next)
156 if (nasaddr == cl->nasaddr &&
163 #ifdef WITH_ACCOUNTING
165 * Store logins in the RADIUS utmp file.
167 static rlm_rcode_t radutmp_accounting(void *instance, REQUEST *request)
169 struct radutmp ut, u;
177 rlm_radutmp_t *inst = instance;
180 char ip_name[32]; /* 255.255.255.255 */
185 if (request->packet->src_ipaddr.af != AF_INET) {
186 DEBUG("rlm_radutmp: IPv6 not supported!");
187 return RLM_MODULE_NOOP;
191 * Which type is this.
193 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
194 RDEBUG("No Accounting-Status-Type record.");
195 return RLM_MODULE_NOOP;
197 status = vp->vp_integer;
200 * Look for weird reboot packets.
202 * ComOS (up to and including 3.5.1b20) does not send
203 * standard PW_STATUS_ACCOUNTING_XXX messages.
205 * Check for: o no Acct-Session-Time, or time of 0
206 * o Acct-Session-Id of "00000000".
208 * We could also check for NAS-Port, that attribute
209 * should NOT be present (but we don't right now).
211 if ((status != PW_STATUS_ACCOUNTING_ON) &&
212 (status != PW_STATUS_ACCOUNTING_OFF)) do {
216 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY))
217 == NULL || vp->vp_date == 0)
219 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID, 0, TAG_ANY))
220 != NULL && vp->length == 8 &&
221 memcmp(vp->vp_strvalue, "00000000", 8) == 0)
223 if (check1 == 0 || check2 == 0) {
224 #if 0 /* Cisco sometimes sends START records without username. */
225 radlog(L_ERR, "rlm_radutmp: no username in record");
226 return RLM_MODULE_FAIL;
231 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
232 if (status == PW_STATUS_STOP)
233 status = PW_STATUS_ACCOUNTING_OFF;
234 if (status == PW_STATUS_START)
235 status = PW_STATUS_ACCOUNTING_ON;
239 memset(&ut, 0, sizeof(ut));
241 ut.nas_address = htonl(INADDR_NONE);
244 * First, find the interesting attributes.
246 for (vp = request->packet->vps; vp; vp = vp->next) {
247 if (!vp->da->vendor) switch (vp->da->attr) {
248 case PW_LOGIN_IP_HOST:
249 case PW_FRAMED_IP_ADDRESS:
250 ut.framed_address = vp->vp_ipaddr;
252 case PW_FRAMED_PROTOCOL:
253 protocol = vp->vp_integer;
255 case PW_NAS_IP_ADDRESS:
256 ut.nas_address = vp->vp_ipaddr;
259 ut.nas_port = vp->vp_integer;
262 case PW_ACCT_DELAY_TIME:
263 ut.delay = vp->vp_integer;
265 case PW_ACCT_SESSION_ID:
267 * If length > 8, only store the
270 off = vp->length - sizeof(ut.session_id);
272 * Ascend is br0ken - it adds a \0
273 * to the end of any string.
276 if (vp->length > 0 &&
277 vp->vp_strvalue[vp->length - 1] == 0)
279 if (off < 0) off = 0;
280 memcpy(ut.session_id, vp->vp_strvalue + off,
281 sizeof(ut.session_id));
283 case PW_NAS_PORT_TYPE:
284 if (vp->vp_integer <= 4)
285 ut.porttype = porttypes[vp->vp_integer];
287 case PW_CALLING_STATION_ID:
288 if(inst->callerid_ok)
289 strlcpy(ut.caller_id,
290 (char *)vp->vp_strvalue,
291 sizeof(ut.caller_id));
297 * If we didn't find out the NAS address, use the
298 * originator's IP address.
300 if (ut.nas_address == htonl(INADDR_NONE)) {
301 ut.nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
302 nas = request->client->shortname;
304 } else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == ut.nas_address) { /* might be a client, might not be. */
305 nas = request->client->shortname;
309 * The NAS isn't a client, it's behind
310 * a proxy server. In that case, just
311 * get the IP address.
313 nas = ip_ntoa(ip_name, ut.nas_address);
317 * Set the protocol field.
319 if (protocol == PW_PPP)
321 else if (protocol == PW_SLIP)
325 ut.time = t - ut.delay;
328 * Get the utmp filename, via xlat.
330 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL, NULL);
333 * See if this was a reboot.
335 * Hmm... we may not want to zap all of the users when
336 * the NAS comes up, because of issues with receiving
337 * UDP packets out of order.
339 if (status == PW_STATUS_ACCOUNTING_ON &&
340 (ut.nas_address != htonl(INADDR_NONE))) {
341 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
343 radutmp_zap(inst, filename, ut.nas_address, ut.time);
344 return RLM_MODULE_OK;
347 if (status == PW_STATUS_ACCOUNTING_OFF &&
348 (ut.nas_address != htonl(INADDR_NONE))) {
349 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
351 radutmp_zap(inst, filename, ut.nas_address, ut.time);
352 return RLM_MODULE_OK;
356 * If we don't know this type of entry pretend we succeeded.
358 if (status != PW_STATUS_START &&
359 status != PW_STATUS_STOP &&
360 status != PW_STATUS_ALIVE) {
361 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d)",
362 nas, ut.nas_port, status);
363 return RLM_MODULE_NOOP;
367 * Translate the User-Name attribute, or whatever else
368 * they told us to use.
371 radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL, NULL);
374 * Copy the previous translated user name.
376 strlcpy(ut.login, buffer, RUT_NAMESIZE);
379 * Perhaps we don't want to store this record into
380 * radutmp. We skip records:
382 * - without a NAS-Port (telnet / tcp access)
383 * - with the username "!root" (console admin login)
386 DEBUG2(" rlm_radutmp: No NAS-Port seen. Cannot do anything.");
387 DEBUG2W("checkrad will probably not work!");
388 return RLM_MODULE_NOOP;
391 if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
392 DEBUG2(" rlm_radutmp: Not recording administrative user");
394 return RLM_MODULE_NOOP;
398 * Enter into the radutmp file.
400 fd = open(filename, O_RDWR|O_CREAT, inst->permission);
402 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
403 filename, strerror(errno));
404 return RLM_MODULE_FAIL;
408 * Lock the utmp file, prefer lockf() over flock().
410 rad_lockfd(fd, LOCK_LEN);
413 * Find the entry for this NAS / portno combination.
415 if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
416 ut.nas_port)) != NULL) {
417 lseek(fd, (off_t)cache->offset, SEEK_SET);
422 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
424 if (u.nas_address != ut.nas_address ||
425 u.nas_port != ut.nas_port)
429 * Don't compare stop records to unused entries.
431 if (status == PW_STATUS_STOP &&
436 if (status == PW_STATUS_STOP &&
437 strncmp(ut.session_id, u.session_id,
438 sizeof(u.session_id)) != 0) {
440 * Don't complain if this is not a
441 * login record (some clients can
442 * send _only_ logout records).
444 if (u.type == P_LOGIN)
445 radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has wrong ID",
451 if (status == PW_STATUS_START &&
452 strncmp(ut.session_id, u.session_id,
453 sizeof(u.session_id)) == 0 &&
455 if (u.type == P_LOGIN) {
456 radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
461 radlog(L_ERR, "rlm_radutmp: Login entry for NAS %s port %u wrong order",
468 * FIXME: the ALIVE record could need
469 * some more checking, but anyway I'd
470 * rather rewrite this mess -- miquels.
472 if (status == PW_STATUS_ALIVE &&
473 strncmp(ut.session_id, u.session_id,
474 sizeof(u.session_id)) == 0 &&
477 * Keep the original login time.
482 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
483 radlog(L_ERR, "rlm_radutmp: negative lseek!");
484 lseek(fd, (off_t)0, SEEK_SET);
490 } /* read the file until we find a match */
493 * Found the entry, do start/update it with
494 * the information from the packet.
496 if (r >= 0 && (status == PW_STATUS_START ||
497 status == PW_STATUS_ALIVE)) {
499 * Remember where the entry was, because it's
500 * easier than searching through the entire file.
503 cache = talloc_zero(inst, NAS_PORT);
505 cache->nasaddr = ut.nas_address;
506 cache->port = ut.nas_port;
508 cache->next = inst->nas_port_list;
509 inst->nas_port_list = cache;
514 write(fd, &ut, sizeof(u));
518 * The user has logged off, delete the entry by
519 * re-writing it in place.
521 if (status == PW_STATUS_STOP) {
526 write(fd, &u, sizeof(u));
528 radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
532 close(fd); /* and implicitely release the locks */
534 return RLM_MODULE_OK;
538 #ifdef WITH_SESSION_MGMT
540 * See if a user is already logged in. Sets request->simul_count to the
541 * current session count for this user and sets request->simul_mpp to 2
542 * if it looks like a multilink attempt based on the requested IP
543 * address, otherwise leaves request->simul_mpp alone.
545 * Check twice. If on the first pass the user exceeds his
546 * max. number of logins, do a second pass and validate all
547 * logins by querying the terminal server (using eg. SNMP).
549 static rlm_rcode_t radutmp_checksimul(void *instance, REQUEST *request)
555 char *call_num = NULL;
557 rlm_radutmp_t *inst = instance;
562 * Get the filename, via xlat.
564 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL, NULL);
566 if ((fd = open(filename, O_RDWR)) < 0) {
568 * If the file doesn't exist, then no users
571 if (errno == ENOENT) {
572 request->simul_count=0;
573 return RLM_MODULE_OK;
577 * Error accessing the file.
579 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
580 filename, strerror(errno));
581 return RLM_MODULE_FAIL;
585 radius_xlat(login, sizeof(login), inst->username, request, NULL, NULL);
588 return RLM_MODULE_NOOP;
592 * WTF? This is probably wrong... we probably want to
593 * be able to check users across multiple session accounting
596 request->simul_count = 0;
599 * Loop over utmp, counting how many people MAY be logged in.
601 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
602 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
603 (!inst->case_sensitive &&
604 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
605 (u.type == P_LOGIN)) {
606 ++request->simul_count;
611 * The number of users logged in is OK,
612 * OR, we've been told to not check the NAS.
614 if ((request->simul_count < request->simul_max) ||
617 return RLM_MODULE_OK;
619 lseek(fd, (off_t)0, SEEK_SET);
622 * Setup some stuff, like for MPP detection.
624 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL)
625 ipno = vp->vp_ipaddr;
626 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL)
627 call_num = vp->vp_strvalue;
630 * lock the file while reading/writing.
632 rad_lockfd(fd, LOCK_LEN);
635 * FIXME: If we get a 'Start' for a user/nas/port which is
636 * listed, but for which we did NOT get a 'Stop', then
637 * it's not a duplicate session. This happens with
638 * static IP's like DSL.
640 request->simul_count = 0;
641 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
642 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
643 (!inst->case_sensitive &&
644 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
645 (u.type == P_LOGIN)) {
646 char session_id[sizeof(u.session_id) + 1];
647 char utmp_login[sizeof(u.login) + 1];
649 strlcpy(session_id, u.session_id, sizeof(session_id));
652 * The login name MAY fill the whole field,
653 * and thus won't be zero-filled.
655 * Note that we take the user name from
656 * the utmp file, as that's the canonical
657 * form. The 'login' variable may contain
658 * a string which is an upper/lowercase
659 * version of u.login. When we call the
660 * routine to check the terminal server,
661 * the NAS may be case sensitive.
663 * e.g. We ask if "bob" is using a port,
664 * and the NAS says "no", because "BOB"
667 strlcpy(utmp_login, u.login, sizeof(u.login));
670 * rad_check_ts may take seconds
671 * to return, and we don't want
672 * to block everyone else while
673 * that's happening. */
674 rad_unlockfd(fd, LOCK_LEN);
675 rcode = rad_check_ts(u.nas_address, u.nas_port,
676 utmp_login, session_id);
677 rad_lockfd(fd, LOCK_LEN);
681 * Stale record - zap it.
683 session_zap(request, u.nas_address,
684 u.nas_port, login, session_id,
685 u.framed_address, u.proto,0);
687 else if (rcode == 1) {
689 * User is still logged in.
691 ++request->simul_count;
694 * Does it look like a MPP attempt?
696 if (strchr("SCPA", u.proto) &&
697 ipno && u.framed_address == ipno)
698 request->simul_mpp = 2;
699 else if (strchr("SCPA", u.proto) && call_num &&
700 !strncmp(u.caller_id,call_num,16))
701 request->simul_mpp = 2;
705 * Failed to check the terminal
706 * server for duplicate logins:
710 radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
711 return RLM_MODULE_FAIL;
715 close(fd); /* and implicitely release the locks */
717 return RLM_MODULE_OK;
721 /* globally exported name */
722 module_t rlm_radutmp = {
725 RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
726 radutmp_instantiate, /* instantiation */
729 NULL, /* authentication */
730 NULL, /* authorization */
731 NULL, /* preaccounting */
732 #ifdef WITH_ACCOUNTING
733 radutmp_accounting, /* accounting */
737 #ifdef WITH_SESSION_MGMT
738 radutmp_checksimul, /* checksimul */
742 NULL, /* pre-proxy */
743 NULL, /* post-proxy */