This patch adds RADIUS authentication support, allowing the
TACACS+ server to authenticate against a RADIUS server. It is
derived from an original "TACACS to RADIUS" patch from Martin
Mersberger <gremlin@portal-to-web.de> which can be found under
http://www.portal-to-web.de/tacacs/. It was cleaned up and ported to the
latest TACACS+ 4.4b1 version by Ralf S. Engelschal <rse@engelschall.com>
for inclusion into the OpenPKG "tacacs" package.

Index: config.c
--- config.c.orig	2003-04-08 03:37:02.000000000 +0200
+++ config.c	2003-10-24 17:01:35.000000000 +0200
@@ -77,6 +77,7 @@
 		            pam <pam_service> |   *** if USE_PAM defined
 					db <string>		  |   *** if USE_DB defined
 					ldap <string>	  |   *** if USE_LDAP defined
+					radius <string>	  |   *** if USE_RADIUS defined
 		            nopassword
 					
 		*<login_spec> for host and default only allow external lists 
@@ -89,6 +90,7 @@
 		            pam <pam_service> |      *** if USE_PAM defined
 				    db <string>		  |      *** if USE_DB defined
 				    ldap <string>	  |      *** if USE_LDAP defined
+				    radius <string>	  |      *** if USE_RADIUS defined
 				    login                    *** use the same method as the login
 					 
    *** Added acct_spec 6/12/02 JRM 	 
@@ -964,6 +966,9 @@
 #ifdef USE_PAM
 				case S_pam:
 #endif
+#ifdef USE_RADIUS
+				case S_radius:
+#endif
 					sym_get(0);
 					authen_default = tac_strdup(sym_buf);
 					break;
@@ -1431,6 +1436,9 @@
 #ifdef USE_PAM	
 			case S_pam:	
 #endif /* USE_PAM */
+#ifdef USE_RADIUS	
+			case S_radius:
+#endif
 				sym_get(0);
 				host->login = tac_strdup(sym_buf);
 				break;
@@ -1494,6 +1502,9 @@
 #ifdef USE_PAM	
 			case S_pam:	
 #endif /* USE_PAM */
+#ifdef USE_RADIUS
+			case S_radius:
+#endif
 				sym_get(0);
 				host->enable = tac_strdup(sym_buf); 
 				break;
@@ -1743,6 +1754,9 @@
 #ifdef USE_PAM	
 			case S_pam:	
 #endif /* USE_PAM */
+#ifdef USE_RADIUS
+			case S_radius:
+#endif
 				sym_get(0);
 				user->login = tac_strdup(sym_buf);
 				break;
@@ -1830,6 +1844,9 @@
 #ifdef USE_PAM	
 			case S_pam:	
 #endif /* USE_PAM */ 
+#ifdef USE_RADIUS
+			case S_radius:
+#endif
 				sym_get(0);
 				user->enable = tac_strdup(sym_buf);
 				break;
Index: parse.c
--- parse.c.orig	2003-03-03 15:30:26.000000000 +0100
+++ parse.c	2003-10-24 17:03:51.000000000 +0200
@@ -101,6 +101,9 @@
 #ifdef USE_LDAP
     declare ("ldap", S_ldap);
 #endif
+#ifdef USE_RADIUS
+    declare("radius", S_radius);
+#endif
     declare("member", S_member);
     declare("message", S_message);
     declare("name", S_name);
@@ -301,5 +304,9 @@
 		return("enable_deny");
 	case S_unix:
 		return("unix");
+#ifdef USE_RADIUS
+	case S_radius:
+		return ("radius");
+#endif /*USE_PAM */	
 	}
 }
Index: parse.h
--- parse.h.orig	2003-03-03 15:28:07.000000000 +0100
+++ parse.h	2003-10-24 17:02:26.000000000 +0200
@@ -108,3 +108,6 @@
 #define S_unix					62
 #define S_motd					63
 #define S_accesslog				64
+#ifdef USE_RADIUS
+#define S_radius          65
+#endif
Index: pwlib.c
--- pwlib.c.orig	2003-04-01 00:13:10.000000000 +0200
+++ pwlib.c	2003-10-24 17:06:25.000000000 +0200
@@ -37,6 +37,10 @@
 #include "ldap.h"
 #endif /* LDAP */
 
+#ifdef USE_RADIUS
+#include "radius.h"
+#endif
+
 /* Generic password verification routines for des, file and cleartext
    passwords */
 
@@ -47,7 +51,6 @@
 static int
 unix_verify(char *user, char *supplied_passwd, struct authen_data *data);
 
-
 void
 set_expiration_status(exp_date, data)
 char *exp_date;
@@ -301,6 +304,17 @@
 
 #endif /* USE_PAM */
 
+#ifdef USE_RADIUS
+	case S_radius:
+        if (radius_verify(name, passwd, cfg_login) == 1) {
+            data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+        } else {
+            data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+			exp_date = NULL; /* no expire check for RADIUS */
+        }
+        break;
+#endif
+
 	case S_des:
 		/* try to verify this des password */
 		if (!des_verify(passwd, cfg_login)) {
Index: radius.h
--- radius.h.orig	2003-10-24 16:58:03.000000000 +0200
+++ radius.h	2003-10-24 16:58:03.000000000 +0200
@@ -0,0 +1,6 @@
+#ifndef __RADIUS_H__
+#define __RADIUS_H__
+
+extern radius_verify(char *, char *, char *);
+
+#endif /* __RADIUS_H__ */
Index: Makefile.in
--- Makefile.in.orig	2003-04-11 04:30:25.000000000 +0200
+++ Makefile.in	2003-10-24 17:16:45.000000000 +0200
@@ -158,7 +158,7 @@
 # $(use_o) has to be BEFORE $(conf_LDADD)!  (for library dependencies)
 tac_plus_LDADD = $(use_o) $(conf_LDADD)
 tac_plus_DEPENDENCIES = $(use_o)
-use = @COND_USE@
+use = @COND_USE@ radius.c
 use_o = $(filter %.o,$(use:.c=.o))
 
 cond_USE_DB = db.c		db.h	db_author.c
@@ -166,6 +166,7 @@
 cond_DB_NULL = db_null.c
 cond_DB_PGSQL = db_pgsql.c
 cond_USE_LDAP = ldap.c	
+cond_USE_RADIUS = radius.c	
 cond_MAXSESS = maxsess.c
 cond_MSCHAP = md4.c		md4.h
 cond_SKEY = skey_fn.c
@@ -181,6 +182,7 @@
 	$(cond_DB_NULL)		\
 	$(cond_DB_PGSQL)	\
 	$(cond_USE_LDAP)	\
+	$(cond_USE_RADIUS)	\
 	$(cond_MAXSESS)		\
 	$(cond_MSCHAP)		\
 	$(cond_SKEY)		\
Index: radius.c
--- radius.c.orig	2003-10-24 16:58:03.000000000 +0200
+++ radius.c	2003-10-24 17:19:49.000000000 +0200
@@ -0,0 +1,117 @@
+/*
+ *   Verify that this user/password is valid per a RADIUS server database
+ *   Return 1 if verified, 0 otherwise.
+ *
+ *   Format of connection string:
+ *       <radius key server1>,<radius server1>,<radius key server2>,<radius server2>,
+ *       ... ,<radius key server9>,<radius server9>
+ *
+ *   Author:
+ *       Martin Mersberger <gremlin@portal-to-web.de>
+ *       http://www.portal-to-web.de/tacacs
+ *
+ *   Dependencies:
+ *       You need to get the Juniper Networks libradius
+ *       (included in FreeBSD >= 4.x)
+ *
+ *   License:
+ *       tac_radius is free software; you can redistribute it
+ *       and/or modify it under the terms of the BSD License
+ */
+
+#include "config.h"
+
+#if defined(USE_RADIUS)
+
+#include <stdio.h>
+#include <string.h>
+
+#include "tac_plus.h"
+#include "radius.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "radlib.h"
+
+int radius_verify(char *user, char *users_passwd, char *str_conn)
+{
+    struct rad_handle *rh;
+    struct in_addr addr;
+    int res;
+    char *token, *cp;
+    char *server[10];
+    char *key[10];
+    int i, j;
+    char l_err[200];
+    int err;
+
+    /* open the radius handle */
+    if ((rh = rad_auth_open()) == NULL ) {
+        report(LOG_ERR,"Can't open rad_open");
+        return 1;
+    }
+
+    /* split the line from the config file into pairs with radiusserver and radius key */
+    cp = strdup(str_conn);
+    i = 0;
+    while ((token = strsep(&cp,",")) != NULL) {
+        if ((i % 2) == 0) {  /* 0,2,4,... are the radius keys    */
+            key[i] = (char *)malloc(sizeof(token) + sizeof(char)); /* get some mem  */
+            sprintf(key[i], "%s\0", token);  /* add the value gotten into a array */
+        } else {
+            server[i-1] = (char *)malloc(sizeof(token) + sizeof(char)); /* 1,3,5... are the radius hosts */
+            sprintf(server[i-1], "%s\0", token); /* malloc and add to array */
+        }
+        i++;
+    }
+
+    /* for each server and key pair gotten from the config file, do a rad_add_server */
+    for (j = 0; j < i; j = j + 2) {
+        report(LOG_INFO, "verify_radius: before ldap_init: radiusserver = %s, radiuskey = %s", server[j],key[j]);
+        if ((rad_add_server(rh,server[j], 0, key[j], 2, 2)) != 0) {
+            report (LOG_ERR, "Error in rad_add_server for %s", server[j]);
+            return (1);
+        }
+    }
+
+    /* create a radius request for ACCESS */
+    if ((rad_create_request(rh ,RAD_ACCESS_REQUEST)) != 0) {
+        report (LOG_ERR, "Error in rad_create_request");
+        return (1);
+    }
+
+    /* prepare the radius request
+     * 1. make a in_addr from the requesting peer ip address
+     * 2. put in username
+     * 3. put in password
+     * 4. insert the in_addr into the rad_request
+     * 5. set the RAD_CONNECT_INFO to "via tacacs+"
+     */
+    inet_aton(session.peer, &addr);
+    rad_put_string(rh, RAD_USER_NAME, user);
+    rad_put_string(rh, RAD_USER_PASSWORD, users_passwd);
+    rad_put_addr(rh, RAD_NAS_IP_ADDRESS,addr);
+    rad_put_string(rh, RAD_CONNECT_INFO," via TACACS+ server");
+
+    /* some debug messages before sending the radius request */
+#if 0
+    report(LOG_INFO, "verify_radius: before rad_send: user = %s, passwd = %s", user, "********");
+    report(LOG_INFO, "verify_radius: before rad_send: peer %s", session.peer);
+#endif
+
+    /* send the radius request and hope, that libradius does a good job */
+    res = rad_send_request(rh);
+
+    /* is the user authenticated? if yes, return 0, else 1 */
+    if (res == RAD_ACCESS_ACCEPT ) {
+        report(LOG_INFO, "Request accepted\n");
+        return 0;
+    } else {
+        report(LOG_INFO, "Request denied %i\n",res);
+        return 1;
+    }
+}
+
+#endif /* RADIUS */
