[tac_plus] finally got around to upgrading tac_plus from an older release
Nathan Neulinger
nneul at umr.edu
Sun Apr 15 01:19:14 UTC 2007
Figured I'd send you this local patch I've been maintaining for a while.
It basically adds support for:
1. External authentication process, passes user= and passwd=
similar to authorization scripts.
2. Adds support for a "msg=" AV pair to set the message that is
sent to the NAS.
3. Allows defining a global default login secret/pw.
-- Nathan
------------------------------------------------------------
Nathan Neulinger EMail: nneul at umr.edu
University of Missouri - Rolla Phone: (573) 341-6679
UMR Information Technology Fax: (573) 341-4216
-------------- next part --------------
diff -ur tacacs+-F4.0.4.14-orig/config.c tacacs+-F4.0.4.14/config.c
--- tacacs+-F4.0.4.14-orig/config.c 2006-12-13 10:42:56.000000000 -0600
+++ tacacs+-F4.0.4.14/config.c 2007-04-14 19:02:56.000000000 -0500
@@ -66,6 +66,7 @@
cleartext <password> |
des <password> |
PAM |
+ external <filename> |
nopassword
<user_attr> := name = <string> |
@@ -1090,6 +1091,7 @@
case S_file:
case S_cleartext:
case S_des:
+ case S_external:
sprintf(buf, "%s ", sym_buf);
sym_get();
strcat(buf, sym_buf);
@@ -1104,7 +1106,7 @@
#ifdef HAVE_PAM
"'PAM', "
#endif
- "or 'des' keyword after 'login =' on line %d",
+ "'external', or 'des' keyword after 'login =' on line %d",
sym_line);
}
sym_get();
diff -ur tacacs+-F4.0.4.14-orig/do_author.c tacacs+-F4.0.4.14/do_author.c
--- tacacs+-F4.0.4.14-orig/do_author.c 2006-12-13 10:42:56.000000000 -0600
+++ tacacs+-F4.0.4.14/do_author.c 2007-04-14 18:58:57.000000000 -0500
@@ -178,7 +178,8 @@
{
int status;
char **out_args;
- int out_cnt, i;
+ char *value;
+ int out_cnt, i, j;
char *cmd;
char error_str[255];
int error_len = 255;
@@ -201,6 +202,24 @@
status = call_pre_process(cmd, data, &out_args, &out_cnt, error_str,
error_len);
+ /* Copy and remove msg= from returned output pairs */
+ for(i=0; i < out_cnt; i++) {
+ value = tac_find_substring("msg=", out_args[i]);
+ if ( value )
+ {
+ if ( data->msg ) { free(data->msg); }
+ data->msg=tac_strdup(value);
+ report(LOG_DEBUG, "Message returned from cmd: %s", value);
+ free(out_args[i]); /* just added */
+ for ( j=i; j<out_cnt-1; j++ ) {
+ out_args[j]=out_args[j+1];
+ out_args[j+1]=NULL;
+ }
+ out_cnt--;
+ }
+ }
+
+
switch (status) {
default:
if (debug & DEBUG_AUTHOR_FLAG)
@@ -297,7 +316,8 @@
post_authorization(char *username, struct author_data *data)
{
char **out_args;
- int out_cnt, i;
+ char *value;
+ int out_cnt, i, j;
int status;
char *after = cfg_get_pvalue(username, TAC_IS_USER,
S_after, TAC_PLUS_RECURSE);
@@ -309,6 +329,23 @@
status = call_post_process(after, data, &out_args, &out_cnt);
+ /* Next set and remove msg= from outgoing pairs */
+ for(i=0; i < out_cnt; i++) {
+ value = tac_find_substring("msg=", out_args[i]);
+ if ( value )
+ {
+ if ( data->msg ) { free(data->msg); }
+ data->msg=tac_strdup(value);
+ report(LOG_DEBUG, "Message returned from cmd: %s", value);
+ free(out_args[i]); /* just added */
+ for ( j=i; j<out_cnt-1; j++ ) {
+ out_args[j]=out_args[j+1];
+ out_args[j+1]=NULL;
+ }
+ out_cnt--;
+ }
+ }
+
if (status != 2) {
/* throw away out_args */
for (i = 0; i < out_cnt; i++) {
diff -ur tacacs+-F4.0.4.14-orig/parse.c tacacs+-F4.0.4.14/parse.c
--- tacacs+-F4.0.4.14-orig/parse.c 2006-12-13 10:42:57.000000000 -0600
+++ tacacs+-F4.0.4.14/parse.c 2007-04-14 18:58:57.000000000 -0500
@@ -85,6 +85,7 @@
#endif
declare("exec", S_exec);
declare("expires", S_expires);
+ declare("external", S_external);
declare("file", S_file);
declare("group", S_group);
declare("global", S_global);
@@ -210,6 +211,8 @@
return("nopassword");
case S_des:
return("des");
+ case S_external:
+ return("external");
case S_svc:
return("service");
case S_default:
diff -ur tacacs+-F4.0.4.14-orig/parse.h tacacs+-F4.0.4.14/parse.h
--- tacacs+-F4.0.4.14-orig/parse.h 2006-07-05 14:00:13.000000000 -0500
+++ tacacs+-F4.0.4.14/parse.h 2007-04-14 19:03:30.000000000 -0500
@@ -86,3 +86,4 @@
#ifdef HAVE_PAM
# define S_pam 49
#endif
+#define S_external 50
diff -ur tacacs+-F4.0.4.14-orig/programs.c tacacs+-F4.0.4.14/programs.c
--- tacacs+-F4.0.4.14-orig/programs.c 2006-12-13 10:42:57.000000000 -0600
+++ tacacs+-F4.0.4.14/programs.c 2007-04-14 19:59:47.000000000 -0500
@@ -486,3 +486,61 @@
return(status);
}
+
+
+/* Execute an external command, pass AV pairs to it, and read av pairs
+from it. Return exit status when command terminates. */
+
+int call_external_auth_process(char *cmd, char **inargsp, int inargs_cnt, char ***outargsp,
+ int *outargs_cntp)
+{
+ char **new_args;
+ int readfd, writefd, errorfd;
+ int status, i;
+ int pid = my_popen(cmd, &readfd, &writefd, &errorfd);
+ char error[1500];
+
+ if (pid < 0) {
+ close_fds(readfd, writefd, errorfd);
+ return(1); /* deny */
+ }
+
+ for (i = 0; i < inargs_cnt; i++) {
+ if (debug & DEBUG_AUTHEN_FLAG)
+ report(LOG_DEBUG, "input %s", inargsp[i]);
+ }
+
+ if (write_args(writefd, inargsp, inargs_cnt)) {
+ close_fds(readfd, writefd, errorfd);
+ return (1); /* failed writing to it */
+ }
+
+ close(writefd);
+ writefd = -1;
+
+ new_args = read_args(0, readfd);
+ *outargsp = new_args;
+
+ if (debug & DEBUG_AUTHEN_FLAG) {
+ for (i = 0; new_args[i]; i++) {
+ report(LOG_DEBUG, "output %s", new_args[i]);
+ }
+ }
+
+ error[0] = '\0';
+ read_string(errorfd, error, 1500);
+ if (error[0] != '\0') {
+ report(LOG_ERR, "Error from program (%d): \"%s\" ",
+ strlen(error), error);
+ }
+
+ /* count the args */
+ for (i = 0; new_args[i]; i++)
+ /* NULL stmt */ ;
+
+ *outargs_cntp = i;
+
+ status = waitfor(pid);
+ return (status);
+}
+
diff -ur tacacs+-F4.0.4.14-orig/pwlib.c tacacs+-F4.0.4.14/pwlib.c
--- tacacs+-F4.0.4.14-orig/pwlib.c 2006-12-13 10:42:57.000000000 -0600
+++ tacacs+-F4.0.4.14/pwlib.c 2007-04-14 19:07:05.000000000 -0500
@@ -130,6 +130,23 @@
cfg_passwd = cfg_get_global_secret(name, recurse);
}
+ if ( !cfg_user_exists(name) )
+ {
+ /* If there is no global password for the user, try seeing if there is
+ a password for the default userid */
+
+ if ( !cfg_passwd) {
+ cfg_passwd = cfg_get_login_secret(DEFAULT_USERNAME, recurse);
+ }
+
+ /* If there is no password for the default userid, try seeing if there
+ is a global password for the default userid */
+
+ if ( !cfg_passwd) {
+ cfg_passwd = cfg_get_global_secret(DEFAULT_USERNAME, recurse);
+ }
+ }
+
/* If we still have no password for this user (or no user for that
* matter) but the default authentication = file <file> statement
* has been issued, attempt to use this password file */
@@ -199,6 +216,21 @@
return(data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
}
+ p = tac_find_substring("external ", cfg_passwd);
+ if (p) {
+ /* try to verify this external password */
+ if (!external_verify_password(name,passwd,data,p)) {
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return (0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ }
+
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ }
+
p = tac_find_substring("file ", cfg_passwd);
if (p) {
return(passwd_file_verify(name, passwd, data, p));
@@ -615,3 +647,83 @@
return(0);
}
#endif
+
+/*
+ * verify a provided password using an external routine
+ * external routine returns 0 if correct, 1 if incorrect
+ * routine interface similar to that of before/after authorization
+ * subroutine returns 1 if verified, 0 otherwise.
+ */
+
+int
+external_verify_password(char *user, char *passwd, struct authen_data *data, char *cmd)
+{
+ int status;
+ char *value;
+ char **out_args;
+ char *in_args[2];
+ int out_cnt, i, j;
+
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify %s for %s using %s",
+ passwd, user, cmd);
+
+ if (passwd == NULL ||
+ *passwd == '\0' ||
+ user == NULL ||
+ *user == '\0' ||
+ cmd == NULL ||
+ *cmd == '\0') {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify returns 0 - something was NULL");
+ return (0);
+ }
+
+ /* Allocate memory for 'user=USERID' and 'passwd=PASSWD' */
+ in_args[0] = (char *) malloc( strlen(user)+strlen("user=")+1 );
+ in_args[1] = (char *) malloc( strlen(passwd)+strlen("passwd=")+1 );
+ sprintf(in_args[0], "user=%s", user);
+ sprintf(in_args[1], "passwd=%s", passwd);
+
+ status = call_external_auth_process(cmd, &in_args, 2,
+ &out_args, &out_cnt);
+
+ free(in_args[0]);
+ free(in_args[1]);
+
+ /* throw away out_args, but keep message */
+ for(i=0; i < out_cnt; i++) {
+ value = tac_find_substring("msg=", out_args[i]);
+ if ( value )
+ {
+ if ( data->server_msg ) { free(data->server_msg); }
+ data->server_msg=tac_strdup(value);
+ }
+ free(out_args[i]);
+ }
+ free(out_args);
+
+ switch (status) {
+ default:
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "cmd %s returns %d (unrecognised value)",
+ cmd, status);
+ return(0);
+
+ case 0: /* Permit - Password Correct*/
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "cmd %s returns 0 (passwd correct)", cmd);
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ return(1);
+
+ case 1: /* Deny - Password Incorrect*/
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "cmd %s returns 1 (passwd incorrect)", cmd);
+ return(0);
+ }
+
+ return (0);
+}
+
diff -ur tacacs+-F4.0.4.14-orig/users_guide tacacs+-F4.0.4.14/users_guide
--- tacacs+-F4.0.4.14-orig/users_guide 2006-11-24 21:43:29.000000000 -0600
+++ tacacs+-F4.0.4.14/users_guide 2007-04-14 20:07:40.000000000 -0500
@@ -373,7 +373,7 @@
daemon to the NAS. This is a security issue if the TACACS+ key is ever
compromised.
-There are 4 ways to authenticate a user for login.
+There are 5 ways to authenticate a user for login.
1). You can include a DES (or cleartext) password for a user or for a
group that s/he is a member of, viz:
@@ -445,6 +445,31 @@
login = PAM
}
+5). Authentication using external routines.
+
+You can have authentication performed by an external routine - such as to
+do a lookup in a database, etc. The userid and password are passed to the
+routine using argument/value pairs similar to the before and after
+authorization routines. For example, if the userid were "joe" and the
+password given were "abc123", the process would receive:
+
+user=joe
+passwd=abc123
+
+To enable external authentication, use as follows:
+
+ user = fred {
+ login = external /path/to/external/authenticator
+ }
+
+The external routine should return and exit success code of 0 if the
+userid and password are correct, 1 otherwise. The routine can also,
+optionally, return a "msg=some string" to change the message that is
+displayed to the user. This message will be displayed regardless of
+whether the authentication is successful or not.
+
+This method of authentication is particularly useful with the DEFAULT
+user.
RECURSIVE PASSWORD LOOKUPS
---------------------------
@@ -1063,6 +1088,12 @@
when specifying AV pairs, there should be no spaces surrounding the
"=" sign when using the programmatic interface.
+NOTE: Regardless of the return code, both the before and after
+authorization routines will detect a "msg=" AV pair, and if present
+will display the given message on the NAS. This can be used to indicate
+to the user why the authorization failed, or just to give positive
+feedback, or to warn to be careful for privileged commands.
+
CALLING SCRIPTS BEFORE AUTHORIZATION
------------------------------------
More information about the tac_plus
mailing list