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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2000 The FreeRADIUS server project
21 * FIXME add copyrights
26 #include <sys/types.h>
42 #define LOCK_LEN sizeof(struct radutmp)
44 static const char porttypes[] = "ASITX";
47 * used for caching radutmp lookups in the accounting component. The
48 * session (checksimul) component doesn't use it, but probably should.
50 typedef struct nas_port {
54 struct nas_port *next;
57 typedef struct rlm_radutmp_t {
58 NAS_PORT *nas_port_list;
67 static CONF_PARSER module_config[] = {
68 { "filename", PW_TYPE_STRING_PTR,
69 offsetof(rlm_radutmp_t,filename), NULL, RADUTMP },
70 { "username", PW_TYPE_STRING_PTR,
71 offsetof(rlm_radutmp_t,username), NULL, "%{User-Name}"},
72 { "case_sensitive", PW_TYPE_BOOLEAN,
73 offsetof(rlm_radutmp_t,case_sensitive), NULL, "yes"},
74 { "check_with_nas", PW_TYPE_BOOLEAN,
75 offsetof(rlm_radutmp_t,check_nas), NULL, "yes"},
76 { "perm", PW_TYPE_INTEGER,
77 offsetof(rlm_radutmp_t,permission), NULL, "0644" },
78 { "callerid", PW_TYPE_BOOLEAN,
79 offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
80 { NULL, -1, 0, NULL, NULL } /* end the list */
83 static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
87 inst = rad_malloc(sizeof(*inst));
89 if (cf_section_parse(conf, inst, module_config)) {
94 inst->nas_port_list = NULL;
103 static int radutmp_detach(void *instance)
106 rlm_radutmp_t *inst = instance;
108 for (p = inst->nas_port_list ; p ; p=next) {
112 free(inst->filename);
118 * Zap all users on a NAS from the radutmp file.
120 static int radutmp_zap(rlm_radutmp_t *inst,
121 const char *filename,
128 if (t == 0) time(&t);
130 fd = open(filename, O_RDWR);
132 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
133 filename, strerror(errno));
134 return RLM_MODULE_FAIL;
138 * Lock the utmp file, prefer lockf() over flock().
140 rad_lockfd(fd, LOCK_LEN);
143 * Find the entry for this NAS / portno combination.
145 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
146 if ((nasaddr != 0 && nasaddr != u.nas_address) ||
152 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
153 radlog(L_ERR, "rlm_radutmp: radutmp_zap: negative lseek!");
154 lseek(fd, (off_t)0, SEEK_SET);
158 write(fd, &u, sizeof(u));
160 close(fd); /* and implicitely release the locks */
166 * Lookup a NAS_PORT in the nas_port_list
168 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
172 for(cl = nas_port_list; cl; cl = cl->next)
173 if (nasaddr == cl->nasaddr &&
181 * Store logins in the RADIUS utmp file.
183 static int radutmp_accounting(void *instance, REQUEST *request)
185 struct radutmp ut, u;
188 uint32_t nas_address = 0;
189 uint32_t framed_address = 0;
193 int just_an_update = 0;
195 int nas_port_type = 0;
197 rlm_radutmp_t *inst = instance;
200 char ip_name[32]; /* 255.255.255.255 */
206 * Which type is this.
208 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
209 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
210 return RLM_MODULE_NOOP;
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))
232 == NULL || vp->lvalue == 0)
234 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
235 != NULL && vp->length == 8 &&
236 memcmp(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));
258 * First, find the interesting attributes.
260 for (vp = request->packet->vps; vp; vp = vp->next) {
261 switch (vp->attribute) {
262 case PW_LOGIN_IP_HOST:
263 case PW_FRAMED_IP_ADDRESS:
264 framed_address = vp->lvalue;
265 ut.framed_address = vp->lvalue;
267 case PW_FRAMED_PROTOCOL:
268 protocol = vp->lvalue;
270 case PW_NAS_IP_ADDRESS:
271 nas_address = vp->lvalue;
272 ut.nas_address = vp->lvalue;
275 ut.nas_port = vp->lvalue;
278 case PW_ACCT_DELAY_TIME:
279 ut.delay = vp->lvalue;
281 case PW_ACCT_SESSION_ID:
283 * If length > 8, only store the
286 off = vp->length - sizeof(ut.session_id);
288 * Ascend is br0ken - it adds a \0
289 * to the end of any string.
292 if (vp->length > 0 &&
293 vp->strvalue[vp->length - 1] == 0)
295 if (off < 0) off = 0;
296 memcpy(ut.session_id, vp->strvalue + off,
297 sizeof(ut.session_id));
299 case PW_NAS_PORT_TYPE:
300 if (vp->lvalue >= 0 && vp->lvalue <= 4)
301 ut.porttype = porttypes[vp->lvalue];
302 nas_port_type = vp->lvalue;
304 case PW_CALLING_STATION_ID:
305 if(inst->callerid_ok)
306 strNcpy(ut.caller_id,
307 (char *)vp->strvalue,
308 sizeof(ut.caller_id));
314 * If we didn't find out the NAS address, use the
315 * originator's IP address.
317 if (nas_address == 0) {
318 nas_address = request->packet->src_ipaddr;
319 ut.nas_address = nas_address;
320 nas = client_name(nas_address); /* MUST be a valid client */
322 } else { /* might be a client, might not be. */
326 * Hack like 'client_name()', but with sane
329 cl = client_find(nas_address);
331 if (cl->shortname[0]) {
338 * The NAS isn't a client, it's behind
339 * a proxy server. In that case, just
340 * get the IP address.
342 nas = ip_ntoa(ip_name, nas_address);
347 * Set the protocol field.
349 if (protocol == PW_PPP)
351 else if (protocol == PW_SLIP)
355 ut.time = t - ut.delay;
358 * Get the utmp filename, via xlat.
360 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
363 * See if this was a reboot.
365 * Hmm... we may not want to zap all of the users when
366 * the NAS comes up, because of issues with receiving
367 * UDP packets out of order.
369 if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
370 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
372 radutmp_zap(inst, filename, nas_address, ut.time);
373 return RLM_MODULE_OK;
376 if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
377 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
379 radutmp_zap(inst, filename, nas_address, ut.time);
380 return RLM_MODULE_OK;
384 * If we don't know this type of entry pretend we succeeded.
386 if (status != PW_STATUS_START &&
387 status != PW_STATUS_STOP &&
388 status != PW_STATUS_ALIVE) {
389 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d)",
390 nas, ut.nas_port, status);
391 return RLM_MODULE_NOOP;
395 * Translate the User-Name attribute, or whatever else
396 * they told us to use.
399 radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
402 * Copy the previous translated user name.
404 strncpy(ut.login, buffer, RUT_NAMESIZE);
407 * Perhaps we don't want to store this record into
408 * radutmp. We skip records:
410 * - without a NAS-Port (telnet / tcp access)
411 * - with the username "!root" (console admin login)
414 DEBUG2(" rlm_radutmp: No NAS-Port seen. Cannot do anything.");
415 DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!");
416 return RLM_MODULE_NOOP;
419 if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
420 DEBUG2(" rlm_radutmp: Not recording administrative user");
422 return RLM_MODULE_NOOP;
426 * Enter into the radutmp file.
428 fd = open(filename, O_RDWR|O_CREAT, inst->permission);
430 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
431 filename, strerror(errno));
432 return RLM_MODULE_FAIL;
436 * Lock the utmp file, prefer lockf() over flock().
438 rad_lockfd(fd, LOCK_LEN);
441 * Find the entry for this NAS / portno combination.
443 if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
444 ut.nas_port)) != NULL) {
445 lseek(fd, (off_t)cache->offset, SEEK_SET);
450 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
452 if (u.nas_address != ut.nas_address ||
453 u.nas_port != ut.nas_port)
456 if (status == PW_STATUS_STOP &&
457 strncmp(ut.session_id, u.session_id,
458 sizeof(u.session_id)) != 0) {
460 * Don't complain if this is not a
461 * login record (some clients can
462 * send _only_ logout records).
464 if (u.type == P_LOGIN)
465 radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has wrong ID",
471 if (status == PW_STATUS_START &&
472 strncmp(ut.session_id, u.session_id,
473 sizeof(u.session_id)) == 0 &&
475 if (u.type == P_LOGIN) {
476 radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
481 radlog(L_ERR, "rlm_radutmp: Login entry for NAS %s port %u wrong order",
488 * FIXME: the ALIVE record could need
489 * some more checking, but anyway I'd
490 * rather rewrite this mess -- miquels.
492 if (status == PW_STATUS_ALIVE &&
493 strncmp(ut.session_id, u.session_id,
494 sizeof(u.session_id)) == 0 &&
497 * Keep the original login time.
504 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
505 radlog(L_ERR, "rlm_radutmp: negative lseek!");
506 lseek(fd, (off_t)0, SEEK_SET);
512 } /* read the file until we find a match */
515 * Found the entry, do start/update it with
516 * the information from the packet.
518 if (r >= 0 && (status == PW_STATUS_START ||
519 status == PW_STATUS_ALIVE)) {
521 * Remember where the entry was, because it's
522 * easier than searching through the entire file.
525 cache = rad_malloc(sizeof(NAS_PORT));
526 cache->nasaddr = ut.nas_address;
527 cache->port = ut.nas_port;
529 cache->next = inst->nas_port_list;
530 inst->nas_port_list = cache;
534 write(fd, &ut, sizeof(u));
538 * The user has logged off, delete the entry by
539 * re-writing it in place.
541 if (status == PW_STATUS_STOP) {
546 write(fd, &u, sizeof(u));
548 radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
553 close(fd); /* and implicitely release the locks */
555 return RLM_MODULE_OK;
559 * See if a user is already logged in. Sets request->simul_count to the
560 * current session count for this user and sets request->simul_mpp to 2
561 * if it looks like a multilink attempt based on the requested IP
562 * address, otherwise leaves request->simul_mpp alone.
564 * Check twice. If on the first pass the user exceeds his
565 * max. number of logins, do a second pass and validate all
566 * logins by querying the terminal server (using eg. SNMP).
568 static int radutmp_checksimul(void *instance, REQUEST *request)
574 char *call_num = NULL;
576 rlm_radutmp_t *inst = instance;
581 * Get the filename, via xlat.
583 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
585 if ((fd = open(filename, O_RDWR)) < 0) {
587 * If the file doesn't exist, then no users
590 if (errno == ENOENT) {
591 request->simul_count=0;
592 return RLM_MODULE_OK;
596 * Error accessing the file.
598 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
599 filename, strerror(errno));
600 return RLM_MODULE_FAIL;
604 radius_xlat(login, sizeof(login), inst->username, request, NULL);
606 return RLM_MODULE_NOOP;
610 * WTF? This is probably wrong... we probably want to
611 * be able to check users across multiple session accounting
614 request->simul_count = 0;
617 * Loop over utmp, counting how many people MAY be logged in.
619 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
620 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
621 (!inst->case_sensitive &&
622 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
623 (u.type == P_LOGIN)) {
624 ++request->simul_count;
629 * The number of users logged in is OK,
630 * OR, we've been told to not check the NAS.
632 if ((request->simul_count < request->simul_max) ||
635 return RLM_MODULE_OK;
637 lseek(fd, (off_t)0, SEEK_SET);
640 * Setup some stuff, like for MPP detection.
642 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
644 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
645 call_num = vp->strvalue;
648 * lock the file while reading/writing.
650 rad_lockfd(fd, LOCK_LEN);
653 * FIXME: If we get a 'Start' for a user/nas/port which is
654 * listed, but for which we did NOT get a 'Stop', then
655 * it's not a duplicate session. This happens with
656 * static IP's like DSL.
658 request->simul_count = 0;
659 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
660 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
661 (!inst->case_sensitive &&
662 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
663 (u.type == P_LOGIN)) {
664 char session_id[sizeof(u.session_id) + 1];
665 char utmp_login[sizeof(u.login) + 1];
667 strNcpy(session_id, u.session_id, sizeof(session_id));
670 * The login name MAY fill the whole field,
671 * and thus won't be zero-filled.
673 * Note that we take the user name from
674 * the utmp file, as that's the canonical
675 * form. The 'login' variable may contain
676 * a string which is an upper/lowercase
677 * version of u.login. When we call the
678 * routine to check the terminal server,
679 * the NAS may be case sensitive.
681 * e.g. We ask if "bob" is using a port,
682 * and the NAS says "no", because "BOB"
685 strNcpy(utmp_login, u.login, sizeof(u.login));
688 * rad_check_ts may take seconds
689 * to return, and we don't want
690 * to block everyone else while
691 * that's happening. */
692 rad_unlockfd(fd, LOCK_LEN);
693 rcode = rad_check_ts(u.nas_address, u.nas_port,
694 utmp_login, session_id);
695 rad_lockfd(fd, LOCK_LEN);
698 * Failed to check the terminal server for
699 * duplicate logins: Return an error.
703 return RLM_MODULE_FAIL;
707 ++request->simul_count;
710 * Does it look like a MPP attempt?
712 if (strchr("SCPA", u.proto) &&
713 ipno && u.framed_address == ipno)
714 request->simul_mpp = 2;
715 else if (strchr("SCPA", u.proto) && call_num &&
716 !strncmp(u.caller_id,call_num,16))
717 request->simul_mpp = 2;
721 * False record - zap it.
723 session_zap(request->packet->sockfd,
724 u.nas_address, u.nas_port, login,
725 session_id, u.framed_address,
730 close(fd); /* and implicitely release the locks */
732 return RLM_MODULE_OK;
735 /* globally exported name */
736 module_t rlm_radutmp = {
738 0, /* type: reserved */
739 NULL, /* initialization */
740 radutmp_instantiate, /* instantiation */
742 NULL, /* authentication */
743 NULL, /* authorization */
744 NULL, /* preaccounting */
745 radutmp_accounting, /* accounting */
746 radutmp_checksimul, /* checksimul */
747 NULL, /* pre-proxy */
748 NULL, /* post-proxy */
751 radutmp_detach, /* detach */