Enable building WITHOUT_ACCOUNTING
[freeradius.git] / src / modules / rlm_radutmp / rlm_radutmp.c
index a81ec10..65fe1c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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";
@@ -64,7 +59,7 @@ typedef struct rlm_radutmp_t {
        int             callerid_ok;
 } rlm_radutmp_t;
 
-static CONF_PARSER module_config[] = {
+static const CONF_PARSER module_config[] = {
        { "filename", PW_TYPE_STRING_PTR,
          offsetof(rlm_radutmp_t,filename), NULL,  RADUTMP },
        { "username", PW_TYPE_STRING_PTR,
@@ -113,8 +108,6 @@ static int radutmp_detach(void *instance)
                next = p->next;
                free(p);
        }
-       if (inst->filename) free(inst->filename);
-       if (inst->username) free(inst->username);
        free(inst);
        return 0;
 }
@@ -122,7 +115,7 @@ static int radutmp_detach(void *instance)
 /*
  *     Zap all users on a NAS from the radutmp file.
  */
-static int radutmp_zap(rlm_radutmp_t *inst,
+static int radutmp_zap(UNUSED rlm_radutmp_t *inst,
                       const char *filename,
                       uint32_t nasaddr,
                       time_t t)
@@ -143,7 +136,7 @@ static int radutmp_zap(rlm_radutmp_t *inst,
         *      Lock the utmp file, prefer lockf() over flock().
         */
        rad_lockfd(fd, LOCK_LEN);
-       
+
        /*
         *      Find the entry for this NAS / portno combination.
         */
@@ -182,6 +175,7 @@ static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsign
 }
 
 
+#ifdef WITH_ACCOUNTING
 /*
  *     Store logins in the RADIUS utmp file.
  */
@@ -190,14 +184,10 @@ static int radutmp_accounting(void *instance, REQUEST *request)
        struct radutmp  ut, u;
        VALUE_PAIR      *vp;
        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;
        rlm_radutmp_t   *inst = instance;
        char            buffer[256];
@@ -207,14 +197,19 @@ static int radutmp_accounting(void *instance, REQUEST *request)
        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) {
+       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;
+       status = vp->vp_integer;
 
        /*
         *      Look for weird reboot packets.
@@ -233,12 +228,12 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                int check1 = 0;
                int check2 = 0;
 
-               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. */
@@ -258,6 +253,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
        time(&t);
        memset(&ut, 0, sizeof(ut));
        ut.porttype = 'A';
+       ut.nas_address = htonl(INADDR_NONE);
 
        /*
         *      First, find the interesting attributes.
@@ -266,22 +262,20 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                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:
-                               ut.nas_port = vp->lvalue;
+                               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:
                                /*
@@ -295,21 +289,20 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                                 *      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 <= 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;
                }
@@ -319,33 +312,20 @@ static int radutmp_accounting(void *instance, REQUEST *request)
         *      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;
-               nas = client_name(nas_address); /* MUST be a valid client */
+       if (ut.nas_address == htonl(INADDR_NONE)) {
+               ut.nas_address = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+               nas = request->client->shortname;
 
-       } else                /* might be a client, might not be. */
-               RADCLIENT *cl;
+       } 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 {
                /*
-                *      Hack like 'client_name()', but with sane
-                *      fall-back.
+                *      The NAS isn't a client, it's behind
+                *      a proxy server.  In that case, just
+                *      get the IP address.
                 */
-               cl = client_find(nas_address);
-               if (cl) {
-                       if (cl->shortname[0]) {
-                               nas = cl->shortname;
-                       } else {
-                               nas = cl->longname;
-                       }
-               } 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, nas_address);
-               }
+               nas = ip_ntoa(ip_name, ut.nas_address);
        }
 
        /*
@@ -371,17 +351,19 @@ static int radutmp_accounting(void *instance, REQUEST *request)
         *      the NAS comes up, because of issues with receiving
         *      UDP packets out of order.
         */
-       if (status == PW_STATUS_ACCOUNTING_ON && nas_address) {
+       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, nas_address, ut.time);
+               radutmp_zap(inst, filename, ut.nas_address, ut.time);
                return RLM_MODULE_OK;
        }
 
-       if (status == PW_STATUS_ACCOUNTING_OFF && nas_address) {
+       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, nas_address, ut.time);
+               radutmp_zap(inst, filename, ut.nas_address, ut.time);
                return RLM_MODULE_OK;
        }
 
@@ -406,7 +388,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
        /*
         *  Copy the previous translated user name.
         */
-       strncpy(ut.login, buffer, RUT_NAMESIZE);
+       strlcpy(ut.login, buffer, RUT_NAMESIZE);
 
        /*
         *      Perhaps we don't want to store this record into
@@ -441,7 +423,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
         *      Lock the utmp file, prefer lockf() over flock().
         */
        rad_lockfd(fd, LOCK_LEN);
-       
+
        /*
         *      Find the entry for this NAS / portno combination.
         */
@@ -449,7 +431,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                                   ut.nas_port)) != NULL) {
                lseek(fd, (off_t)cache->offset, SEEK_SET);
        }
-       
+
        r = 0;
        off = 0;
        while (read(fd, &u, sizeof(u)) == sizeof(u)) {
@@ -457,7 +439,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                if (u.nas_address != ut.nas_address ||
                    u.nas_port    != ut.nas_port)
                        continue;
-               
+
                /*
                 *      Don't compare stop records to unused entries.
                 */
@@ -480,7 +462,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                        r = -1;
                        break;
                }
-               
+
                if (status == PW_STATUS_START &&
                    strncmp(ut.session_id, u.session_id,
                            sizeof(u.session_id)) == 0  &&
@@ -496,7 +478,7 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                        r = -1;
                        break;
                }
-               
+
                /*
                 *      FIXME: the ALIVE record could need
                 *      some more checking, but anyway I'd
@@ -510,10 +492,8 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                         *      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, "rlm_radutmp: negative lseek!");
                        lseek(fd, (off_t)0, SEEK_SET);
@@ -542,11 +522,11 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                        cache->next = inst->nas_port_list;
                        inst->nas_port_list = cache;
                }
-               
+
                ut.type = P_LOGIN;
                write(fd, &ut, sizeof(u));
        }
-       
+
        /*
         *      The user has logged off, delete the entry by
         *      re-writing it in place.
@@ -560,14 +540,15 @@ static int radutmp_accounting(void *instance, REQUEST *request)
                } else if (r == 0) {
                        radlog(L_ERR, "rlm_radutmp: Logout for NAS %s port %u, but no Login record",
                               nas, ut.nas_port);
-                       r = -1;
                }
        }
        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
@@ -652,10 +633,10 @@ static int radutmp_checksimul(void *instance, REQUEST *request)
        /*
         *      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;
 
        /*
         *      lock the file while reading/writing.
@@ -677,7 +658,7 @@ static int radutmp_checksimul(void *instance, REQUEST *request)
                        char session_id[sizeof(u.session_id) + 1];
                        char utmp_login[sizeof(u.login) + 1];
 
-                       strNcpy(session_id, u.session_id, sizeof(session_id));
+                       strlcpy(session_id, u.session_id, sizeof(session_id));
 
                        /*
                         *      The login name MAY fill the whole field,
@@ -695,7 +676,7 @@ static int radutmp_checksimul(void *instance, REQUEST *request)
                         *      and the NAS says "no", because "BOB"
                         *      is using the port.
                         */
-                       strNcpy(utmp_login, u.login, sizeof(u.login));
+                       strlcpy(utmp_login, u.login, sizeof(u.login));
 
                        /*
                         *      rad_check_ts may take seconds
@@ -706,17 +687,19 @@ static int radutmp_checksimul(void *instance, REQUEST *request)
                        rcode = rad_check_ts(u.nas_address, u.nas_port,
                                             utmp_login, session_id);
                        rad_lockfd(fd, LOCK_LEN);
-                       
-                       /*
-                        *      Failed to check the terminal server for
-                        *      duplicate logins: Return an error.
-                        */
-                       if (rcode < 0) {
-                               close(fd);
-                               return RLM_MODULE_FAIL;
-                       }
 
-                       if (rcode == 1) {
+                       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;
 
                                /*
@@ -731,12 +714,13 @@ static int radutmp_checksimul(void *instance, REQUEST *request)
                        }
                        else {
                                /*
-                                *      False record - zap it.
+                                *      Failed to check the terminal
+                                *      server for duplicate logins:
+                                *      Return an error.
                                 */
-                               session_zap(request,
-                                           u.nas_address, u.nas_port, login,
-                                           session_id, u.framed_address,
-                                           u.proto);
+                               close(fd);
+                               radlog(L_ERR, "rlm_radutmp: Failed to check the terminal server for user '%s'.", utmp_login);
+                               return RLM_MODULE_FAIL;
                        }
                }
        }
@@ -744,24 +728,32 @@ static int radutmp_checksimul(void *instance, REQUEST *request)
 
        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 */
-         NULL,                 /* pre-proxy */
-         NULL,                 /* post-proxy */
-         NULL                  /* post-auth */
-  },
-  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 */
+       },
 };