Moved all radutmp functionality from radiusd into rlm_radutmp.
authorpacman <pacman>
Mon, 15 Jan 2001 06:03:05 +0000 (06:03 +0000)
committerpacman <pacman>
Mon, 15 Jan 2001 06:03:05 +0000 (06:03 +0000)
13 files changed:
raddb/radiusd.conf.in
src/include/conf.h
src/include/radiusd.h
src/include/radutmp.h
src/main/Makefile
src/main/acct.c
src/main/auth.c
src/main/radiusd.c
src/main/radutmp.c [deleted file]
src/main/radzap.c
src/main/session.c
src/modules/rlm_radutmp/Makefile
src/modules/rlm_radutmp/rlm_radutmp.c

index b26a3bd..607e5e7 100644 (file)
@@ -567,6 +567,25 @@ modules {
                sqltrace = no
        }
 
+       radutmp {
+               filename = ${logdir}/radutmp
+               perm = 0600
+               callerid = "yes"
+       }
+
+       # "Safe" radutmp - does not contain caller ID, so it can be
+       # world-readable, and radwho can work for normal users, without
+       # exposing any information that isn't already exposed by who(1).
+       #
+       # This is another instance of the radutmp module, but it is given
+       # then name "sradutmp" to identify it later in the "accounting"
+       # section.
+       radutmp sradutmp {
+               filename = ${logdir}/sradutmp
+               perm = 0644
+               callerid = "no"
+       }
+
        #
        # The "always" module is here for debugging purposes. Each instance simply
        # returns the same result, always, without doing anything.
@@ -664,15 +683,18 @@ preacct {
        preprocess
 }
 
-# Accounting. Log to detail file, and to the radwtmp file.
+# Accounting. Log to detail file, and to the radwtmp file, and maintain
+# radutmp.
 accounting {
        #acct_unique
        detail
        unix
+       radutmp
+       #sradutmp
 }
 
 # Session database, used for checking Simultaneous-Use. The radutmp module
 # handles this
 session {
-       #radutmp
+       radutmp
 }
index 15deb35..7b0620a 100644 (file)
@@ -11,7 +11,9 @@
 #define RADIUS_REALMS          "realms"
 
 #define RADUTMP                        LOGDIR "/radutmp"
+#define SRADUTMP               LOGDIR "/sradutmp"
 #define RADWTMP                        LOGDIR "/radwtmp"
+#define SRADWTMP               LOGDIR "/sradwtmp"
 
 #define CHECKRAD               SBINDIR "/checkrad"
 
index b0574df..aee2585 100644 (file)
@@ -168,10 +168,12 @@ extern uint32_t           myip;
 extern int             log_auth_detail;
 extern int             auth_port;
 extern int             acct_port;
+extern int             acctfd;
 extern int             proxy_port;
 extern int             proxyfd;
 extern int             proxy_retry_count;
 extern int             proxy_retry_delay;
+extern int             spawn_flag;
 
 /* Define a global config structure */
 extern struct main_config_t mainconfig;
@@ -183,11 +185,6 @@ extern struct main_config_t mainconfig;
 /* acct.c */
 int            rad_accounting(REQUEST *);
 
-/* radutmp.c */
-int            radutmp_add(REQUEST *);
-int            radutmp_zap(uint32_t nas, int port, char *user, time_t t);
-int            radutmp_checksimul(char *name, VALUE_PAIR *, int maxsimul);
-
 /* session.c */
 int            rad_check_ts(uint32_t nasaddr, int port, const char *user,
                             const char *sessionid);
@@ -199,6 +196,7 @@ int         session_zap(uint32_t nasaddr, int port, const char *user,
 void           debug_pair(FILE *, VALUE_PAIR *);
 int            log_err (char *);
 void           sig_cleanup(int);
+int            rad_process(REQUEST *, int);
 int            rad_respond(REQUEST *, RAD_REQUEST_FUNP fun);
 
 /* util.c */
index 57796cb..3beff1a 100644 (file)
@@ -50,5 +50,6 @@ struct radutmp {
  *     Take the size of the structure from the actual structure definition.
  */
 #define RUT_NAMESIZE sizeof(((struct radutmp *) NULL)->login)
+#define RUT_SESSSIZE sizeof(((struct radutmp *) NULL)->session_id)
 
 #endif /* _RADUTMP_H */
index 3cfd8a1..ad505b0 100644 (file)
@@ -6,7 +6,7 @@ include ../../Make.inc
 
 SERVER_OBJS    = radiusd.o files.o util.o acct.o nas.o log.o valuepair.o \
                  version.o proxy.o exec.o auth.o timestr.o conffile.o \
-                 modules.o modcall.o radutmp.o xlat.o threads.o smux.o \
+                 modules.o modcall.o session.o xlat.o threads.o smux.o \
                  radius_snmp.o client.o request_list.o
 INCLUDES       = ../include/radiusd.h ../include/conf.h ../include/autoconf.h 
 
@@ -61,9 +61,6 @@ modules.o:  modules.c $(INCLUDES)
 modcall.o:  modcall.c $(INCLUDES)
        $(CC) $(CFLAGS) $(INCLTDL) -c modcall.c
 
-radutmp.o:  radutmp.c $(INCLUDES)
-       $(CC) $(CFLAGS) -c radutmp.c
-
 session.o:  session.c $(INCLUDES) ../include/modules.h
        $(CC) $(CFLAGS) -c session.c
 
@@ -124,8 +121,8 @@ raduse: raduse.o
 radzap.o: radzap.c $(INCLUDES)
        $(CC) $(CFLAGS) -c radzap.c
 
-radzap: radzap.o util.o nas.o radutmp.o log.o client.o
-       $(CC) $(CFLAGS) $(LDFLAGS) -o radzap radzap.o util.o nas.o radutmp.o log.o client.o $(LIBS)
+radzap: radzap.o util.o nas.o log.o client.o
+       $(CC) $(CFLAGS) $(LDFLAGS) -o radzap radzap.o util.o nas.o log.o client.o $(LIBS)
 
 clean:
        rm -rf *.o *.so *~ modules_static.h radiusd radwho raduse \
index 1cc39ad..3705ca8 100644 (file)
@@ -64,11 +64,6 @@ int rad_accounting(REQUEST *request)
        reply = RLM_MODULE_OK;
        if (!request->proxy) {
                /*
-                *      Keep the radutmp file in sync.
-                */
-               radutmp_add(request);
-
-               /*
                 *      Do accounting and if OK, reply.
                 */
                reply = module_accounting(request);
index 6bf4438..1d3e93d 100644 (file)
@@ -553,8 +553,7 @@ int rad_authenticate(REQUEST *request)
                 *      for the Simultaneous-Use parameter.
                 */
                if (namepair &&
-                   (r = radutmp_checksimul((char *)namepair->strvalue,
-                   request->packet->vps, check_item->lvalue)) != 0) {
+                   (r = module_checksimul(request, check_item->lvalue)) != 0) {
 
                        if (check_item->lvalue > 1) {
                                sprintf(umsg,
index 4b4885b..d36bb74 100644 (file)
@@ -113,7 +113,7 @@ struct      main_config_t   mainconfig;
 
 static int             got_child = FALSE;
 static int             authfd;
-static int             acctfd;
+int                    acctfd;
 int                    proxyfd;
 static pid_t           radius_pid;
 static int             request_num_counter = 0; /* per-request unique ID */
@@ -132,7 +132,7 @@ static gid_t                server_gid;
 static const char      *uid_name = NULL;
 static const char      *gid_name = NULL;
 static int             proxy_requests = TRUE;
-static int             spawn_flag = TRUE;
+int                    spawn_flag = TRUE;
 static struct rlimit   core_limits;
 
 static void    usage(void);
@@ -141,7 +141,6 @@ static void sig_fatal (int);
 static void    sig_hup (int);
 
 static void    rad_reject(REQUEST *request);
-static int     rad_process (REQUEST *, int);
 static struct timeval *rad_clean_list(time_t curtime);
 static REQUEST *rad_check_list(REQUEST *);
 static REQUEST *proxy_check_list(REQUEST *request);
@@ -1352,8 +1351,12 @@ int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun)
         *      Note that we do this CPU-intensive work in
         *      a child thread, not the master.  This helps to
         *      spread the load a little bit.
+        *
+        *      Internal requests (ones that never go on the
+        *      wire) have ->data==NULL (data is the wire
+        *      format) and don't need to be "decoded"
         */
-       if (rad_decode(packet, original, secret) != 0) {
+       if (packet->data && rad_decode(packet, original, secret) != 0) {
                radlog(L_ERR, "%s", librad_errstr);
                rad_reject(request);
                goto finished_request;
diff --git a/src/main/radutmp.c b/src/main/radutmp.c
deleted file mode 100644 (file)
index a85f88e..0000000
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * radutmp.c   Radius session management.
- *
- * Version:    $Id$
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   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
- *
- * Copyright 2000  The FreeRADIUS server project
- * Copyright 2000  Alan DeKok <aland@ox.org>
- */
-
-static const char rcsid[] =
-"$Id$";
-
-#include       "autoconf.h"
-#include       "libradius.h"
-
-#include       <sys/file.h>
-#include       <sys/stat.h>
-
-#include       <fcntl.h>
-#include       <stdlib.h>
-#include       <string.h>
-#include       <ctype.h>
-#include       <signal.h>
-
-#if HAVE_SYS_WAIT_H
-# include <sys/wait.h>
-#endif
-#ifndef WEXITSTATUS
-# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
-#endif
-#ifndef WIFEXITED
-# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
-#endif
-
-#include       "radiusd.h"
-#include       "radutmp.h"
-
-static const char porttypes[] = "ASITX";
-
-#define LOCK_LEN sizeof(struct radutmp)
-
-/*
- *     used for caching radutmp lookups.
- */
-typedef struct nas_port {
-       uint32_t                nasaddr;
-       int                     port;
-       off_t                   offset;
-       struct nas_port         *next;
-} NAS_PORT;
-static NAS_PORT *nas_port_list = NULL;
-
-/*
- *     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
-}
-
-/*
- *     Lookup a NAS_PORT in the nas_port_list
- */
-static NAS_PORT *nas_port_find(uint32_t nasaddr, int port)
-{
-       NAS_PORT        *cl;
-
-       for(cl = nas_port_list; cl; cl = cl->next)
-               if (nasaddr == cl->nasaddr &&
-                       port == cl->port)
-                       break;
-       return cl;
-}
-
-
-/*
- *     Zap a user, or all users on a NAS, from the radutmp file.
- */
-int radutmp_zap(uint32_t nasaddr, int port, char *user, time_t t)
-{
-       struct radutmp  u;
-       FILE            *fp;
-       int             fd;
-
-       if (t == 0) time(&t);
-       fp = fopen(RADWTMP, "a");
-
-       if ((fd = open(RADUTMP, O_RDWR|O_CREAT, 0644)) >= 0) {
-               int r;
-
-               radutmp_lock(fd);
-               
-               /*
-                *      Find the entry for this NAS / portno combination.
-                */
-               r = 0;
-               while (read(fd, &u, sizeof(u)) == sizeof(u)) {
-                       if (((nasaddr != 0 && nasaddr != u.nas_address) ||
-                             (port >= 0   && port    != u.nas_port) ||
-                             (user != NULL &&
-                              strncmp(u.login, user, sizeof(u.login)) != 0) ||
-                              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));
-
-#if 0 /* FIXME: should we fixup radwtmp as well or not ? */
-                       /*
-                        *      Add a logout entry to the wtmp file.
-                        */
-                       if (fp != NULL)  {
-                               make_wtmp(&u, &wt, PW_STATUS_STOP);
-                               fwrite(&wt, sizeof(wt), 1, fp);
-                       }
-#endif
-               }
-               close(fd);
-       }
-       if (fp) fclose(fp);
-
-       return 0;
-}
-
-
-/*
- *     Store logins in the RADIUS utmp file.
- */
-int radutmp_add(REQUEST *request)
-{
-       struct radutmp  ut, u;
-       VALUE_PAIR      *vp;
-       int             rb_record = 0;
-       int             status = -1;
-       int             nas_address = 0;
-       int             framed_address = 0;
-       int             protocol = -1;
-       time_t          t;
-       int             fd;
-       int             ret = 0;
-       int             just_an_update = 0;
-       int             port_seen = 0;
-       int             nas_port_type = 0;
-       int             off;
-
-       /*
-        *      Which type is this.
-        */
-       if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
-               radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
-               return -1;
-       }
-       status = vp->lvalue;
-       if (status == PW_STATUS_ACCOUNTING_ON ||
-           status == PW_STATUS_ACCOUNTING_OFF) rb_record = 1;
-
-       if (!rb_record &&
-           (vp = pairfind(request->packet->vps, PW_USER_NAME)) == NULL) 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)
-                       check1 = 1;
-               if ((vp = pairfind(request->packet->vps, PW_ACCT_SESSION_ID))
-                    != NULL && vp->length == 8 &&
-                    memcmp(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");
-                       return -1;
-#else
-                       break;
-#endif
-               }
-               radlog(L_INFO, "Accounting: 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';
-
-       /*
-        *      First, find the interesting attributes.
-        */
-       for (vp = request->packet->vps; vp; vp = vp->next) {
-               switch (vp->attribute) {
-                       case PW_USER_NAME:
-                               strncpy(ut.login, (char *)vp->strvalue,
-                                       RUT_NAMESIZE);
-                               break;
-                       case PW_LOGIN_IP_HOST:
-                       case PW_FRAMED_IP_ADDRESS:
-                               framed_address = vp->lvalue;
-                               ut.framed_address = vp->lvalue;
-                               break;
-                       case PW_FRAMED_PROTOCOL:
-                               protocol = vp->lvalue;
-                               break;
-                       case PW_NAS_IP_ADDRESS:
-                               nas_address = vp->lvalue;
-                               ut.nas_address = vp->lvalue;
-                               break;
-                       case PW_NAS_PORT_ID:
-                               ut.nas_port = vp->lvalue;
-                               port_seen = 1;
-                               break;
-                       case PW_ACCT_DELAY_TIME:
-                               ut.delay = vp->lvalue;
-                               break;
-                       case PW_ACCT_SESSION_ID:
-                               /*
-                                *      If length > 8, only store the
-                                *      last 8 bytes.
-                                */
-                               off = vp->length - sizeof(ut.session_id);
-                               if (off < 0) off = 0;
-                               memcpy(ut.session_id, 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;
-                               break;
-                       case PW_CALLING_STATION_ID:
-                               strncpy(ut.caller_id, (char *)vp->strvalue,
-                                       sizeof(ut.caller_id));
-                               ut.caller_id[sizeof(ut.caller_id) - 1] = 0;
-                               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 (protocol == PW_PPP)
-               ut.proto = 'P';
-       else if (protocol == PW_SLIP)
-               ut.proto = 'S';
-       else
-               ut.proto = 'T';
-       ut.time = t - ut.delay;
-
-       /*
-        *      See if this was a portmaster reboot.
-        */
-       if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
-               radlog(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
-                       nas_name(nas_address));
-               radutmp_zap(nas_address, -1, NULL, ut.time);
-               return 0;
-       }
-       if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
-               radlog(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
-                       nas_name(nas_address));
-               radutmp_zap(nas_address, -1, NULL, ut.time);
-               return 0;
-       }
-
-       /*
-        *      If we don't know this type of entry pretend we succeeded.
-        */
-       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);
-               return 0;
-       }
-
-       /*
-        *      Perhaps we don't want to store this record into
-        *      radutmp. We skip records:
-        *
-        *      - without a NAS-Port-Id (telnet / tcp access)
-        *      - with the username "!root" (console admin login)
-        */
-       if (!port_seen || strncmp(ut.login, "!root", RUT_NAMESIZE) == 0)
-               return 0;
-
-       /*
-        *      Enter into the radutmp file.
-        */
-       if ((fd = open(RADUTMP, O_RDWR|O_CREAT, 0644)) >= 0) {
-               NAS_PORT *cache;
-               int r;
-
-               radutmp_lock(fd);
-
-               /*
-                *      Find the entry for this NAS / portno combination.
-                */
-               if ((cache = nas_port_find(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_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);
-                               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) {
-                               /*
-                                *      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;
-               }
-
-               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 = nas_port_list;
-                               nas_port_list = cache;
-                       }
-                       ut.type = P_LOGIN;
-                       write(fd, &ut, sizeof(u));
-               }
-               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;
-                       }
-               }
-               close(fd);
-       } else {
-               radlog(L_ERR, "Accounting: %s: %s", RADUTMP, strerror(errno));
-               ret = -1;
-       }
-
-       return ret;
-}
-
-
-/*
- *     Timeout handler (10 secs)
- */
-static int got_alrm;
-static void alrm_handler(int sig)
-{
-       sig = sig; /* -Wunused */
-       got_alrm = 1;
-}
-
-/*
- *     Check one terminal server to see if a user is logged in.
- */
-int rad_check_ts(uint32_t nasaddr, int portnum, const char *user,
-                 const char *session_id)
-{
-       int     pid, st, e;
-       int     n;
-       NAS     *nas;
-       char    address[16];
-       char    port[8];
-       void    (*handler)(int);
-
-       /*
-        *      Find NAS type.
-        */
-       if ((nas = nas_find(nasaddr)) == NULL) {
-               radlog(L_ERR, "Accounting: unknown NAS");
-               return -1;
-       }
-
-       /*
-        *      Fork.
-        */
-       handler = signal(SIGCHLD, SIG_DFL);
-       if ((pid = fork()) < 0) {
-               radlog(L_ERR, "Accounting: fork: %s", strerror(errno));
-               signal(SIGCHLD, handler);
-               return -1;
-       }
-
-       if (pid > 0) {
-               /*
-                *      Parent - Wait for checkrad to terminate.
-                *      We timeout in 10 seconds.
-                */
-               got_alrm = 0;
-               signal(SIGALRM, alrm_handler);
-               alarm(10);
-               while((e = waitpid(pid, &st, 0)) != pid)
-                       if (e < 0 && (errno != EINTR || got_alrm))
-                               break;
-               alarm(0);
-               signal(SIGCHLD, handler);
-               if (got_alrm) {
-                       kill(pid, SIGTERM);
-                       sleep(1);
-                       kill(pid, SIGKILL);
-                       radlog(L_ERR, "Check-TS: timeout waiting for checkrad");
-                       return 2;
-               }
-               if (e < 0) {
-                       radlog(L_ERR, "Check-TS: unknown error in waitpid()");
-                       return 2;
-               }
-               return WEXITSTATUS(st);
-       }
-
-       /*
-        *      Child - exec checklogin with the right parameters.
-        */
-       for (n = 32; n >= 3; n--)
-               close(n);
-
-       ip_ntoa(address, nasaddr);
-       sprintf(port, "%d", portnum);
-
-#ifdef __EMX__
-       /* OS/2 can't directly execute scripts then we call the command
-          processor to execute checkrad
-       */
-       execl(getenv("COMSPEC"), "", "/C","checkrad",nas->nastype, address, port,
-               user, session_id, NULL);
-#else
-       execl(CHECKRAD, "checkrad",nas->nastype, address, port,
-               user, session_id, NULL);
-#endif
-       radlog(L_ERR, "Check-TS: exec %s: %s", CHECKRAD, strerror(errno));
-
-       /*
-        *      Exit - 2 means "some error occured".
-        */
-       exit(2);
-}
-
-/*
- *     See if a user is already logged in.
- *
- *     Check twice. If on the first pass the user exceeds his
- *     max. number of logins, do a second pass and validate all
- *     logins by querying the terminal server (using eg. SNMP).
- *
- *     Returns: 0 == OK, 1 == double logins, 2 == multilink attempt
- */
-int radutmp_checksimul(char *name, VALUE_PAIR *request, int maxsimul)
-{
-       VALUE_PAIR      *fra;
-       struct radutmp  u;
-       uint32_t        ipno = 0;
-       int             fd;
-       int             count;
-       int             mpp = 1;
-       int             rcode;
-
-       if ((fd = open(RADUTMP, O_CREAT|O_RDWR, 0644)) < 0)
-               return 0;
-
-       /*
-        *      We don't lock in the first pass.
-        */
-       count = 0;
-       while(read(fd, &u, sizeof(u)) == sizeof(u))
-               if (strncmp(name, u.login, RUT_NAMESIZE) == 0
-                   && u.type == P_LOGIN)
-                       count++;
-
-       if (count < maxsimul) {
-               close(fd);
-               return 0;
-       }
-       lseek(fd, (off_t)0, SEEK_SET);
-
-       /*
-        *      Setup some stuff, like for MPP detection.
-        */
-       if ((fra = pairfind(request, PW_FRAMED_IP_ADDRESS)) != NULL)
-               ipno = fra->lvalue;
-
-       radutmp_lock(fd);
-
-       /*
-        *      Allright, there are too many concurrent logins.
-        *      Check all registered logins by querying the
-        *      terminal server directly.
-        *      FIXME: rad_check_ts() runs with locked radutmp file!
-        */
-       count = 0;
-       while (read(fd, &u, sizeof(u)) == sizeof(u)) {
-               if (strncmp(name, 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);
-
-                       /*
-                        *      rad_check_ts may take seconds to return,
-                        *      and we don't want to block everyone else
-                        *      while that's happening.
-                        */
-                       radutmp_unlock(fd);
-                       rcode = rad_check_ts(u.nas_address, u.nas_port,
-                                            u.login, session_id);
-                       radutmp_lock(fd);
-
-                       if (rcode == 1) {
-                               count++;
-                               /*
-                                *      Does it look like a MPP attempt?
-                                */
-                               if (strchr("SCPA", u.proto) &&
-                                   ipno && u.framed_address == ipno)
-                                       mpp = 2;
-                       }
-                       else {
-                               /*
-                                *      False record - zap it.
-                                */
-
-                               lseek(fd, -(off_t)sizeof(u), SEEK_CUR);
-                               u.type = P_IDLE;
-                               write(fd, &u, sizeof(u));
-
-#if 0 /* FIXME: should we fixup radwtmp as well or not ? */
-                               if ((wfp = fopen(RADWTMP, "a")) != NULL) {
-                                       make_wtmp(&u, &wt, PW_STATUS_STOP);
-                                       fwrite(&wt, sizeof(wt), 1, wfp);
-                                       fclose(wfp);
-                               }
-#endif
-                       }
-               }
-       }
-       close(fd);
-
-       return (count < maxsimul) ? 0 : mpp;
-}
-
index c9963ed..1860b32 100644 (file)
 
 #include       <stdio.h>
 #include       <stdlib.h>
+#include       <string.h>
 #include       <fcntl.h>
+#include       <netdb.h>
+#include       <limits.h>
+
+#if HAVE_NETINET_IN_H
+#  include      <netinet/in.h>
+#endif
 
 #if HAVE_MALLOC_H
 #  include     <malloc.h>
 #endif
 
 #include       "radiusd.h"
+#include       "radutmp.h"
 
+/* FIXME: Some of the following are unused and just there to make the linker
+ * happy. Also all of log.o is linked in mainly to make the linker happy. */
 int debug_flag = 0;
-const char *progname = "radzap";
+const char *progname;
 const char *radlog_dir = NULL;
+const char *radius_dir = NULL;
+#if 0
+int auth_port; /* Not really used */
+#endif
+int acct_port = 0;
+
+#define LOCK_LEN sizeof(struct radutmp)
+
+/*
+ *     Internal wrapper for locking, to minimize the number of ifdef's
+ *     in the source. Copied from rlm_radutmp.c (was src/main/radutmp.c),
+ *     perhaps these wrappers should be #defined in radutmp.h
+ *
+ *     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
+}
+
+static int radutmp_lookup(struct radutmp *u, uint32_t nasaddr, int port,
+                         const char *user)
+{
+       int             fd;
+
+       if ((fd = open(RADUTMP, O_RDONLY|O_CREAT, 0644)) >= 0) {
+               /*
+                *      Lock the utmp file, prefer lockf() over flock().
+                */
+               radutmp_lock(fd);
+
+               /*
+                *      Find the entry for this NAS / portno combination.
+                */
+               while (read(fd, u, sizeof(*u)) == sizeof(*u)) {
+                       if ((nasaddr != 0 && nasaddr != u->nas_address) ||
+                             (port >= 0  && port    != u->nas_port) ||
+                             (user != NULL &&
+                              strncmp(u->login, user, sizeof u->login) != 0) ||
+                              u->type != P_LOGIN)
+                               continue;
+                       /*
+                        *      Match. Zap it.
+                        */
+                       close(fd);
+                       return 1;
+               }
+               close(fd);
+       }
+       return 0;
+}
+static int do_accton_packet(uint32_t nasaddr);
+static int do_stop_packet(const struct radutmp *u);
 
 /*
  *     Zap a user from the radutmp and radwtmp file.
@@ -50,20 +132,28 @@ int main(int argc, char **argv)
        int     nas_port = -1;
        char    *user = NULL;
        char    *s;
-       time_t  t;
        char    buf[256];
+       struct radutmp u;
 
-       if (argc < 2 || argc > 4 || (argc > 1 && argv[1][0] == '-')) {
+       progname = argv[0];
+       --argc, ++argv;
+       if (argc > 1 && !strcmp(argv[0], "-p")) {
+               acct_port = atoi(argv[1]);
+               argc -= 2, argv+=2;
+       }
+       if (argc < 1 || argc > 3 || argv[1][0] == '-') {
                fprintf(stderr, "Usage: radzap termserver [port] [user]\n");
                fprintf(stderr, "       radzap is only an admin tool to clean the radutmp file!\n");
                exit(1);
        }
-       if (argc > 2) {
-               s = argv[2];
+       if (argc > 1) {
+               s = argv[1];
                if (*s == 's' || *s == 'S') s++;
                nas_port = atoi(s);
        }
-       if (argc > 3) user     = argv[3];
+       if (argc > 2) user     = argv[2];
+
+       radius_dir = strdup(RADIUS_DIR);
 
        /*
         *      Read the "naslist" file.
@@ -75,9 +165,9 @@ int main(int argc, char **argv)
        /*
         *      Find the IP address of the terminal server.
         */
-       if ((nas = nas_findbyname(argv[1])) == NULL && argv[1][0] != 0) {
-               if ((ip = ip_getaddr(argv[1])) == INADDR_NONE) {
-                       fprintf(stderr, "%s: host not found.\n", argv[1]);
+       if ((nas = nas_findbyname(argv[0])) == NULL && argv[0][0] != 0) {
+               if ((ip = ip_getaddr(argv[0])) == INADDR_NONE) {
+                       fprintf(stderr, "%s: host not found.\n", argv[0]);
                        exit(1);
                }
        }
@@ -88,8 +178,191 @@ int main(int argc, char **argv)
        if (user) printf(", user %s", user);
        printf("\n");
 
-       t = time(NULL);
-       radutmp_zap(ip, nas_port, user, t);
+       if(nas_port < 0) {
+               return do_accton_packet(ip);
+       }
+
+       if(!radutmp_lookup(&u, ip, nas_port, user)) {
+               fprintf(stderr, "Entry not found\n");
+               return 1;
+       }
+
+       return do_stop_packet(&u);
+}
+
+static int getport(const char *name)
+{
+       struct  servent         *svp;
+
+       svp = getservbyname (name, "udp");
+       if (!svp) {
+               return 0;
+       }
+
+       return ntohs(svp->s_port);
+}
+
+static const char *getlocalhostsecret(void)
+{
+       RADCLIENT *cl;
+       char fn[PATH_MAX];
+       snprintf(fn, sizeof fn, "%s/%s", radius_dir, RADIUS_CLIENTS);
+       if(read_clients_file(fn)<0) {
+               radlog(L_ERR|L_CONS, "Errors reading clients");
+               exit(1);
+       }
+       cl=client_find(htonl(INADDR_LOOPBACK));
+       if(!cl) {
+               radlog(L_ERR|L_CONS, "No clients entry for localhost");
+               exit(1);
+       }
+       return (const char *)cl->secret;
+}
+
+/* Packet-fabrication macros. Don't stare directly at them without protective
+ * eye gear */
+#define PAIR(n,v,t,e) do { \
+  if(!(vp=paircreate(n, t))) { \
+    radlog(L_ERR|L_CONS, "no memory"); \
+    pairfree(&req->vps); \
+    return 1; \
+  } \
+  vp->e=v; \
+  pairadd(&req->vps, vp); \
+} while(0)
+#define INTPAIR(n,v) PAIR(n,v,PW_TYPE_INTEGER,lvalue)
+#define IPPAIR(n,v) PAIR(n,v,PW_TYPE_IPADDR,lvalue)
+#define STRINGPAIR(n,v) do { \
+  if(!(vp=paircreate(n, PW_TYPE_STRING))) { \
+    radlog(L_ERR|L_CONS, "no memory"); \
+    pairfree(&req->vps); \
+    return 1; \
+  } \
+  strNcpy((char *)vp->strvalue, v, sizeof vp->strvalue); \
+  vp->length=strlen(v); \
+  pairadd(&req->vps, vp); \
+} while(0)
+
+static int do_packet(int allports, uint32_t nasaddr, const struct radutmp *u)
+{
+       int i, retries=5, timeout=3;
+       struct timeval tv;
+       RADIUS_PACKET *req, *rep;
+       VALUE_PAIR *vp;
+       const char *secret=getlocalhostsecret();
+
+       if ((req = rad_alloc(1)) == NULL) {
+               librad_perror("radzap");
+               exit(1);
+       }
+       req->id = getpid() & 0xFF;
+       req->code = PW_ACCOUNTING_REQUEST;
+        req->dst_port = acct_port;
+       if(req->dst_port == 0) req->dst_port = getport("radacct");
+       if(req->dst_port == 0) req->dst_port = PW_ACCT_UDP_PORT;
+       req->dst_ipaddr = ip_getaddr("localhost");
+       if(!req->dst_ipaddr) req->dst_ipaddr = 0x7f000001;
+       req->vps = NULL;
+
+       if(allports) {
+               INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_ACCOUNTING_OFF);
+               IPPAIR(PW_NAS_IP_ADDRESS, nasaddr);
+               INTPAIR(PW_ACCT_DELAY_TIME, 0);
+       } else {
+               char login[sizeof u->login+1];
+               char session_id[sizeof u->session_id+1];
+               strNcpy(login, u->login, sizeof login);
+               strNcpy(session_id, u->session_id, sizeof session_id);
+               INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
+               IPPAIR(PW_NAS_IP_ADDRESS, u->nas_address);
+               INTPAIR(PW_ACCT_DELAY_TIME, 0);
+               STRINGPAIR(PW_USER_NAME, login);
+               INTPAIR(PW_NAS_PORT_ID, u->nas_port);
+               STRINGPAIR(PW_ACCT_SESSION_ID, session_id);
+               if(u->proto=='P') {
+                 INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
+                 INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP);
+               } else if(u->proto=='S') {
+                 INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
+                 INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP);
+               } else {
+                 INTPAIR(PW_SERVICE_TYPE, PW_LOGIN_USER); /* A guess, really */
+               }
+               IPPAIR(PW_FRAMED_IP_ADDRESS, u->framed_address);
+               INTPAIR(PW_ACCT_SESSION_TIME, 0);
+               INTPAIR(PW_ACCT_INPUT_OCTETS, 0);
+               INTPAIR(PW_ACCT_OUTPUT_OCTETS, 0);
+               INTPAIR(PW_ACCT_INPUT_PACKETS, 0);
+               INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0);
+       }
+       if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               perror("radzap: socket: ");
+               exit(1);
+       }
+
+       for (i = 0; i < retries; i++) {
+               fd_set          rdfdesc;
+
+               rad_send(req, secret);
+
+               /* And wait for reply, timing out as necessary */
+               FD_ZERO(&rdfdesc);
+               FD_SET(req->sockfd, &rdfdesc);
 
+               tv.tv_sec = (int)timeout;
+               tv.tv_usec = 1000000 * (timeout - (int)timeout);
+
+               /* Something's wrong if we don't get exactly one fd. */
+               if (select(req->sockfd + 1, &rdfdesc, NULL, NULL, &tv) != 1) {
+                       continue;
+               }
+
+               rep = rad_recv(req->sockfd);
+               if (rep != NULL) {
+                       break;
+               } else {        /* NULL: couldn't receive the packet */
+                       librad_perror("radzap:");
+                       exit(1);
+               }
+       }
+
+       /* No response or no data read (?) */
+       if (i == retries) {
+               fprintf(stderr, "radzap: no response from server\n");
+               exit(1);
+       }
+
+       if (rad_decode(rep, req, secret) != 0) {
+               librad_perror("rad_decode");
+               exit(1);
+       }
+
+       vp_printlist(stdout, rep->vps);
        return 0;
 }
+
+static int do_accton_packet(uint32_t nasaddr)
+{
+  return do_packet(1, nasaddr, 0);
+}
+
+static int do_stop_packet(const struct radutmp *u)
+{
+  return do_packet(0, 0, u);
+}
+
+#if 0
+/* FIXME: Not called. Needed for files.o to link. Ick */
+int setup_modules(void); /* -Wmissing-prototypes */
+int setup_modules(void)
+{
+  abort();
+}
+
+/* FIXME: Not called. Needed for files.o to link. Ick */
+int read_radius_conf_file(void); /* -Wmissing-prototypes */
+int read_radius_conf_file(void)
+{
+  abort();
+}
+#endif
index b9bf0de..cfc458d 100644 (file)
 int session_zap(uint32_t nasaddr, int port, const char *user,
                const char *sessionid, uint32_t cliaddr, char proto, time_t t)
 {
-  static unsigned char id=0;
+  static unsigned char id = 0;
 
-  REQUEST stopreq;
-  RADIUS_PACKET stoppkt;
+  REQUEST *stopreq;
+  RADIUS_PACKET *stoppkt;
   VALUE_PAIR *vp, *userpair;
-  int modret;
   int ret;
 
-  memset(&stoppkt, 0, sizeof stoppkt);
-  stoppkt.data=0;
-  stoppkt.sockfd=-1;
-  stoppkt.code=PW_ACCOUNTING_REQUEST;
-  stoppkt.id=id++;
-  stoppkt.timestamp=t?t:time(0);
-  stoppkt.vps=0;
+  stoppkt = rad_malloc(sizeof *stoppkt);
+  memset(stoppkt, 0, sizeof stoppkt);
+  stoppkt->data = NULL;
+  stoppkt->sockfd = acctfd;
+  stoppkt->code = PW_ACCOUNTING_REQUEST;
+  stoppkt->id = id++;
+  stoppkt->timestamp = t?t:time(0);
+  stoppkt->vps = NULL;
 
   /* Hold your breath */
 #define PAIR(n,v,t,e) do { \
-    if(!(vp=paircreate(n, t))) { \
+    if(!(vp = paircreate(n, t))) { \
       radlog(L_ERR|L_CONS, "no memory"); \
-      pairfree(&stoppkt.vps); \
+      pairfree(&stoppkt->vps); \
       return 0; \
     } \
-    vp->e=v; \
-    pairadd(&stoppkt.vps, vp); \
+    vp->e = v; \
+    pairadd(&stoppkt->vps, vp); \
   } while(0)
 #define INTPAIR(n,v) PAIR(n,v,PW_TYPE_INTEGER,lvalue)
 #define IPPAIR(n,v) PAIR(n,v,PW_TYPE_IPADDR,lvalue)
 #define STRINGPAIR(n,v) do { \
-    if(!(vp=paircreate(n, PW_TYPE_STRING))) { \
+    if(!(vp = paircreate(n, PW_TYPE_STRING))) { \
       radlog(L_ERR|L_CONS, "no memory"); \
-      pairfree(&stoppkt.vps); \
+      pairfree(&stoppkt->vps); \
       return 0; \
     } \
     strNcpy((char *)vp->strvalue, v, sizeof vp->strvalue); \
-    vp->length=strlen(v); \
-    pairadd(&stoppkt.vps, vp); \
+    vp->length = strlen(v); \
+    pairadd(&stoppkt->vps, vp); \
   } while(0)
 
   INTPAIR(PW_ACCT_STATUS_TYPE, PW_STATUS_STOP);
   IPPAIR(PW_NAS_IP_ADDRESS, nasaddr);
   INTPAIR(PW_ACCT_DELAY_TIME, 0);
   STRINGPAIR(PW_USER_NAME, user);
-  userpair=vp;
+  userpair = vp;
   INTPAIR(PW_NAS_PORT_ID, port);
   STRINGPAIR(PW_ACCT_SESSION_ID, sessionid);
-  if(proto=='P') {
+  if(proto == 'P') {
     INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
     INTPAIR(PW_FRAMED_PROTOCOL, PW_PPP);
-  } else if(proto=='S') {
+  } else if(proto == 'S') {
     INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
     INTPAIR(PW_FRAMED_PROTOCOL, PW_SLIP);
   } else {
@@ -101,27 +101,24 @@ int session_zap(uint32_t nasaddr, int port, const char *user,
   INTPAIR(PW_ACCT_INPUT_PACKETS, 0);
   INTPAIR(PW_ACCT_OUTPUT_PACKETS, 0);
 
-  memset(&stopreq, 0, sizeof stopreq);
-  stopreq.packet=&stoppkt;
-  stopreq.proxy=0;
-  stopreq.reply=0;
-  stopreq.config_items=0;
-  stopreq.username=userpair;
-  stopreq.child_pid=-1;
-  stopreq.timestamp=stoppkt.timestamp;
-  stopreq.next=0;
-
-  /* FIXME: Need to run preaccouting, so replication can work */
-  modret = module_accounting(&stopreq);
-  if(modret==RLM_MODULE_NOOP ||
-     modret==RLM_MODULE_OK ||
-     modret==RLM_MODULE_UPDATED ||
-     modret==RLM_MODULE_HANDLED)
-    ret=1;
-  else
-    ret=0;
-
-  pairfree(&stoppkt.vps);
+  stopreq = rad_malloc(sizeof *stopreq);
+  memset(stopreq, 0, sizeof *stopreq);
+#ifndef NDEBUG
+  stopreq->magic = REQUEST_MAGIC;
+#endif
+  stopreq->packet = stoppkt;
+  stopreq->proxy = NULL;
+  stopreq->reply = NULL;
+  stopreq->proxy_reply = NULL;
+  stopreq->config_items = NULL;
+  stopreq->username = userpair;
+  stopreq->password = NULL;
+  stopreq->timestamp = stoppkt->timestamp;
+  stopreq->number = 0; /* FIXME */
+  stopreq->child_pid = NO_SUCH_CHILD_PID;
+  stopreq->container = NULL;
+  ret = rad_process(stopreq, spawn_flag);
+
   return ret;
 }
 
index d601363..939b475 100644 (file)
@@ -1,12 +1,7 @@
 include config.mak
 
-#
-#  This module is NOT built by default, as it's in a REALLY alpha
-#  development stage, and doesn't integrate well with the rest of the
-#  system.
-#
-#TARGET        = rlm_radutmp
-#SRCS  = rlm_radutmp.c
+TARGET = rlm_radutmp
+SRCS   = rlm_radutmp.c
 
 include ../rules.mak
 
index c7d154a..46fca6b 100644 (file)
 
 #define LOCK_LEN sizeof(struct radutmp)
 
-static char porttypes[] = "ASITX";
+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 {
-       UINT4                   nasaddr;
+       uint32_t                nasaddr;
        int                     port;
        off_t                   offset;
        struct nas_port         *next;
 } NAS_PORT;
 
-struct acct_instance {
+struct radutmp_instance {
   NAS_PORT *nas_port_list;
-  char radutmp_fn[PATH_MAX];
+  char *radutmp_fn;
   int permission;
-  int callerid_ok:1;
+  int callerid_ok;
 };
 
-struct sess_instance {
-  char radutmp_fn[PATH_MAX];
-};
+/*
+ *     A temporary holding area for config values to be extracted
+ *     into, before they are copied into the instance data
+ */
+static struct radutmp_instance config;
 
+static CONF_PARSER module_config[] = {
+  { "filename", PW_TYPE_STRING_PTR, &config.radutmp_fn,  RADUTMP },
+  { "perm",     PW_TYPE_INTEGER,    &config.permission,  "0644" },
+  { "callerid", PW_TYPE_BOOLEAN,    &config.callerid_ok, "no" },
+  { NULL, -1, NULL, NULL }             /* end the list */
+};
 
-/* RADDB/modules syntax for radutmp accounting:
- *  accounting rlm_radutmp.so [filename] [permission] [option...]
- * filename defaults to the old location (usually /var/log/radutmp)
- * permission defaults to 644, but the umask radiusd was started with applies
- * current options are "callerid" and "nocallerid". default is nocallerid
- * unless a restrictive permission is specified.
- *
- * For session component:
- *  session rlm_radutmp.so [filename] */
-static int radutmp_instantiate(int component, int argc, char **argv,
-                              void **instance)
+static int radutmp_instantiate(CONF_SECTION *conf, void **instance)
 {
-       switch(component) {
-               case RLM_COMPONENT_ACCT:
-               {
-                       const char *fn=RADUTMP;
-                       unsigned int perm=0644;
-                       int callerid=-1;
-
-                       if(argc) {
-                               fn=argv[0];
-                               --argc,++argv;
-                       }
-                       if(argc) {
-                               if(sscanf(argv[0], "%o", &perm)!=1) {
-                                       log(L_ERR, "Invalid permission %s",
-                                           argv[0]);
-                               }
-                               --argc,++argv;
-                       }
-                       while(argc) {
-                               if(!strcmp(argv[0], "callerid")) {
-                                       callerid=1;
-                               } else if(!strcmp(argv[0], "nocallerid")) {
-                                       callerid=0;
-                               } else {
-                                       log(L_ERR, "Unknown option %s",
-                                           argv[0]);
-                                       return -1;
-                               }
-                               --argc,++argv;
-                       }
-
-                       if(callerid==-1)
-                               callerid = !(perm & 004);
-
-                       *instance = malloc(sizeof(struct acct_instance));
-                       if (!*instance) {
-                               log(L_ERR|L_CONS, "Out of memory\n");
-                               return -1;
-                       }
-#define ru_instance ((struct acct_instance *)(*instance))
-                       ru_instance->nas_port_list=0;
-                       if(*fn=='/')
-                               strNcpy(ru_instance->radutmp_fn, fn, PATH_MAX);
-                       else
-                               snprintf(ru_instance->radutmp_fn, PATH_MAX,
-                                        "%s/%s", LOGDIR, fn);
-                       ru_instance->permission=perm;
-                       ru_instance->callerid_ok=callerid;
-#undef ru_instance
-               }
-                       break;
-               case RLM_COMPONENT_SESS:
-               {
-                       const char *fn=RADUTMP;
-
-                       if(argc) {
-                               fn=argv[0];
-                               --argc,++argv;
-                       }
-                       if(argc) {
-                               log(L_ERR,
-                                   "Too many args in radutmp session config\n");
-                               return -1;
-                       }
-
-                       *instance = malloc(sizeof(struct sess_instance));
-                       if (!*instance) {
-                               log(L_ERR|L_CONS, "Out of memory\n");
-                               return -1;
-                       }
-#define ru_instance ((struct sess_instance *)(*instance))
-                       if(*fn=='/')
-                               strNcpy(ru_instance->radutmp_fn, fn, PATH_MAX);
-                       else
-                               snprintf(ru_instance->radutmp_fn, PATH_MAX,
-                                        "%s/%s", LOGDIR, fn);
-#undef ru_instance
-               }
-                       break;
-               default:
-                       *instance=0;
-                       break;
+       *instance = rad_malloc(sizeof(struct radutmp_instance));
+       if (cf_section_parse(conf, module_config)) {
+               free(*instance);
+               return -1;
        }
+#define ru_instance ((struct radutmp_instance *)(*instance))
+       ru_instance->nas_port_list = NULL;
+       ru_instance->radutmp_fn = config.radutmp_fn;
+       ru_instance->permission = config.permission;
+       ru_instance->callerid_ok = config.callerid_ok;
+       config.radutmp_fn = NULL;
+#undef ru_instance
        return 0;
 }
 
@@ -171,32 +128,24 @@ static int radutmp_instantiate(int component, int argc, char **argv,
 /*
  *     Detach.
  */
-static int radutmp_detach(int component, void *instance)
+static int radutmp_detach(void *instance)
 {
-       switch(component) {
-               case RLM_COMPONENT_ACCT:
-               {
-                       NAS_PORT *p, *next;
-                       struct acct_instance *inst = instance;
-
-                       for(p=inst->nas_port_list ; p ; p=next) {
-                               next=p->next;
-                               free(p);
-                       }
-                       free(instance);
-               }
-                       break;
-               case RLM_COMPONENT_SESS:
-                       free(instance);
-                       break;
+       NAS_PORT *p, *next;
+       struct radutmp_instance *inst = instance;
+
+       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 acct_instance *inst, UINT4 nasaddr, time_t t)
+static int radutmp_zap(struct radutmp_instance *inst, uint32_t nasaddr, time_t t)
 {
        struct radutmp  u;
        int             fd;
@@ -208,11 +157,8 @@ static int radutmp_zap(struct acct_instance *inst, UINT4 nasaddr, time_t t)
                /*
                 *      Lock the utmp file, prefer lockf() over flock().
                 */
-#if defined(F_LOCK) && !defined(BSD)
-               (void)lockf(fd, F_LOCK, LOCK_LEN);
-#else
-               (void)flock(fd, LOCK_EX);
-#endif
+               radutmp_lock(fd);
+
                /*
                 *      Find the entry for this NAS / portno combination.
                 */
@@ -224,8 +170,8 @@ static int radutmp_zap(struct acct_instance *inst, UINT4 nasaddr, time_t t)
                         *      Match. Zap it.
                         */
                        if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
-                               log(L_ERR, "Accounting: radutmp_zap: "
-                                          "negative lseek!\n");
+                               radlog(L_ERR, "Accounting: radutmp_zap: "
+                                             "negative lseek!\n");
                                lseek(fd, (off_t)0, SEEK_SET);
                        }
                        u.type = P_IDLE;
@@ -233,6 +179,8 @@ static int radutmp_zap(struct acct_instance *inst, UINT4 nasaddr, time_t t)
                        write(fd, &u, sizeof(u));
                }
                close(fd);
+       } else {
+               radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
        }
 
        return 0;
@@ -241,7 +189,7 @@ static int radutmp_zap(struct acct_instance *inst, UINT4 nasaddr, time_t t)
 /*
  *     Lookup a NAS_PORT in the nas_port_list
  */
-static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, UINT4 nasaddr, int port)
+static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, int port)
 {
        NAS_PORT        *cl;
 
@@ -262,8 +210,8 @@ static int radutmp_accounting(void *instance, REQUEST *request)
        VALUE_PAIR      *vp;
        int             rb_record = 0;
        int             status = -1;
-       int             nas_address = 0;
-       int             framed_address = 0;
+       uint32_t        nas_address = 0;
+       uint32_t        framed_address = 0;
        int             protocol = -1;
        time_t          t;
        int             fd;
@@ -271,14 +219,14 @@ static int radutmp_accounting(void *instance, REQUEST *request)
        int             port_seen = 0;
        int             nas_port_type = 0;
        int             off;
-       struct acct_instance *inst = instance;
+       struct radutmp_instance *inst = instance;
 
        /*
         *      Which type is this.
         */
        if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
-               log(L_ERR, "Accounting: no Accounting-Status-Type record.");
-               return RLM_ACCT_FAIL;
+               radlog(L_ERR, "Accounting: no Accounting-Status-Type record.");
+               return RLM_MODULE_NOOP;
        }
        status = vp->lvalue;
        if (status == PW_STATUS_ACCOUNTING_ON ||
@@ -308,13 +256,13 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                        check2 = 1;
                if (check1 == 0 || check2 == 0) {
 #if 0 /* Cisco sometimes sends START records without username. */
-                       log(L_ERR, "Accounting: no username in record");
-                       return RLM_ACCT_FAIL;
+                       radlog(L_ERR, "Accounting: no username in record");
+                       return RLM_MODULE_FAIL;
 #else
                        break;
 #endif
                }
-               log(L_INFO, "Accounting: converting reboot records.");
+               radlog(L_INFO, "Accounting: converting reboot records.");
                if (status == PW_STATUS_STOP)
                        status = PW_STATUS_ACCOUNTING_OFF;
                if (status == PW_STATUS_START)
@@ -378,7 +326,8 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                                break;
                        case PW_CALLING_STATION_ID:
                                if(inst->callerid_ok)
-                                       strNcpy(ut.caller_id, vp->strvalue,
+                                       strNcpy(ut.caller_id,
+                                               (char *)vp->strvalue,
                                                sizeof(ut.caller_id));
                                break;
                }
@@ -405,16 +354,16 @@ static int radutmp_accounting(void *instance, REQUEST *request)
         *      See if this was a portmaster reboot.
         */
        if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
-               log(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
+               radlog(L_INFO, "NAS %s restarted (Accounting-On packet seen)",
                        nas_name(nas_address));
                radutmp_zap(inst, nas_address, ut.time);
-               return RLM_ACCT_OK;
+               return RLM_MODULE_OK;
        }
        if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
-               log(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
+               radlog(L_INFO, "NAS %s rebooted (Accounting-Off packet seen)",
                        nas_name(nas_address));
                radutmp_zap(inst, nas_address, ut.time);
-               return RLM_ACCT_OK;
+               return RLM_MODULE_OK;
        }
 
        /*
@@ -423,9 +372,9 @@ static int radutmp_accounting(void *instance, REQUEST *request)
        if (status != PW_STATUS_START &&
            status != PW_STATUS_STOP &&
            status != PW_STATUS_ALIVE) {
-               log(L_ERR, "NAS %s port %d unknown packet type %d)",
+               radlog(L_ERR, "NAS %s port %d unknown packet type %d)",
                        nas_name(nas_address), ut.nas_port, status);
-               return RLM_ACCT_FAIL_SOFT;
+               return RLM_MODULE_NOOP;
        }
 
        /*
@@ -436,7 +385,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
         *      - with the username "!root" (console admin login)
         */
        if (!port_seen || strncmp(ut.login, "!root", RUT_NAMESIZE) == 0)
-               return RLM_ACCT_OK;
+               return RLM_MODULE_NOOP;
 
        /*
         *      Enter into the radutmp file.
@@ -449,11 +398,8 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                /*
                 *      Lock the utmp file, prefer lockf() over flock().
                 */
-#if defined(F_LOCK) && !defined(BSD)
-               (void)lockf(fd, F_LOCK, LOCK_LEN);
-#else
-               (void)flock(fd, LOCK_EX);
-#endif
+               radutmp_lock(fd);
+
                /*
                 *      Find the entry for this NAS / portno combination.
                 */
@@ -478,7 +424,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                                 *      send _only_ logout records).
                                 */
                                if (u.type == P_LOGIN)
-                                       log(L_ERR,
+                                       radlog(L_ERR,
                "Accounting: logout: entry for NAS %s port %d has wrong ID",
                                        nas_name(nas_address), u.nas_port);
                                r = -1;
@@ -490,13 +436,13 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                             sizeof(u.session_id)) == 0  &&
                            u.time >= ut.time) {
                                if (u.type == P_LOGIN) {
-                                       log(L_INFO,
+                                       radlog(L_INFO,
                "Accounting: login: entry for NAS %s port %d duplicate",
                                        nas_name(nas_address), u.nas_port);
                                        r = -1;
                                        break;
                                }
-                               log(L_ERR,
+                               radlog(L_ERR,
                "Accounting: login: entry for NAS %s port %d wrong order",
                                nas_name(nas_address), u.nas_port);
                                r = -1;
@@ -521,7 +467,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                        }
 
                        if (lseek(fd, -(off_t)sizeof(u), SEEK_CUR) < 0) {
-                               log(L_ERR, "Accounting: negative lseek!\n");
+                               radlog(L_ERR, "Accounting: negative lseek!\n");
                                lseek(fd, (off_t)0, SEEK_SET);
                                off = 0;
                        } else
@@ -551,7 +497,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                                u.delay = ut.delay;
                                write(fd, &u, sizeof(u));
                        } else if (r == 0) {
-                               log(L_ERR,
+                               radlog(L_ERR,
                "Accounting: logout: login entry for NAS %s port %d not found",
                                nas_name(nas_address), ut.nas_port);
                                r = -1;
@@ -559,58 +505,65 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                }
                close(fd);
        } else {
-               log(L_ERR, "Accounting: %s: %s", inst->radutmp_fn, strerror(errno));
-               return RLM_ACCT_FAIL;
+               radlog(L_ERR, "Accounting: %s: %m", inst->radutmp_fn);
+               return RLM_MODULE_FAIL;
        }
 
-       return RLM_ACCT_OK;
+       return RLM_MODULE_OK;
 }
 
 /*
- *     See if a user is already logged in. Sets *count to the current
- *     session count for this user and sets *mpp to 2 if it looks like a
- *     multilink attempt based on the requested ipno, otherwise leaves it
- *     alone.
+ *     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
+ *     if it looks like a multilink attempt based on the requested IP
+ *     address, otherwise leaves request->simul_mpp alone.
+ *
+ *     Check twice. If on the first pass the user exceeds his
+ *     max. number of logins, do a second pass and validate all
+ *     logins by querying the terminal server (using eg. SNMP).
  */
-static int radutmp_checksimul(void *instance, const char *name, int *count,
-                             int doradcheck, UINT4 ipno, int *mpp)
+static int radutmp_checksimul(void *instance, REQUEST *request)
 {
        struct radutmp  u;
        int             fd;
-       struct sess_instance *inst = instance;
+       VALUE_PAIR      *fra;
+       uint32_t        ipno = 0;
+       int             rcode;
+       const char *name = (char *)request->username->strvalue;
+       struct radutmp_instance *inst = instance;
 
        if ((fd = open(inst->radutmp_fn, O_RDWR)) < 0) {
                if(errno!=ENOENT)
-                       return RLM_CSIM_FAIL;
-               *count=0;
-               return RLM_CSIM_OK;
+                       return RLM_MODULE_FAIL;
+               request->simul_count=0;
+               return RLM_MODULE_OK;
        }
 
-       if(!doradcheck) {
-               *count = 0;
-               while(read(fd, &u, sizeof(u)) == sizeof(u))
-                       if (strncmp(name, u.login, RUT_NAMESIZE) == 0
-                           && u.type == P_LOGIN)
-                               ++*count;
+       request->simul_count = 0;
+       while(read(fd, &u, sizeof(u)) == sizeof(u)) {
+               if (strncmp(name, u.login, RUT_NAMESIZE) == 0
+                   && u.type == P_LOGIN)
+                       ++request->simul_count;
+       }
+
+       if(request->simul_count < request->simul_max) {
                close(fd);
-               return RLM_CSIM_OK;
+               return RLM_MODULE_OK;
        }
+       lseek(fd, (off_t)0, SEEK_SET);
 
        /*
-        *      lockf() the file while reading/writing.
+        *      Setup some stuff, like for MPP detection.
         */
-#if defined(F_LOCK) && !defined(BSD)
-               (void)lockf(fd, F_LOCK, LOCK_LEN);
-#else
-               (void)flock(fd, LOCK_EX);
-#endif
+       if ((fra = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL)
+               ipno = fra->lvalue;
 
        /*
-        *      Check all registered logins by querying the
-        *      terminal server directly.
-        *      FIXME: rad_check_ts() runs with locked radutmp file!
+        *      lockf() the file while reading/writing.
         */
-       *count = 0;
+               radutmp_lock(fd);
+
+       request->simul_count = 0;
        while (read(fd, &u, sizeof(u)) == sizeof(u)) {
                if (strncmp(name, u.login, RUT_NAMESIZE) == 0
                    && u.type == P_LOGIN) {
@@ -618,15 +571,25 @@ static int radutmp_checksimul(void *instance, const char *name, int *count,
                        char session_id[sizeof u.session_id+1];
                        strNcpy(login, u.login, sizeof login);
                        strNcpy(session_id, u.session_id, sizeof session_id);
-                       if (rad_check_ts(u.nas_address, u.nas_port, login, 
-                                        session_id) == 1) {
-                               ++*count;
+
+                       /*
+                        *      rad_check_ts may take seconds to return,
+                        *      and we don't want to block everyone else
+                        *      while that's happening.
+                        */
+                       radutmp_unlock(fd);
+                       rcode = rad_check_ts(u.nas_address, u.nas_port, login, 
+                                            session_id);
+                       radutmp_lock(fd);
+
+                       if (rcode == 1) {
+                               ++request->simul_count;
                                /*
                                 *      Does it look like a MPP attempt?
                                 */
                                if (strchr("SCPA", u.proto) &&
                                    ipno && u.framed_address == ipno)
-                                       *mpp = 2;
+                                       request->simul_mpp = 2;
                        }
                        else {
                                /*
@@ -641,7 +604,7 @@ static int radutmp_checksimul(void *instance, const char *name, int *count,
        }
        close(fd);
 
-       return RLM_CSIM_OK;
+       return RLM_MODULE_OK;
 }
 
 /* globally exported name */
@@ -649,9 +612,10 @@ module_t rlm_radutmp = {
   "radutmp",
   0,                            /* type: reserved */
   NULL,                        /* initialization */
-  radutmp_instantiate,          /* initialization */
+  radutmp_instantiate,          /* instantiation */
   NULL,                         /* authorization */
   NULL,                         /* authentication */
+  NULL,                         /* preaccounting */
   radutmp_accounting,           /* accounting */
   radutmp_checksimul,          /* checksimul */
   radutmp_detach,               /* detach */