/*
- * rlm_radutmp.c
+ * rlm_radutmp.c
*
* Version: $Id$
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000,2006 The FreeRADIUS server project
* FIXME add copyrights
*/
-#include "autoconf.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/radutmp.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
#include <fcntl.h>
-#include <time.h>
-#include <errno.h>
#include <limits.h>
#include "config.h"
-#include "radiusd.h"
-#include "radutmp.h"
-#include "modules.h"
-
#define LOCK_LEN sizeof(struct radutmp)
static const char porttypes[] = "ASITX";
/*
- * Internal wrapper for locking, to minimize the number of ifdef's
- * in the source.
- *
- * Lock the utmp file, prefer lockf() over flock()
- */
-static void radutmp_lock(int fd)
-{
-#if defined(F_LOCK) && !defined(BSD)
- (void)lockf(fd, F_LOCK, LOCK_LEN);
-#else
- (void)flock(fd, LOCK_EX);
-#endif
-}
-
-/*
- * Internal wrapper for unlocking, to minimize the number of ifdef's
- * in the source.
- *
- * Unlock the utmp file, prefer lockf() over flock()
- */
-static void radutmp_unlock(int fd)
-{
-#if defined(F_LOCK) && !defined(BSD)
- (void)lockf(fd, F_ULOCK, LOCK_LEN);
-#else
- (void)flock(fd, LOCK_UN);
-#endif
-}
-
-/*
* used for caching radutmp lookups in the accounting component. The
* session (checksimul) component doesn't use it, but probably should.
*/
typedef struct nas_port {
uint32_t nasaddr;
- int port;
+ unsigned int port;
off_t offset;
struct nas_port *next;
} NAS_PORT;
-struct radutmp_instance {
- NAS_PORT *nas_port_list;
- char *radutmp_fn;
- char *username;
- int permission;
- int callerid_ok;
-};
-
-static CONF_PARSER module_config[] = {
- { "filename", PW_TYPE_STRING_PTR,
- offsetof(struct radutmp_instance,radutmp_fn), NULL, RADUTMP },
- { "username", PW_TYPE_STRING_PTR,
- offsetof(struct radutmp_instance,username), NULL, "%{User-Name}"},
- { "perm", PW_TYPE_INTEGER,
- offsetof(struct radutmp_instance,permission), NULL, "0644" },
- { "callerid", PW_TYPE_BOOLEAN,
- offsetof(struct radutmp_instance,callerid_ok), NULL, "no" },
- { NULL, -1, 0, NULL, NULL } /* end the list */
+typedef struct rlm_radutmp_t {
+ NAS_PORT *nas_port_list;
+ char *filename;
+ char *username;
+ int case_sensitive;
+ int check_nas;
+ int permission;
+ int callerid_ok;
+} rlm_radutmp_t;
+
+static const CONF_PARSER module_config[] = {
+ { "filename", PW_TYPE_STRING_PTR,
+ offsetof(rlm_radutmp_t,filename), NULL, RADUTMP },
+ { "username", PW_TYPE_STRING_PTR,
+ offsetof(rlm_radutmp_t,username), NULL, "%{User-Name}"},
+ { "case_sensitive", PW_TYPE_BOOLEAN,
+ offsetof(rlm_radutmp_t,case_sensitive), NULL, "yes"},
+ { "check_with_nas", PW_TYPE_BOOLEAN,
+ offsetof(rlm_radutmp_t,check_nas), NULL, "yes"},
+ { "perm", PW_TYPE_INTEGER,
+ offsetof(rlm_radutmp_t,permission), NULL, "0644" },
+ { "callerid", PW_TYPE_BOOLEAN,
+ offsetof(rlm_radutmp_t,callerid_ok), NULL, "no" },
+ { NULL, -1, 0, NULL, NULL } /* end the list */
};
static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
{
- struct radutmp_instance *inst;
+ rlm_radutmp_t *inst;
+
inst = rad_malloc(sizeof(*inst));
+ if (!inst) {
+ return -1;
+ }
+ memset(inst, 0, sizeof(*inst));
+
if (cf_section_parse(conf, inst, module_config)) {
free(inst);
return -1;
}
+
inst->nas_port_list = NULL;
*instance = inst;
return 0;
static int radutmp_detach(void *instance)
{
NAS_PORT *p, *next;
- struct radutmp_instance *inst = instance;
+ rlm_radutmp_t *inst = instance;
- for(p=inst->nas_port_list ; p ; p=next) {
- next=p->next;
+ for (p = inst->nas_port_list ; p ; p=next) {
+ next = p->next;
free(p);
}
- free(inst->radutmp_fn);
free(inst);
return 0;
}
/*
* Zap all users on a NAS from the radutmp file.
*/
-static int radutmp_zap(struct radutmp_instance *inst, uint32_t nasaddr, time_t t)
+static int radutmp_zap(UNUSED rlm_radutmp_t *inst,
+ const char *filename,
+ uint32_t nasaddr,
+ time_t t)
{
struct radutmp u;
int fd;
if (t == 0) time(&t);
- fd = open(inst->radutmp_fn, O_RDWR);
- if (fd >= 0) {
- /*
- * Lock the utmp file, prefer lockf() over flock().
- */
- radutmp_lock(fd);
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
+ filename, strerror(errno));
+ return RLM_MODULE_FAIL;
+ }
- /*
- * Find the entry for this NAS / portno combination.
- */
- while (read(fd, &u, sizeof(u)) == sizeof(u)) {
- if ((nasaddr != 0 && nasaddr != u.nas_address) ||
- u.type != P_LOGIN)
- continue;
- /*
- * Match. Zap it.
- */
- if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
- radlog(L_ERR, "Accounting: radutmp_zap: "
- "negative lseek!\n");
- lseek(fd, (off_t)0, SEEK_SET);
- }
- u.type = P_IDLE;
- u.time = t;
- write(fd, &u, sizeof(u));
- }
- close(fd);
- } else {
- radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
+ /*
+ * Lock the utmp file, prefer lockf() over flock().
+ */
+ rad_lockfd(fd, LOCK_LEN);
+
+ /*
+ * Find the entry for this NAS / portno combination.
+ */
+ while (read(fd, &u, sizeof(u)) == sizeof(u)) {
+ if ((nasaddr != 0 && nasaddr != u.nas_address) ||
+ u.type != P_LOGIN)
+ continue;
+ /*
+ * Match. Zap it.
+ */
+ if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
+ radlog(L_ERR, "rlm_radutmp: radutmp_zap: negative lseek!");
+ lseek(fd, (off_t)0, SEEK_SET);
+ }
+ u.type = P_IDLE;
+ u.time = t;
+ write(fd, &u, sizeof(u));
}
+ close(fd); /* and implicitely release the locks */
return 0;
}
/*
* Lookup a NAS_PORT in the nas_port_list
*/
-static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, int port)
+static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
{
NAS_PORT *cl;
}
+#ifdef WITH_ACCOUNTING
/*
* Store logins in the RADIUS utmp file.
*/
{
struct radutmp ut, u;
VALUE_PAIR *vp;
- int rb_record = 0;
int status = -1;
- uint32_t nas_address = 0;
- uint32_t framed_address = 0;
int protocol = -1;
time_t t;
int fd;
- int just_an_update = 0;
int port_seen = 0;
- int nas_port_type = 0;
int off;
- struct radutmp_instance *inst = instance;
+ rlm_radutmp_t *inst = instance;
char buffer[256];
+ char filename[1024];
+ char ip_name[32]; /* 255.255.255.255 */
+ const char *nas;
+ NAS_PORT *cache;
+ int r;
+
+ if (request->packet->src_ipaddr.af != AF_INET) {
+ DEBUG("rlm_radutmp: IPv6 not supported!");
+ return RLM_MODULE_NOOP;
+ }
/*
* Which type is this.
*/
- if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
- radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
+ if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0)) == NULL) {
+ radlog(L_ERR, "rlm_radutmp: No Accounting-Status-Type record.");
return RLM_MODULE_NOOP;
}
- status = vp->lvalue;
- if (status == PW_STATUS_ACCOUNTING_ON ||
- status == PW_STATUS_ACCOUNTING_OFF) rb_record = 1;
+ status = vp->vp_integer;
/*
- * Translate the User-Name attribute, or whatever else
- * they told us to use.
+ * Look for weird reboot packets.
+ *
+ * ComOS (up to and including 3.5.1b20) does not send
+ * standard PW_STATUS_ACCOUNTING_XXX messages.
+ *
+ * Check for: o no Acct-Session-Time, or time of 0
+ * o Acct-Session-Id of "00000000".
+ *
+ * We could also check for NAS-Port, that attribute
+ * should NOT be present (but we don't right now).
*/
- *buffer = '\0';
- radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
-
- if (!rb_record &&
- (*buffer != '\0')) do {
+ if ((status != PW_STATUS_ACCOUNTING_ON) &&
+ (status != PW_STATUS_ACCOUNTING_OFF)) do {
int check1 = 0;
int check2 = 0;
- /*
- * ComOS (up to and including 3.5.1b20) does not send
- * standard PW_STATUS_ACCOUNTING_XXX messages.
- *
- * Check for: o no Acct-Session-Time, or time of 0
- * o Acct-Session-Id of "00000000".
- *
- * We could also check for NAS-Port, that attribute
- * should NOT be present (but we don't right now).
- */
- if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME))
- == NULL || vp->lvalue == 0)
+ if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0))
+ == NULL || vp->vp_date == 0)
check1 = 1;
- if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
+ if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID, 0))
!= NULL && vp->length == 8 &&
- memcmp(vp->strvalue, "00000000", 8) == 0)
+ memcmp(vp->vp_strvalue, "00000000", 8) == 0)
check2 = 1;
if (check1 == 0 || check2 == 0) {
#if 0 /* Cisco sometimes sends START records without username. */
- radlog(L_ERR, "Accounting: no username in record");
+ radlog(L_ERR, "rlm_radutmp: no username in record");
return RLM_MODULE_FAIL;
#else
break;
#endif
}
- radlog(L_INFO, "Accounting: converting reboot records.");
+ radlog(L_INFO, "rlm_radutmp: converting reboot records.");
if (status == PW_STATUS_STOP)
status = PW_STATUS_ACCOUNTING_OFF;
if (status == PW_STATUS_START)
status = PW_STATUS_ACCOUNTING_ON;
- rb_record = 1;
} while(0);
time(&t);
memset(&ut, 0, sizeof(ut));
ut.porttype = 'A';
-
- /*
- * Copy the previous translated user name.
- */
- strncpy(ut.login, buffer, RUT_NAMESIZE);
+ ut.nas_address = htonl(INADDR_NONE);
/*
* First, find the interesting attributes.
switch (vp->attribute) {
case PW_LOGIN_IP_HOST:
case PW_FRAMED_IP_ADDRESS:
- framed_address = vp->lvalue;
- ut.framed_address = vp->lvalue;
+ ut.framed_address = vp->vp_ipaddr;
break;
case PW_FRAMED_PROTOCOL:
- protocol = vp->lvalue;
+ protocol = vp->vp_integer;
break;
case PW_NAS_IP_ADDRESS:
- nas_address = vp->lvalue;
- ut.nas_address = vp->lvalue;
+ ut.nas_address = vp->vp_ipaddr;
break;
- case PW_NAS_PORT_ID:
- ut.nas_port = vp->lvalue;
+ case PW_NAS_PORT:
+ ut.nas_port = vp->vp_integer;
port_seen = 1;
break;
case PW_ACCT_DELAY_TIME:
- ut.delay = vp->lvalue;
+ ut.delay = vp->vp_integer;
break;
case PW_ACCT_SESSION_ID:
/*
* Compensate.
*/
if (vp->length > 0 &&
- vp->strvalue[vp->length - 1] == 0)
+ vp->vp_strvalue[vp->length - 1] == 0)
off--;
if (off < 0) off = 0;
- memcpy(ut.session_id, vp->strvalue + off,
+ memcpy(ut.session_id, vp->vp_strvalue + off,
sizeof(ut.session_id));
break;
case PW_NAS_PORT_TYPE:
- if (vp->lvalue >= 0 && vp->lvalue <= 4)
- ut.porttype = porttypes[vp->lvalue];
- nas_port_type = vp->lvalue;
+ if (vp->vp_integer <= 4)
+ ut.porttype = porttypes[vp->vp_integer];
break;
case PW_CALLING_STATION_ID:
if(inst->callerid_ok)
- strNcpy(ut.caller_id,
- (char *)vp->strvalue,
+ strlcpy(ut.caller_id,
+ (char *)vp->vp_strvalue,
sizeof(ut.caller_id));
break;
}
* If we didn't find out the NAS address, use the
* originator's IP address.
*/
- if (nas_address == 0) {
- nas_address = request->packet->src_ipaddr;
- ut.nas_address = nas_address;
+ if (ut.nas_address == htonl(INADDR_NONE)) {
+ ut.nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+ nas = request->client->shortname;
+
+ } else if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr == ut.nas_address) { /* might be a client, might not be. */
+ nas = request->client->shortname;
+
+ } else {
+ /*
+ * The NAS isn't a client, it's behind
+ * a proxy server. In that case, just
+ * get the IP address.
+ */
+ nas = ip_ntoa(ip_name, ut.nas_address);
}
+ /*
+ * Set the protocol field.
+ */
if (protocol == PW_PPP)
ut.proto = 'P';
else if (protocol == PW_SLIP)
ut.time = t - ut.delay;
/*
- * See if this was a portmaster reboot.
+ * Get the utmp filename, via xlat.
*/
- if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
- radlog(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
- nas_name(nas_address));
- radutmp_zap(inst, nas_address, ut.time);
+ radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
+
+ /*
+ * See if this was a reboot.
+ *
+ * Hmm... we may not want to zap all of the users when
+ * the NAS comes up, because of issues with receiving
+ * UDP packets out of order.
+ */
+ if (status == PW_STATUS_ACCOUNTING_ON &&
+ (ut.nas_address != htonl(INADDR_NONE))) {
+ radlog(L_INFO, "rlm_radutmp: NAS %s restarted (Accounting-On packet seen)",
+ nas);
+ radutmp_zap(inst, filename, ut.nas_address, ut.time);
return RLM_MODULE_OK;
}
- if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
- radlog(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
- nas_name(nas_address));
- radutmp_zap(inst, nas_address, ut.time);
+
+ if (status == PW_STATUS_ACCOUNTING_OFF &&
+ (ut.nas_address != htonl(INADDR_NONE))) {
+ radlog(L_INFO, "rlm_radutmp: NAS %s rebooted (Accounting-Off packet seen)",
+ nas);
+ radutmp_zap(inst, filename, ut.nas_address, ut.time);
return RLM_MODULE_OK;
}
if (status != PW_STATUS_START &&
status != PW_STATUS_STOP &&
status != PW_STATUS_ALIVE) {
- radlog(L_ERR, "NAS %s port %d unknown packet type %d)",
- nas_name(nas_address), ut.nas_port, status);
+ radlog(L_ERR, "rlm_radutmp: NAS %s port %u unknown packet type %d)",
+ nas, ut.nas_port, status);
return RLM_MODULE_NOOP;
}
/*
+ * Translate the User-Name attribute, or whatever else
+ * they told us to use.
+ */
+ *buffer = '\0';
+ radius_xlat(buffer, sizeof(buffer), inst->username, request, NULL);
+
+ /*
+ * Copy the previous translated user name.
+ */
+ strlcpy(ut.login, buffer, RUT_NAMESIZE);
+
+ /*
* Perhaps we don't want to store this record into
* radutmp. We skip records:
*
- * - without a NAS-Port-Id (telnet / tcp access)
+ * - without a NAS-Port (telnet / tcp access)
* - with the username "!root" (console admin login)
*/
- if (!port_seen || strncmp(ut.login, "!root", RUT_NAMESIZE) == 0)
+ if (!port_seen) {
+ DEBUG2(" rlm_radutmp: No NAS-Port seen. Cannot do anything.");
+ DEBUG2(" rlm_radumtp: WARNING: checkrad will probably not work!");
return RLM_MODULE_NOOP;
+ }
+
+ if (strncmp(ut.login, "!root", RUT_NAMESIZE) == 0) {
+ DEBUG2(" rlm_radutmp: Not recording administrative user");
+
+ return RLM_MODULE_NOOP;
+ }
/*
* Enter into the radutmp file.
*/
- fd = open(inst->radutmp_fn, O_RDWR|O_CREAT, inst->permission);
- if (fd >= 0) {
- NAS_PORT *cache;
- int r;
+ fd = open(filename, O_RDWR|O_CREAT, inst->permission);
+ if (fd < 0) {
+ radlog(L_ERR, "rlm_radutmp: Error accessing file %s: %s",
+ filename, strerror(errno));
+ return RLM_MODULE_FAIL;
+ }
+
+ /*
+ * Lock the utmp file, prefer lockf() over flock().
+ */
+ rad_lockfd(fd, LOCK_LEN);
- /*
- * Lock the utmp file, prefer lockf() over flock().
- */
- radutmp_lock(fd);
+ /*
+ * Find the entry for this NAS / portno combination.
+ */
+ if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
+ ut.nas_port)) != NULL) {
+ lseek(fd, (off_t)cache->offset, SEEK_SET);
+ }
+
+ r = 0;
+ off = 0;
+ while (read(fd, &u, sizeof(u)) == sizeof(u)) {
+ off += sizeof(u);
+ if (u.nas_address != ut.nas_address ||
+ u.nas_port != ut.nas_port)
+ continue;
/*
- * Find the entry for this NAS / portno combination.
+ * Don't compare stop records to unused entries.
*/
- if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address,
- ut.nas_port)) != NULL)
- lseek(fd, (off_t)cache->offset, SEEK_SET);
-
- r = 0;
- off = 0;
- while (read(fd, &u, sizeof(u)) == sizeof(u)) {
- off += sizeof(u);
- if (u.nas_address != ut.nas_address ||
- u.nas_port != ut.nas_port)
- continue;
-
- if (status == PW_STATUS_STOP &&
- strncmp(ut.session_id, u.session_id,
- sizeof(u.session_id)) != 0) {
- /*
- * Don't complain if this is not a
- * login record (some clients can
- * send _only_ logout records).
- */
- if (u.type == P_LOGIN)
- radlog(L_ERR,
- "Accounting: logout: entry for NAS %s port %d has wrong ID",
- nas_name(nas_address), u.nas_port);
- r = -1;
- break;
- }
+ if (status == PW_STATUS_STOP &&
+ u.type == P_IDLE) {
+ continue;
+ }
- if (status == PW_STATUS_START &&
- strncmp(ut.session_id, u.session_id,
- sizeof(u.session_id)) == 0 &&
- u.time >= ut.time) {
- if (u.type == P_LOGIN) {
- radlog(L_INFO,
- "Accounting: login: entry for NAS %s port %d duplicate",
- nas_name(nas_address), u.nas_port);
- r = -1;
- break;
- }
- radlog(L_ERR,
- "Accounting: login: entry for NAS %s port %d wrong order",
- nas_name(nas_address), u.nas_port);
+ if (status == PW_STATUS_STOP &&
+ strncmp(ut.session_id, u.session_id,
+ sizeof(u.session_id)) != 0) {
+ /*
+ * Don't complain if this is not a
+ * login record (some clients can
+ * send _only_ logout records).
+ */
+ if (u.type == P_LOGIN)
+ radlog(L_ERR, "rlm_radutmp: Logout entry for NAS %s port %u has wrong ID",
+ nas, u.nas_port);
+ r = -1;
+ break;
+ }
+
+ if (status == PW_STATUS_START &&
+ strncmp(ut.session_id, u.session_id,
+ sizeof(u.session_id)) == 0 &&
+ u.time >= ut.time) {
+ if (u.type == P_LOGIN) {
+ radlog(L_INFO, "rlm_radutmp: Login entry for NAS %s port %u duplicate",
+ nas, u.nas_port);
r = -1;
break;
}
+ radlog(L_ERR, "rlm_radutmp: Login entry for NAS %s port %u wrong order",
+ nas, u.nas_port);
+ r = -1;
+ break;
+ }
+ /*
+ * FIXME: the ALIVE record could need
+ * some more checking, but anyway I'd
+ * rather rewrite this mess -- miquels.
+ */
+ if (status == PW_STATUS_ALIVE &&
+ strncmp(ut.session_id, u.session_id,
+ sizeof(u.session_id)) == 0 &&
+ u.type == P_LOGIN) {
/*
- * FIXME: the ALIVE record could need
- * some more checking, but anyway I'd
- * rather rewrite this mess -- miquels.
+ * Keep the original login time.
*/
- if (status == PW_STATUS_ALIVE &&
- strncmp(ut.session_id, u.session_id,
- sizeof(u.session_id)) == 0 &&
- u.type == P_LOGIN) {
- /*
- * Keep the original login time.
- */
- ut.time = u.time;
- if (u.login[0] != 0)
- just_an_update = 1;
- }
-
- if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
- radlog(L_ERR, "Accounting: negative lseek!\n");
- lseek(fd, (off_t)0, SEEK_SET);
- off = 0;
- } else
- off -= sizeof(u);
- r = 1;
- break;
+ ut.time = u.time;
}
- if (r >= 0 && (status == PW_STATUS_START ||
- status == PW_STATUS_ALIVE)) {
- if (cache == NULL) {
- cache = rad_malloc(sizeof(NAS_PORT));
- cache->nasaddr = ut.nas_address;
- cache->port = ut.nas_port;
- cache->offset = off;
- cache->next = inst->nas_port_list;
- inst->nas_port_list = cache;
- }
- ut.type = P_LOGIN;
- write(fd, &ut, sizeof(u));
+ if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
+ radlog(L_ERR, "rlm_radutmp: negative lseek!");
+ lseek(fd, (off_t)0, SEEK_SET);
+ off = 0;
+ } else
+ off -= sizeof(u);
+ r = 1;
+ break;
+ } /* read the file until we find a match */
+
+ /*
+ * Found the entry, do start/update it with
+ * the information from the packet.
+ */
+ if (r >= 0 && (status == PW_STATUS_START ||
+ status == PW_STATUS_ALIVE)) {
+ /*
+ * Remember where the entry was, because it's
+ * easier than searching through the entire file.
+ */
+ if (cache == NULL) {
+ cache = rad_malloc(sizeof(NAS_PORT));
+ cache->nasaddr = ut.nas_address;
+ cache->port = ut.nas_port;
+ cache->offset = off;
+ cache->next = inst->nas_port_list;
+ inst->nas_port_list = cache;
}
- if (status == PW_STATUS_STOP) {
- if (r > 0) {
- u.type = P_IDLE;
- u.time = ut.time;
- u.delay = ut.delay;
- write(fd, &u, sizeof(u));
- } else if (r == 0) {
- radlog(L_ERR,
- "Accounting: logout: login entry for NAS %s port %d not found",
- nas_name(nas_address), ut.nas_port);
- r = -1;
- }
+
+ ut.type = P_LOGIN;
+ write(fd, &ut, sizeof(u));
+ }
+
+ /*
+ * The user has logged off, delete the entry by
+ * re-writing it in place.
+ */
+ if (status == PW_STATUS_STOP) {
+ if (r > 0) {
+ u.type = P_IDLE;
+ u.time = ut.time;
+ u.delay = ut.delay;
+ write(fd, &u, sizeof(u));
+ } else if (r == 0) {
+ radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
+ nas, ut.nas_port);
}
- close(fd);
- } else {
- radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
- return RLM_MODULE_FAIL;
}
+ close(fd); /* and implicitely release the locks */
return RLM_MODULE_OK;
}
+#endif
+#ifdef WITH_SESSION_MGMT
/*
* See if a user is already logged in. Sets request->simul_count to the
* current session count for this user and sets request->simul_mpp to 2
uint32_t ipno = 0;
char *call_num = NULL;
int rcode;
- struct radutmp_instance *inst = instance;
+ rlm_radutmp_t *inst = instance;
char login[256];
+ char filename[1024];
- if ((fd = open(inst->radutmp_fn, O_RDWR)) < 0) {
- if(errno!=ENOENT)
- return RLM_MODULE_FAIL;
- request->simul_count=0;
- return RLM_MODULE_OK;
+ /*
+ * Get the filename, via xlat.
+ */
+ radius_xlat(filename, sizeof(filename), inst->filename, request, NULL);
+
+ if ((fd = open(filename, O_RDWR)) < 0) {
+ /*
+ * If the file doesn't exist, then no users
+ * are logged in.
+ */
+ if (errno == ENOENT) {
+ request->simul_count=0;
+ return RLM_MODULE_OK;
+ }
+
+ /*
+ * Error accessing the file.
+ */
+ radlog(L_ERR, "rlm_radumtp: Error accessing file %s: %s",
+ filename, strerror(errno));
+ return RLM_MODULE_FAIL;
}
*login = '\0';
radius_xlat(login, sizeof(login), inst->username, request, NULL);
- if(*login == '\0')
- return RLM_MODULE_FAIL;
+ if (!*login) {
+ return RLM_MODULE_NOOP;
+ }
+ /*
+ * WTF? This is probably wrong... we probably want to
+ * be able to check users across multiple session accounting
+ * methods.
+ */
request->simul_count = 0;
- while(read(fd, &u, sizeof(u)) == sizeof(u)) {
- if (strncmp(login, u.login, RUT_NAMESIZE) == 0
- && u.type == P_LOGIN)
+
+ /*
+ * Loop over utmp, counting how many people MAY be logged in.
+ */
+ while (read(fd, &u, sizeof(u)) == sizeof(u)) {
+ if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
+ (!inst->case_sensitive &&
+ (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
+ (u.type == P_LOGIN)) {
++request->simul_count;
+ }
}
- if(request->simul_count < request->simul_max) {
+ /*
+ * The number of users logged in is OK,
+ * OR, we've been told to not check the NAS.
+ */
+ if ((request->simul_count < request->simul_max) ||
+ !inst->check_nas) {
close(fd);
return RLM_MODULE_OK;
}
/*
* Setup some stuff, like for MPP detection.
*/
- if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
- ipno = vp->lvalue;
- if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL)
- call_num = vp->strvalue;
+ if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0)) != NULL)
+ ipno = vp->vp_ipaddr;
+ if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0)) != NULL)
+ call_num = vp->vp_strvalue;
/*
- * lockf() the file while reading/writing.
+ * lock the file while reading/writing.
*/
- radutmp_lock(fd);
+ rad_lockfd(fd, LOCK_LEN);
+ /*
+ * FIXME: If we get a 'Start' for a user/nas/port which is
+ * listed, but for which we did NOT get a 'Stop', then
+ * it's not a duplicate session. This happens with
+ * static IP's like DSL.
+ */
request->simul_count = 0;
while (read(fd, &u, sizeof(u)) == sizeof(u)) {
- if (strncmp(login, u.login, RUT_NAMESIZE) == 0
- && u.type == P_LOGIN) {
- char session_id[sizeof u.session_id+1];
- strNcpy(session_id, u.session_id, sizeof session_id);
+ if (((strncmp(login, u.login, RUT_NAMESIZE) == 0) ||
+ (!inst->case_sensitive &&
+ (strncasecmp(login, u.login, RUT_NAMESIZE) == 0))) &&
+ (u.type == P_LOGIN)) {
+ char session_id[sizeof(u.session_id) + 1];
+ char utmp_login[sizeof(u.login) + 1];
+
+ strlcpy(session_id, u.session_id, sizeof(session_id));
/*
- * rad_check_ts may take seconds to return,
- * and we don't want to block everyone else
- * while that's happening.
+ * The login name MAY fill the whole field,
+ * and thus won't be zero-filled.
+ *
+ * Note that we take the user name from
+ * the utmp file, as that's the canonical
+ * form. The 'login' variable may contain
+ * a string which is an upper/lowercase
+ * version of u.login. When we call the
+ * routine to check the terminal server,
+ * the NAS may be case sensitive.
+ *
+ * e.g. We ask if "bob" is using a port,
+ * and the NAS says "no", because "BOB"
+ * is using the port.
*/
- radutmp_unlock(fd);
- rcode = rad_check_ts(u.nas_address, u.nas_port, login,
- session_id);
- radutmp_lock(fd);
+ strlcpy(utmp_login, u.login, sizeof(u.login));
- if (rcode == 1) {
+ /*
+ * rad_check_ts may take seconds
+ * to return, and we don't want
+ * to block everyone else while
+ * that's happening. */
+ rad_unlockfd(fd, LOCK_LEN);
+ rcode = rad_check_ts(u.nas_address, u.nas_port,
+ utmp_login, session_id);
+ rad_lockfd(fd, LOCK_LEN);
+
+ if (rcode == 0) {
+ /*
+ * Stale record - zap it.
+ */
+ session_zap(request, u.nas_address,
+ u.nas_port, login, session_id,
+ u.framed_address, u.proto,0);
+ }
+ else if (rcode == 1) {
+ /*
+ * User is still logged in.
+ */
++request->simul_count;
+
/*
* Does it look like a MPP attempt?
*/
}
else {
/*
- * False record - zap it.
+ * Failed to check the terminal
+ * server for duplicate logins:
+ * Return an error.
*/
-
- session_zap(u.nas_address, u.nas_port, login,
- session_id, u.framed_address,
- u.proto, 0);
+ close(fd);
+ radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
+ return RLM_MODULE_FAIL;
}
}
}
- close(fd);
+ close(fd); /* and implicitely release the locks */
return RLM_MODULE_OK;
}
+#endif
/* globally exported name */
module_t rlm_radutmp = {
- "radutmp",
- 0, /* type: reserved */
- NULL, /* initialization */
- radutmp_instantiate, /* instantiation */
- {
- NULL, /* authentication */
- NULL, /* authorization */
- NULL, /* preaccounting */
- radutmp_accounting, /* accounting */
- radutmp_checksimul /* checksimul */
- },
- radutmp_detach, /* detach */
- NULL, /* destroy */
+ RLM_MODULE_INIT,
+ "radutmp",
+ RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE, /* type */
+ radutmp_instantiate, /* instantiation */
+ radutmp_detach, /* detach */
+ {
+ NULL, /* authentication */
+ NULL, /* authorization */
+ NULL, /* preaccounting */
+#ifdef WITH_ACCOUNTING
+ radutmp_accounting, /* accounting */
+#else
+ NULL,
+#endif
+#ifdef WITH_SESSION_MGMT
+ radutmp_checksimul, /* checksimul */
+#else
+ NULL,
+#endif
+ NULL, /* pre-proxy */
+ NULL, /* post-proxy */
+ NULL /* post-auth */
+ },
};