[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