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));
91 memset(inst, 0, sizeof(*inst));
93 if (cf_section_parse(conf, inst, module_config)) {
98 inst->nas_port_list = NULL;
107 static int radutmp_detach(void *instance)
110 rlm_radutmp_t *inst = instance;
112 for (p = inst->nas_port_list ; p ; p=next) {
116 if (inst->filename) free(inst->filename);
117 if (inst->username) free(inst->username);
123 * Zap all users on a NAS from the radutmp file.
125 static int radutmp_zap(rlm_radutmp_t *inst,
126 const char *filename,
133 if (t == 0) time(&t);
135 fd = open(filename, O_RDWR);
137 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
138 filename, strerror(errno));
139 return RLM_MODULE_FAIL;
143 * Lock the utmp file, prefer lockf() over flock().
145 rad_lockfd(fd, LOCK_LEN);
148 * Find the entry for this NAS / portno combination.
150 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
151 if ((nasaddr != 0 && nasaddr != u.nas_address) ||
157 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
158 radlog(L_ERR, "rlm_radutmp: radutmp_zap: negative lseek!");
159 lseek(fd, (off_t)0, SEEK_SET);
163 write(fd, &u, sizeof(u));
165 close(fd); /* and implicitely release the locks */
171 * Lookup a NAS_PORT in the nas_port_list
173 static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
177 for(cl = nas_port_list; cl; cl = cl->next)
178 if (nasaddr == cl->nasaddr &&
186 * Store logins in the RADIUS utmp file.
188 static int radutmp_accounting(void *instance, REQUEST *request)
190 struct radutmp ut, u;
193 uint32_t nas_address = 0;
194 uint32_t framed_address = 0;
198 int just_an_update = 0;
200 int nas_port_type = 0;
202 rlm_radutmp_t *inst = instance;
205 char ip_name[32]; /* 255.255.255.255 */
211 * Which type is this.
213 if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
214 radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
215 return RLM_MODULE_NOOP;
220 * Look for weird reboot packets.
222 * ComOS (up to and including 3.5.1b20) does not send
223 * standard PW_STATUS_ACCOUNTING_XXX messages.
225 * Check for: o no Acct-Session-Time, or time of 0
226 * o Acct-Session-Id of "00000000".
228 * We could also check for NAS-Port, that attribute
229 * should NOT be present (but we don't right now).
231 if ((status != PW_STATUS_ACCOUNTING_ON) &&
232 (status != PW_STATUS_ACCOUNTING_OFF)) do {
236 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
237 == NULL || vp->lvalue == 0)
239 if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
240 != NULL && vp->length == 8 &&
241 memcmp(vp->strvalue, "00000000", 8) == 0)
243 if (check1 == 0 || check2 == 0) {
244 #if 0 /* Cisco sometimes sends START records without username. */
245 radlog(L_ERR, "rlm_radutmp: no username in record");
246 return RLM_MODULE_FAIL;
251 radlog(L_INFO, "rlm_radutmp: converting reboot records.");
252 if (status == PW_STATUS_STOP)
253 status = PW_STATUS_ACCOUNTING_OFF;
254 if (status == PW_STATUS_START)
255 status = PW_STATUS_ACCOUNTING_ON;
259 memset(&ut, 0, sizeof(ut));
263 * First, find the interesting attributes.
265 for (vp = request->packet->vps; vp; vp = vp->next) {
266 switch (vp->attribute) {
267 case PW_LOGIN_IP_HOST:
268 case PW_FRAMED_IP_ADDRESS:
269 framed_address = vp->lvalue;
270 ut.framed_address = vp->lvalue;
272 case PW_FRAMED_PROTOCOL:
273 protocol = vp->lvalue;
275 case PW_NAS_IP_ADDRESS:
276 nas_address = vp->lvalue;
277 ut.nas_address = vp->lvalue;
280 ut.nas_port = vp->lvalue;
283 case PW_ACCT_DELAY_TIME:
284 ut.delay = vp->lvalue;
286 case PW_ACCT_SESSION_ID:
288 * If length > 8, only store the
291 off = vp->length - sizeof(ut.session_id);
293 * Ascend is br0ken - it adds a \0
294 * to the end of any string.
297 if (vp->length > 0 &&
298 vp->strvalue[vp->length - 1] == 0)
300 if (off < 0) off = 0;
301 memcpy(ut.session_id, vp->strvalue + off,
302 sizeof(ut.session_id));
304 case PW_NAS_PORT_TYPE:
305 if (vp->lvalue >= 0 && vp->lvalue <= 4)
306 ut.porttype = porttypes[vp->lvalue];
307 nas_port_type = vp->lvalue;
309 case PW_CALLING_STATION_ID:
310 if(inst->callerid_ok)
311 strNcpy(ut.caller_id,
312 (char *)vp->strvalue,
313 sizeof(ut.caller_id));
319 * If we didn't find out the NAS address, use the
320 * originator's IP address.
322 if (nas_address == 0) {
323 nas_address = request->packet->src_ipaddr;
324 ut.nas_address = nas_address;
325 nas = client_name(nas_address); /* MUST be a valid client */
327 } else { /* might be a client, might not be. */
331 * Hack like 'client_name()', but with sane
334 cl = client_find(nas_address);
336 if (cl->shortname[0]) {
343 * The NAS isn't a client, it's behind
344 * a proxy server. In that case, just
345 * get the IP address.
347 nas = ip_ntoa(ip_name, nas_address);
352 * Set the protocol field.
354 if (protocol == PW_PPP)
356 else if (protocol == PW_SLIP)
360 ut.time = t - ut.delay;
363 * Get the utmp filename, via xlat.
365 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
368 * See if this was a reboot.
370 * Hmm... we may not want to zap all of the users when
371 * the NAS comes up, because of issues with receiving
372 * UDP packets out of order.
374 if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
375 radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
377 radutmp_zap(inst, filename, nas_address, ut.time);
378 return RLM_MODULE_OK;
381 if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
382 radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
384 radutmp_zap(inst, filename, nas_address, ut.time);
385 return RLM_MODULE_OK;
389 * If we don't know this type of entry pretend we succeeded.
391 if (status != PW_STATUS_START &&
392 status != PW_STATUS_STOP &&
393 status != PW_STATUS_ALIVE) {
394 radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d)",
395 nas, ut.nas_port, status);
396 return RLM_MODULE_NOOP;
400 * Translate the User-Name attribute, or whatever else
401 * they told us to use.
404 radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
407 * Copy the previous translated user name.
409 strncpy(ut.login, buffer, RUT_NAMESIZE);
412 * Perhaps we don't want to store this record into
413 * radutmp. We skip records:
415 * - without a NAS-Port (telnet / tcp access)
416 * - with the username "!root" (console admin login)
419 DEBUG2(" rlm_radutmp: No NAS-Port seen. Cannot do anything.");
420 DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!");
421 return RLM_MODULE_NOOP;
424 if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
425 DEBUG2(" rlm_radutmp: Not recording administrative user");
427 return RLM_MODULE_NOOP;
431 * Enter into the radutmp file.
433 fd = open(filename, O_RDWR|O_CREAT, inst->permission);
435 radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
436 filename, strerror(errno));
437 return RLM_MODULE_FAIL;
441 * Lock the utmp file, prefer lockf() over flock().
443 rad_lockfd(fd, LOCK_LEN);
446 * Find the entry for this NAS / portno combination.
448 if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
449 ut.nas_port)) != NULL) {
450 lseek(fd, (off_t)cache->offset, SEEK_SET);
455 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
457 if (u.nas_address != ut.nas_address ||
458 u.nas_port != ut.nas_port)
461 if (status == PW_STATUS_STOP &&
462 strncmp(ut.session_id, u.session_id,
463 sizeof(u.session_id)) != 0) {
465 * Don't complain if this is not a
466 * login record (some clients can
467 * send _only_ logout records).
469 if (u.type == P_LOGIN)
470 radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has wrong ID",
476 if (status == PW_STATUS_START &&
477 strncmp(ut.session_id, u.session_id,
478 sizeof(u.session_id)) == 0 &&
480 if (u.type == P_LOGIN) {
481 radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
486 radlog(L_ERR, "rlm_radutmp: Login entry for NAS %s port %u wrong order",
493 * FIXME: the ALIVE record could need
494 * some more checking, but anyway I'd
495 * rather rewrite this mess -- miquels.
497 if (status == PW_STATUS_ALIVE &&
498 strncmp(ut.session_id, u.session_id,
499 sizeof(u.session_id)) == 0 &&
502 * Keep the original login time.
509 if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
510 radlog(L_ERR, "rlm_radutmp: negative lseek!");
511 lseek(fd, (off_t)0, SEEK_SET);
517 } /* read the file until we find a match */
520 * Found the entry, do start/update it with
521 * the information from the packet.
523 if (r >= 0 && (status == PW_STATUS_START ||
524 status == PW_STATUS_ALIVE)) {
526 * Remember where the entry was, because it's
527 * easier than searching through the entire file.
530 cache = rad_malloc(sizeof(NAS_PORT));
531 cache->nasaddr = ut.nas_address;
532 cache->port = ut.nas_port;
534 cache->next = inst->nas_port_list;
535 inst->nas_port_list = cache;
539 write(fd, &ut, sizeof(u));
543 * The user has logged off, delete the entry by
544 * re-writing it in place.
546 if (status == PW_STATUS_STOP) {
551 write(fd, &u, sizeof(u));
553 radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
558 close(fd); /* and implicitely release the locks */
560 return RLM_MODULE_OK;
564 * See if a user is already logged in. Sets request->simul_count to the
565 * current session count for this user and sets request->simul_mpp to 2
566 * if it looks like a multilink attempt based on the requested IP
567 * address, otherwise leaves request->simul_mpp alone.
569 * Check twice. If on the first pass the user exceeds his
570 * max. number of logins, do a second pass and validate all
571 * logins by querying the terminal server (using eg. SNMP).
573 static int radutmp_checksimul(void *instance, REQUEST *request)
579 char *call_num = NULL;
581 rlm_radutmp_t *inst = instance;
586 * Get the filename, via xlat.
588 radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
590 if ((fd = open(filename, O_RDWR)) < 0) {
592 * If the file doesn't exist, then no users
595 if (errno == ENOENT) {
596 request->simul_count=0;
597 return RLM_MODULE_OK;
601 * Error accessing the file.
603 radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
604 filename, strerror(errno));
605 return RLM_MODULE_FAIL;
609 radius_xlat(login, sizeof(login), inst->username, request, NULL);
611 return RLM_MODULE_NOOP;
615 * WTF? This is probably wrong... we probably want to
616 * be able to check users across multiple session accounting
619 request->simul_count = 0;
622 * Loop over utmp, counting how many people MAY be logged in.
624 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
625 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
626 (!inst->case_sensitive &&
627 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
628 (u.type == P_LOGIN)) {
629 ++request->simul_count;
634 * The number of users logged in is OK,
635 * OR, we've been told to not check the NAS.
637 if ((request->simul_count < request->simul_max) ||
640 return RLM_MODULE_OK;
642 lseek(fd, (off_t)0, SEEK_SET);
645 * Setup some stuff, like for MPP detection.
647 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
649 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
650 call_num = vp->strvalue;
653 * lock the file while reading/writing.
655 rad_lockfd(fd, LOCK_LEN);
658 * FIXME: If we get a 'Start' for a user/nas/port which is
659 * listed, but for which we did NOT get a 'Stop', then
660 * it's not a duplicate session. This happens with
661 * static IP's like DSL.
663 request->simul_count = 0;
664 while (read(fd, &u, sizeof(u)) == sizeof(u)) {
665 if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
666 (!inst->case_sensitive &&
667 (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
668 (u.type == P_LOGIN)) {
669 char session_id[sizeof(u.session_id) + 1];
670 char utmp_login[sizeof(u.login) + 1];
672 strNcpy(session_id, u.session_id, sizeof(session_id));
675 * The login name MAY fill the whole field,
676 * and thus won't be zero-filled.
678 * Note that we take the user name from
679 * the utmp file, as that's the canonical
680 * form. The 'login' variable may contain
681 * a string which is an upper/lowercase
682 * version of u.login. When we call the
683 * routine to check the terminal server,
684 * the NAS may be case sensitive.
686 * e.g. We ask if "bob" is using a port,
687 * and the NAS says "no", because "BOB"
690 strNcpy(utmp_login, u.login, sizeof(u.login));
693 * rad_check_ts may take seconds
694 * to return, and we don't want
695 * to block everyone else while
696 * that's happening. */
697 rad_unlockfd(fd, LOCK_LEN);
698 rcode = rad_check_ts(u.nas_address, u.nas_port,
699 utmp_login, session_id);
700 rad_lockfd(fd, LOCK_LEN);
703 * Failed to check the terminal server for
704 * duplicate logins: Return an error.
708 return RLM_MODULE_FAIL;
712 ++request->simul_count;
715 * Does it look like a MPP attempt?
717 if (strchr("SCPA", u.proto) &&
718 ipno && u.framed_address == ipno)
719 request->simul_mpp = 2;
720 else if (strchr("SCPA", u.proto) && call_num &&
721 !strncmp(u.caller_id,call_num,16))
722 request->simul_mpp = 2;
726 * False record - zap it.
729 u.nas_address, u.nas_port, login,
730 session_id, u.framed_address,
735 close(fd); /* and implicitely release the locks */
737 return RLM_MODULE_OK;
740 /* globally exported name */
741 module_t rlm_radutmp = {
743 0, /* type: reserved */
744 NULL, /* initialization */
745 radutmp_instantiate, /* instantiation */
747 NULL, /* authentication */
748 NULL, /* authorization */
749 NULL, /* preaccounting */
750 radutmp_accounting, /* accounting */
751 radutmp_checksimul, /* checksimul */
752 NULL, /* pre-proxy */
753 NULL, /* post-proxy */
756 radutmp_detach, /* detach */