]> git.ozlabs.org Git - ppp.git/blobdiff - pppd/plugins/radius/radius.c
Add option 'avpair' to send arbitrary RADIUS attributes to the server.
[ppp.git] / pppd / plugins / radius / radius.c
index 29b283610d7c139b0ba8569fd6d4206f7db5b6ca..53757abcbb1701f0b9952fbe64853b08c4e4b7fe 100644 (file)
@@ -24,7 +24,7 @@
 *
 ***********************************************************************/
 static char const RCSID[] =
-"$Id: radius.c,v 1.10 2002/07/13 06:24:36 kad Exp $";
+"$Id: radius.c,v 1.16 2002/10/01 08:36:49 fcusack Exp $";
 
 #include "pppd.h"
 #include "chap.h"
@@ -40,13 +40,20 @@ static char const RCSID[] =
 #include <syslog.h>
 #include <sys/types.h>
 #include <sys/time.h>
+#include <string.h>
 
 #define BUF_LEN 1024
 
 static char *config_file = NULL;
+static int add_avp(char **);
+static struct avpopt {
+    char *vpstr;
+    struct avpopt *next;
+} *avpopt = NULL;
 
 static option_t Options[] = {
     { "radius-config-file", o_string, &config_file },
+    { "avpair", o_special, add_avp },
     { NULL }
 };
 
@@ -70,6 +77,7 @@ static void radius_choose_ip(u_int32_t *addrp);
 static int radius_init(char *msg);
 static int get_client_port(char *ifname);
 static int radius_allowed_address(u_int32_t addr);
+static void radius_acct_interim(void *);
 #ifdef MPPE
 static int radius_setmppekeys(VALUE_PAIR *vp, REQUEST_INFO *req_info,
                              chap_state *);
@@ -80,6 +88,10 @@ static int radius_setmppekeys2(VALUE_PAIR *vp, REQUEST_INFO *req_info);
 #define MAXSESSIONID 32
 #endif
 
+#ifndef MAXCLASSLEN
+#define MAXCLASSLEN 500
+#endif
+
 struct radius_state {
     int accounting_started;
     int initialized;
@@ -92,8 +104,12 @@ struct radius_state {
     char config_file[MAXPATHLEN];
     char session_id[MAXSESSIONID + 1];
     time_t start_time;
+    int acct_interim_interval;
     SERVER *authserver;                /* Authentication server to use */
     SERVER *acctserver;                /* Accounting server to use */
+    int class_len;
+    char class[MAXCLASSLEN];
+    VALUE_PAIR *avp;   /* Additional (user supplied) vp's to send to server */
 };
 
 void (*radius_attributes_hook)(VALUE_PAIR *) = NULL;
@@ -142,6 +158,28 @@ plugin_init(void)
     info("RADIUS plugin initialized.");
 }
 
+/**********************************************************************
+* %FUNCTION: add_avp
+* %ARGUMENTS:
+*  argv -- the <attribute=value> pair to add
+* %RETURNS:
+*  1
+* %DESCRIPTION:
+*  Adds an av pair to be passed on to the RADIUS server on each request.
+***********************************************************************/
+static int
+add_avp(char **argv)
+{
+    struct avpopt *p = malloc(sizeof(struct avpopt));
+
+    /* Append to a list of vp's for later parsing */
+    p->vpstr = strdup(*argv);
+    p->next = avpopt;
+    avpopt = p;
+
+    return 1;
+}
+
 /**********************************************************************
 * %FUNCTION: radius_secret_check
 * %ARGUMENTS:
@@ -236,6 +274,10 @@ radius_pap_auth(char *user,
                       VENDOR_NONE);
     }
 
+    /* Add user specified vp's */
+    if (rstate.avp)
+       rc_avpair_insert(&send, NULL, rstate.avp);
+
     if (rstate.authserver) {
        result = rc_auth_using_server(rstate.authserver,
                                      rstate.client_port, send,
@@ -389,6 +431,10 @@ radius_chap_auth(char *user,
 
     }
 
+    /* Add user specified vp's */
+    if (rstate.avp)
+       rc_avpair_insert(&send, NULL, rstate.avp);
+
     /*
      * make authentication with RADIUS server
      */
@@ -511,9 +557,19 @@ radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg,
                break;
            case PW_OCTETS_DIRECTION:
                /* Session traffic limit direction check */
-               maxoctets_dir = vp->lvalue & 3;
+               maxoctets_dir = ( vp->lvalue > 4 ) ? 0 : vp->lvalue ;
+               break;
+#endif
+           case PW_ACCT_INTERIM_INTERVAL:
+               /* Send accounting updates every few seconds */
+               rstate.acct_interim_interval = vp->lvalue;
+               /* RFC says it MUST NOT be less than 60 seconds */
+               /* We use "0" to signify not sending updates */
+               if (rstate.acct_interim_interval &&
+                   rstate.acct_interim_interval < 60) {
+                   rstate.acct_interim_interval = 60;
+               }
                break;
-#endif         
            case PW_FRAMED_IP_ADDRESS:
                /* seting up remote IP addresses */
                remote = vp->lvalue;
@@ -532,7 +588,16 @@ radius_setparams(chap_state *cstate, VALUE_PAIR *vp, char *msg,
                    rstate.ip_addr = remote;
                }
                break;
+           case PW_CLASS:
+               /* Save Class attribute to pass it in accounting request */
+               if (vp->lvalue <= MAXCLASSLEN) {
+                   rstate.class_len=vp->lvalue;
+                   memcpy(rstate.class, vp->strvalue, rstate.class_len);
+               } /* else too big for our buffer - ignore it */
+               break;
            }
+
+
 #ifdef CHAPMS
        } else if (vp->vendorcode == VENDOR_MICROSOFT) {
            switch (vp->attribute) {
@@ -744,6 +809,10 @@ radius_acct_start(void)
     rc_avpair_add(&send, PW_USER_NAME,
                   rstate.user, 0, VENDOR_NONE);
 
+    if (rstate.class_len > 0)
+       rc_avpair_add(&send, PW_CLASS,
+                     rstate.class, rstate.class_len, VENDOR_NONE);
+
     av_type = PW_STATUS_START;
     rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE);
 
@@ -769,6 +838,10 @@ radius_acct_start(void)
     av_type = htonl(hisaddr);
     rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
 
+    /* Add user specified vp's */
+    if (rstate.avp)
+       rc_avpair_insert(&send, NULL, rstate.avp);
+
     if (rstate.acctserver) {
        result = rc_acct_using_server(rstate.acctserver,
                                      rstate.client_port, send);
@@ -784,6 +857,10 @@ radius_acct_start(void)
                "Accounting START failed for %s", rstate.user);
     } else {
        rstate.accounting_started = 1;
+       /* Kick off periodic accounting reports */
+       if (rstate.acct_interim_interval) {
+           TIMEOUT(radius_acct_interim, NULL, rstate.acct_interim_interval);
+       }
     }
 }
 
@@ -861,6 +938,10 @@ radius_acct_stop(void)
     av_type = htonl(hisaddr);
     rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
 
+    /* Add user specified vp's */
+    if (rstate.avp)
+       rc_avpair_insert(&send, NULL, rstate.avp);
+
     if (rstate.acctserver) {
        result = rc_acct_using_server(rstate.acctserver,
                                      rstate.client_port, send);
@@ -876,6 +957,105 @@ radius_acct_stop(void)
     rc_avpair_free(send);
 }
 
+/**********************************************************************
+* %FUNCTION: radius_acct_interim
+* %ARGUMENTS:
+*  None
+* %RETURNS:
+*  Nothing
+* %DESCRIPTION:
+*  Sends an interim accounting message to the RADIUS server
+***********************************************************************/
+static void
+radius_acct_interim(void *ignored)
+{
+    UINT4 av_type;
+    VALUE_PAIR *send = NULL;
+    ipcp_options *ho = &ipcp_hisoptions[0];
+    u_int32_t hisaddr;
+    int result;
+
+    if (!rstate.initialized) {
+       return;
+    }
+
+    if (!rstate.accounting_started) {
+       return;
+    }
+
+    rc_avpair_add(&send, PW_ACCT_SESSION_ID, rstate.session_id,
+                  0, VENDOR_NONE);
+
+    rc_avpair_add(&send, PW_USER_NAME, rstate.user, 0, VENDOR_NONE);
+
+    av_type = PW_STATUS_ALIVE;
+    rc_avpair_add(&send, PW_ACCT_STATUS_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_FRAMED;
+    rc_avpair_add(&send, PW_SERVICE_TYPE, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_PPP;
+    rc_avpair_add(&send, PW_FRAMED_PROTOCOL, &av_type, 0, VENDOR_NONE);
+
+    av_type = PW_RADIUS;
+    rc_avpair_add(&send, PW_ACCT_AUTHENTIC, &av_type, 0, VENDOR_NONE);
+
+    /* Update link stats */
+    update_link_stats(0);
+
+    if (link_stats_valid) {
+       link_stats_valid = 0; /* Force later code to update */
+
+       av_type = link_connect_time;
+       rc_avpair_add(&send, PW_ACCT_SESSION_TIME, &av_type, 0, VENDOR_NONE);
+
+       av_type = link_stats.bytes_out;
+       rc_avpair_add(&send, PW_ACCT_OUTPUT_OCTETS, &av_type, 0, VENDOR_NONE);
+
+       av_type = link_stats.bytes_in;
+       rc_avpair_add(&send, PW_ACCT_INPUT_OCTETS, &av_type, 0, VENDOR_NONE);
+
+       av_type = link_stats.pkts_out;
+       rc_avpair_add(&send, PW_ACCT_OUTPUT_PACKETS, &av_type, 0, VENDOR_NONE);
+
+       av_type = link_stats.pkts_in;
+       rc_avpair_add(&send, PW_ACCT_INPUT_PACKETS, &av_type, 0, VENDOR_NONE);
+    }
+
+    if (*remote_number) {
+       rc_avpair_add(&send, PW_CALLING_STATION_ID,
+                      remote_number, 0, VENDOR_NONE);
+    }
+
+    av_type = PW_ASYNC;
+    rc_avpair_add(&send, PW_NAS_PORT_TYPE, &av_type, 0, VENDOR_NONE);
+
+    hisaddr = ho->hisaddr;
+    av_type = htonl(hisaddr);
+    rc_avpair_add(&send, PW_FRAMED_IP_ADDRESS , &av_type , 0, VENDOR_NONE);
+
+    /* Add user specified vp's */
+    if (rstate.avp)
+       rc_avpair_insert(&send, NULL, rstate.avp);
+
+    if (rstate.acctserver) {
+       result = rc_acct_using_server(rstate.acctserver,
+                                     rstate.client_port, send);
+    } else {
+       result = rc_acct(rstate.client_port, send);
+    }
+
+    if (result != OK_RC) {
+       /* RADIUS server could be down so make this a warning */
+       syslog(LOG_WARNING,
+               "Interim accounting failed for %s", rstate.user);
+    }
+    rc_avpair_free(send);
+
+    /* Schedule another one */
+    TIMEOUT(radius_acct_interim, NULL, rstate.acct_interim_interval);
+}
+
 /**********************************************************************
 * %FUNCTION: radius_ip_up
 * %ARGUMENTS:
@@ -947,6 +1127,16 @@ radius_init(char *msg)
                 rc_conf_str("mapfile"));
        return -1;
     }
+
+    /* Add av pairs saved during option parsing */
+    while (avpopt) {
+       struct avpopt *n = avpopt->next;
+
+       rc_avpair_parse(avpopt->vpstr, &rstate.avp);
+       free(avpopt->vpstr);
+       free(avpopt);
+       avpopt = n;
+    }
     return 0;
 }