patch to radius.c

neff at panix.com neff at panix.com
Wed Mar 13 00:27:42 UTC 2002


I have attached a patch for radius.c (the program to authenticate against
a radius server).  This patch allows for multiple radius servers and uses
the configuration file routines in conffile.h.  If this patch doesn't
quite work, I would be happy to make modifications.  -- Felicia

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
neff at panix.com                           Felicia Neff
Panix Staff



-- Attached file included as plaintext by Ecartis --
-- File: radius.patch

--- radius.c.sav	Tue Mar 12 18:52:03 2002
+++ radius.c	Tue Mar 12 18:56:00 2002
@@ -2,6 +2,7 @@
 **
 **  Authenticate a user against a remote radius server.
 */
+
 #include "config.h"
 #include "clibrary.h"
 #include <netinet/in.h>
@@ -25,6 +26,7 @@
 #include "libinn.h"
 #include "nntp.h"
 #include "paths.h"
+#include "conffile.h"
 
 /* Needed on AIX 4.1 to get fd_set and friends. */
 #ifdef HAVE_SYS_SELECT_H
@@ -54,89 +56,171 @@
 
     char *prefix, *suffix;	/* futz with the username, if necessary */
     int ignore_source;
+
+    struct _rad_config_t *next;  /* point to any additional servers */
 } rad_config_t;
 
-int read_config(FILE *f, rad_config_t *radconfig)
+typedef struct _sending_t {
+    auth_req req;
+    int reqlen;
+    struct sockaddr_in sinr;
+    struct _sending_t *next;
+} sending_t;
+
+#define RADlbrace  1
+#define RADrbrace  2
+#define RADserver  10
+#define RADhost    11
+#define RADsecret  12
+#define RADport    13
+#define RADlochost 14
+#define RADlocport 15
+#define RADprefix  16
+#define RADsuffix  17
+#define RADsource  18
+
+static CONFTOKEN radtoks[] = {
+  { RADlbrace,   "{" },
+  { RADrbrace,   "}" },
+  { RADserver,   "server" },
+  { RADhost,     "radhost:" },
+  { RADsecret,   "secret:" },
+  { RADport,     "radport:" },
+  { RADlochost,  "lochost:" },
+  { RADlocport,  "locport:" },
+  { RADprefix,   "prefix:" },
+  { RADsuffix,   "suffix:" },
+  { RADsource,   "ignore-source:" },
+  { 0, 0 }
+};
+
+rad_config_t *get_radconf(void)
 {
-    char buf[SMBUF];
-    char *keyword, *iter;
-    int lineno;
-
-    buf[sizeof(buf)-1] = '\0';
-    lineno = 0;
-    while (fgets(buf, sizeof(buf)-1, f) != (char*) 0) {
-	lineno++;
-	buf[strlen(buf)-1] = '\0';	/* strip '\n' */
-
-	if (iter = strchr(buf, '#'))
-	    *iter = '\0';		/* strip comments */
-
-	iter = buf+strlen(buf)-1;	/* strip trailing whitespace */
-	while (iter >= buf && isspace(*iter))
-	    iter--;
-	iter[1] = '\0';
-
-	if (buf[0] == '\0')
-	    continue;			/* empty line */
-
-	/* get the keyword part of the keyword: value */
-	keyword = buf;
-	while (isspace(*keyword))
-	    keyword++;
-	iter = strchr(keyword, ':');
-	if (!iter) {
-	    fprintf(stderr, "malformed keyword in rad_config, line %d\n", lineno);
-	    exit(1);
-	}
-	*iter++ = '\0';
+  rad_config_t *new;
 
-	/* now the value part */
-	while (*iter && isspace(*iter))
-	    iter++;
-	if (!*iter) {
-	    fprintf(stderr, "expecting value after keyword %s in rad_config, line %d\n",
-	      keyword, lineno);
-	    exit(1);
-	}
+  new = malloc(sizeof(rad_config_t));
+  if (new == NULL){
+    fprintf(stderr, "radius: unable to malloc\n");
+    exit (1);
+  }
+  new->next = NULL;
+
+  return new;
+}
 
-        /* what are we setting? */
-	if (!strcmp(keyword, "secret")) {
+int read_config(char *authfile, rad_config_t *radconf)
+{
+    int inbrace;
+    rad_config_t *radconfig=NULL;
+    CONFFILE *file;
+    CONFTOKEN *token;
+    char *server;
+    int type;
+    char *iter;
+
+    if ((file = CONFfopen(authfile)) == NULL){
+      fprintf(stderr, "radius: couldn't open config file %s: %s\n", authfile, 
+	      strerror(errno));
+      exit (1);
+    }
+
+    inbrace = 0;
+    while ((token = CONFgettoken(radtoks, file)) != NULL) {
+      if (!inbrace) {
+	if (token->type != RADserver) {
+	  fprintf(stderr, "Expected 'server' keyword, line %d\n", file->lineno); 
+	  exit (1);
+	}
+	if ((token = CONFgettoken(0, file)) == NULL) {
+	  fprintf(stderr, "Expected server name, line %d\n", file->lineno);
+	  exit (1);
+	}
+	server = COPY(token->name);
+	if ((token = CONFgettoken(radtoks, file)) == NULL 
+	    || token->type != RADlbrace) {
+	  fprintf(stderr, "Expected '{', line %d\n", file->lineno);
+	  exit (1);
+	}
+	inbrace = 1;
+
+	if (radconfig == NULL)
+	  radconfig = radconf;
+	else {
+	  radconfig->next = get_radconf();
+	  radconfig = radconfig->next;
+	}
+      }
+      else {
+	type = token->type;
+	if (type == RADrbrace)
+	  inbrace = 0;
+	else {
+	  if ((token = CONFgettoken(0, file)) == NULL) {
+	    fprintf(stderr, "Keyword with no value, line %d\n", file->lineno);
+	    exit (1);
+	  }
+	  iter = token->name;
+
+	  /* what are we setting? */
+	  switch(type) {
+	  case RADsecret:
 	    if (radconfig->secret) continue;
 	    radconfig->secret = COPY(iter);
-	} else if (!strcmp(keyword, "radhost")) {
+	    break;
+	  case RADhost:
 	    if (radconfig->radhost) continue;
 	    radconfig->radhost = COPY(iter);
-	} else if (!strcmp(keyword, "radport")) {
+	    break;
+	  case RADport:
 	    if (radconfig->radport) continue;
 	    radconfig->radport = atoi(iter);
-	} else if (!strcmp(keyword, "lochost")) {
+	    break;
+	  case RADlochost:
 	    if (radconfig->lochost) continue;
 	    radconfig->lochost = COPY(iter);
-	} else if (!strcmp(keyword, "locport")) {
+	    break;
+	  case RADlocport:
 	    if (radconfig->locport) continue;
 	    radconfig->locport = atoi(iter);
-	} else if (!strcmp(keyword, "prefix")) {
+	    break;
+	  case RADprefix:
 	    if (radconfig->prefix) continue;
 	    radconfig->prefix = COPY(iter);
-	} else if (!strcmp(keyword, "suffix")) {
+	    break;
+	  case RADsuffix:
 	    if (radconfig->suffix) continue;
 	    radconfig->suffix = COPY(iter);
-	} else if (!strcmp(keyword, "ignore-source")) {
+	    break;
+	  case RADsource:
 	    if (!strcasecmp(iter, "true"))
 		radconfig->ignore_source = 1;
 	    else if (!strcasecmp(iter, "false"))
 		radconfig->ignore_source = 0;
 	    else {
-		fprintf(stderr, "Expected \"true\" or \"false\" after %s in rad_config, line %d\n",
-		  keyword, lineno);
+		fprintf(stderr, "Expected \"true\" or \"false\" after "
+			"ignore-source in rad_config, line %d\n", file->lineno);
 		exit(1);
 	    }
-	} else {
-	    fprintf(stderr, "unknown keyword %s in rad_config, line %d\n",
-	      keyword, lineno);
+	    break;
+	  default:
+	    fprintf(stderr, "unknown keyword in rad_config, line %d\n",
+		    file->lineno);
 	    exit(1);
+	  }
 	}
+      }
     }
+
+    CONFfclose(file);
+
+    if (!radconf->radhost) {
+	fprintf(stderr, "No radius host to authenticate against.\n");
+	exit(1);
+    } else if (!radconf->secret) {
+	fprintf(stderr, "No shared secret with radius host.\n");
+	exit(1);
+    }
+
     return(0);
 }
 
@@ -152,7 +236,31 @@
 #define PW_AUTHENTICATION_ACK 2
 #define PW_AUTHENTICATION_REJECT 3
 
-int rad_auth(rad_config_t *config, char *uname, char *pass)
+void req_copyto (auth_req to, sending_t *from)
+{
+  to.code = from->req.code;
+  to.id = from->req.id;
+  to.length = from->req.length;
+  strcpy(to.vector, from->req.vector);
+  strcpy(to.data, from->req.data);
+  to.datalen = from->req.datalen;
+
+  return;
+}
+
+void req_copyfrom (sending_t *to, auth_req from)
+{
+  to->req.code = from.code;
+  to->req.id = from.id;
+  to->req.length = from.length;
+  strcpy(to->req.vector, from.vector);
+  strcpy(to->req.data, from.data);
+  to->req.datalen = from.datalen;
+
+  return;
+}
+
+int rad_auth(rad_config_t *radconfig, char *uname, char *pass)
 {
     auth_req req;
     int i, j, jlen, passstart;
@@ -160,12 +268,11 @@
     HASH digest;
     struct timeval seed;
     MD5_CTX ctx;
-    struct sockaddr_in sinl, sinr;
+    struct sockaddr_in sinl;
     int sock;
     struct hostent *hent;
-    int done;
+    int done = 0;
     int ret;
-    int reqlen;
     int passlen;
     time_t now, end;
     struct timeval tmout;
@@ -174,16 +281,38 @@
     uint32_t nvalue;
     ARGTYPE slen;
     int authtries= 3; /* number of times to try reaching the radius server */
+    rad_config_t *config;
+    sending_t *reqtop, *sreq = NULL, *new;
+
+    /* set up the linked list */
+    config = radconfig;
 
-    /* first, build the sockaddrs */
-    memset(&sinl, '\0', sizeof(sinl));
-    memset(&sinr, '\0', sizeof(sinr));
-    sinl.sin_family = AF_INET;
-    sinr.sin_family = AF_INET;
-    if (config->lochost == NULL) {
-	if (gethostname(secbuf, sizeof(secbuf)) != 0) {
-	    fprintf(stderr, "radius: cant get localhostname\n");
-	    return(-1);
+    while (config != NULL){
+      new = malloc(sizeof(sending_t));
+      if (new == NULL){
+	fprintf(stderr, "radius: can't malloc sending_t struct\n");
+	return (-2);
+      }
+      new->next = NULL;
+
+      if (sreq == NULL){
+	reqtop = new;
+	sreq = new;
+      } else {
+	sreq->next = new;
+	sreq = sreq->next;
+      }
+      req_copyto(req, sreq);
+  
+      /* first, build the sockaddrs */
+      memset(&sinl, '\0', sizeof(sinl));
+      memset(&sreq->sinr, '\0', sizeof(sreq->sinr));
+      sinl.sin_family = AF_INET;
+      sreq->sinr.sin_family = AF_INET;
+      if (config->lochost == NULL) {
+  	if (gethostname(secbuf, sizeof(secbuf)) != 0) {
+  	    fprintf(stderr, "radius: cant get localhostname\n");
+	    return (-2);
 	}
 	config->lochost = COPY(secbuf);
     }
@@ -192,25 +321,26 @@
 	    if ((hent = gethostbyname(config->lochost)) == NULL) {
 		fprintf(stderr, "radius: cant gethostbyname lochost %s\n",
 		        config->lochost);
-		return(-1);
+		return (-2);
 	    }
 	    memcpy(&sinl.sin_addr.s_addr, hent->h_addr,
                    sizeof(struct in_addr));
 	}
     }
-    if (inet_aton(config->radhost, &sinr.sin_addr) != 1) {
+    if (inet_aton(config->radhost, &sreq->sinr.sin_addr) != 1) {
 	if ((hent = gethostbyname(config->radhost)) == NULL) {
 	    fprintf(stderr, "radius: cant gethostbyname radhost %s\n",
 	            config->radhost);
-	    return(-1);
+	    return (-2);
 	}
-	memcpy(&sinr.sin_addr.s_addr, hent->h_addr_list[0],
+	memcpy(&sreq->sinr.sin_addr.s_addr, hent->h_addr_list[0],
                sizeof(struct in_addr));
     }
+
     if (config->radport)
-	sinr.sin_port = htons(config->radport);
+	sreq->sinr.sin_port = htons(config->radport);
     else
-	sinr.sin_port = htons(PW_AUTH_UDP_PORT);
+	sreq->sinr.sin_port = htons(PW_AUTH_UDP_PORT);
 
     /* seed the random number generator for the auth vector */
     gettimeofday(&seed, 0);
@@ -259,7 +389,7 @@
     req.datalen += req.data[req.datalen+1];
 
     /* Add NAS_PORT and NAS_IP_ADDRESS into request */
-    if ((nvalue = config->locport) == 0)
+   if ((nvalue = config->locport) == 0)
         nvalue = RADIUS_LOCAL_PORT;
     req.data[req.datalen++] = RAD_NAS_PORT;
     req.data[req.datalen++] = sizeof(nvalue) + 2;
@@ -302,9 +432,16 @@
 	    MD5Final(&ctx);
 	    memcpy(digest.hash, ctx.digest, MD5_DIGESTSIZE);
 	}
+      }
+      sreq->reqlen = req.length;
+      req.length = htons(req.length);
+
+      req_copyfrom(sreq, req);
+
+      /* Go to the next record in the list */
+      config = config->next;
     }
-    reqlen = req.length;
-    req.length = htons(req.length);
+
     /* YAYY! The auth_req is ready to go! Build the reply socket and send out
      * the message. */
 
@@ -322,13 +459,20 @@
     }
 
     while (!done && authtries--) {
+      for (config = radconfig, sreq = reqtop; sreq != NULL && !done; 
+	   config = config->next, sreq = sreq->next){
+
+	req_copyto(req, sreq);
+
 	/* send out the packet and wait for reply. */
-	if (sendto(sock, (char *)&req, reqlen, 0, (struct sockaddr*) &sinr,
-	       sizeof(sinr)) < 0) {
-	    fprintf(stderr, "radius: cant send auth_req: %s\n", strerror(errno));
-	    close(sock);
-	    return(-1);
+	if (sendto(sock, (char *)&req, sreq->reqlen, 0, 
+		   (struct sockaddr*) &sreq->sinr, 
+		   sizeof (struct sockaddr_in)) < 0) {
+	  fprintf(stderr, "radius: cant send auth_req: %s\n", strerror(errno));
+	  close(sock);
+	  return(-1);
 	}
+
 	/* wait 5 seconds maximum for a radius reply. */
 	now = time(0);
 	end = now+5;
@@ -352,43 +496,51 @@
 	    continue;
 	}
 	slen = sizeof(sinl);
-	if ((jlen = recvfrom(sock, (char *)&req, sizeof(req)-sizeof(int), 0, 
-	                     (struct sockaddr*) &sinl, &slen)) < 0) {
+	if ((jlen = recvfrom(sock, (char *)&req, 
+			     sizeof(req)-sizeof(int), 0, 
+			     (struct sockaddr*) &sinl, &slen)) < 0) {
 	    fprintf(stderr, "radius: couldnt recvfrom: %s\n", strerror(errno));
 	    break;
 	}
+
 	if (!config->ignore_source) {
-	    if (sinl.sin_addr.s_addr != sinr.sin_addr.s_addr ||
-	      (sinl.sin_port != sinr.sin_port)) {
-		fprintf(stderr, "radius: received unexpected UDP packet from %s:%d.\n",
-		  inet_ntoa(sinl.sin_addr), ntohs(sinl.sin_port));
+	    if ((sinl.sin_addr.s_addr != sreq->sinr.sin_addr.s_addr) ||
+	      (sinl.sin_port != sreq->sinr.sin_port)) {
+	      fprintf(stderr, "radius: received unexpected UDP packet "
+			"from %s:%d.\n", inet_ntoa(sinl.sin_addr), 
+			ntohs(sinl.sin_port));
 		continue;
 	    }
 	}
-	reqlen = ntohs(req.length);
-	if (jlen < 4+AUTH_VECTOR_LEN || jlen != reqlen) {
+
+	sreq->reqlen = ntohs(req.length);
+	if (jlen < 4+AUTH_VECTOR_LEN || jlen != sreq->reqlen) {
 	    fprintf(stderr, "radius: received badly-sized packet.\n");
 	    continue;
 	}
 	/* verify the checksum */
-	memcpy(((char*)&req)+reqlen, config->secret, strlen(config->secret));
+	memcpy(((char*)&req)+sreq->reqlen, config->secret, strlen(config->secret));
 	memcpy(secbuf, req.vector, sizeof(req.vector));
 	memcpy(req.vector, secbuf+sizeof(req.vector), sizeof(req.vector));
 	MD5Init(&ctx);
-	MD5Update(&ctx, (char*)&req, strlen(config->secret)+reqlen);
-	MD5COUNT(&ctx, strlen(config->secret)+reqlen);
+	MD5Update(&ctx, (char*)&req, strlen(config->secret)+sreq->reqlen);
+	MD5COUNT(&ctx, strlen(config->secret)+sreq->reqlen);
 	MD5Final(&ctx);
 	if (memcmp(ctx.digest, secbuf, sizeof(HASH)) != 0) {
 	    fprintf(stderr, "radius: checksum didn't match.\n");
 	    continue;
 	}
+
 	/* FINALLY!  Got back a known-good packet.  See if we're in. */
 	ret = (req.code == PW_AUTHENTICATION_ACK) ? 0 : -1;
 	done = 1;
+
+	req_copyfrom(sreq, req);
+      }
     }
     if (!done) {
-	fprintf(stderr, "radius: couldn't talk to remote radius server %s:%d\n",
-	  inet_ntoa(sinr.sin_addr), ntohs(sinr.sin_port));
+      fprintf(stderr, "radius: couldn't talk to remote radius server %s:%d\n",
+	      inet_ntoa(sreq->sinr.sin_addr), ntohs(sreq->sinr.sin_port));
     }
     close(sock);
     return(ret);
@@ -409,7 +561,6 @@
     int havefile, haveother;
     char uname[SMBUF], pass[SMBUF];
     char buff[SMBUF];
-    FILE *f;
     rad_config_t radconfig;
     int retval;
     char *radius_config;
@@ -418,7 +569,7 @@
     haveother = havefile = 0;
     if (ReadInnConf() < 0) exit(1);
 
-    while ((opt = getopt(argc, argv, "f:h:p:P:q:s:l:S")) != -1) {
+    while ((opt = getopt(argc, argv, "f:h")) != -1) {
 	switch (opt) {
 	  case 'f':
 	    if (haveother) {
@@ -427,100 +578,27 @@
 		fprintf(stderr, "-f flag after another, non -f flag.\n");
 		exit(1);
 	    }
-	    if (!havefile) {
-		/* override the standard config completely if the user
-		 * specifies an alternate config file */
-		bzero(&radconfig, sizeof(rad_config_t));
-		havefile = 1;
-	    }
-	    f = fopen(optarg, "r");
-	    if (!f) {
-		fprintf(stderr, "couldn't open config file %s: %s\n", optarg, strerror(errno));
-		exit(1);
-	    }
-	    read_config(f, &radconfig);
-	    fclose(f);
-	    break;
+  	    if (!havefile) {
+  		/* override the standard config completely if the user
+  		 * specifies an alternate config file */
+	        bzero(&radconfig, sizeof(rad_config_t));
+  		havefile = 1;
+  	    }
+	    read_config(optarg, &radconfig);
+  	    break;
+	case 'h':
+	  printf("Usage: radius [-f config]\n");
+	  return 0;
+	  break;
+  	}
+      }
+      if (argc != optind)
+  	exit(2);
+      if (!havefile) {
+  	radius_config = cpcatpath(innconf->pathetc, _PATH_RADIUS_CONFIG);
+	read_config(radius_config, &radconfig);
 
-	  case 'h':
-	    if (haveother & RAD_HAVE_HOST) {
-		fprintf(stderr, "two -h options.\n");
-		exit(1);
-	    }
-	    haveother |= RAD_HAVE_HOST;
-	    if (radconfig.radhost)
-		DISPOSE(radconfig.radhost);
-	    radconfig.radhost = optarg;
-	    break;
-	  case 'p':
-	    if (haveother & RAD_HAVE_PORT) {
-		fprintf(stderr, "two -p options.\n");
-		exit(1);
-	    }
-	    haveother |= RAD_HAVE_PORT;
-	    radconfig.radport = atoi(optarg);
-	    break;
-	  case 'P':
-	    if (haveother & RAD_HAVE_LOCPORT) {
-		fprintf(stderr, "two -P options.\n");
-		exit(1);
-	    }
-	    haveother |= RAD_HAVE_LOCPORT;
-	    radconfig.locport = atoi(optarg);
-	    break;
-	  case 'q':
-	    if (haveother & RAD_HAVE_PREFIX) {
-		fprintf(stderr, "two -q options.\n");
-		exit(1);
-	    }
-	    haveother |= RAD_HAVE_PREFIX;
-	    if (radconfig.prefix)
-		DISPOSE(radconfig.prefix);
-	    radconfig.prefix = optarg;
-	    break;
-	  case 's':
-	    if (haveother & RAD_HAVE_SUFFIX) {
-		fprintf(stderr, "two -s options.\n");
-		exit(1);
-	    }
-	    haveother |= RAD_HAVE_SUFFIX;
-	    if (radconfig.suffix)
-		DISPOSE(radconfig.suffix);
-	    radconfig.suffix = optarg;
-	    break;
-	  case 'l':
-	    if (haveother & RAD_HAVE_LOCHOST) {
-		fprintf(stderr, "two -l options.\n");
-		exit(1);
-	    }
-	    haveother |= RAD_HAVE_LOCHOST;
-	    if (radconfig.lochost)
-		DISPOSE(radconfig.lochost);
-	    radconfig.lochost = optarg;
-	    break;
-	  case 'S':
-	    radconfig.ignore_source = 1;
-	    break;
-	}
-    }
-    if (argc != optind)
-	exit(2);
-    if (!havefile) {
-	radius_config = cpcatpath(innconf->pathetc, _PATH_RADIUS_CONFIG);
-	if (!(f = fopen(radius_config, "r"))) {
-	    fprintf(stderr, "couldn't open config file %s: %s\n", radius_config,
-                    strerror(errno));
-	} else {
-            read_config(f, &radconfig);
-            fclose(f);
-        }
-    }
-    if (!radconfig.radhost) {
-	fprintf(stderr, "No radius host to authenticate against.\n");
-	exit(1);
-    } else if (!radconfig.secret) {
-	fprintf(stderr, "No shared secret with radius host.\n");
-	exit(1);
+  	DISPOSE(radius_config);
     }
 
     uname[0] = '\0';
@@ -544,6 +622,7 @@
 	exit(3);
 
     /* got username and password, check that they're valid */
+
     retval = rad_auth(&radconfig, uname, pass);
     if (retval == -1) {
 	fprintf(stderr, "radius: user %s password doesn't match.\n", uname);



More information about the inn-workers mailing list