Then I added account checking into the authentication so pam based account restrictions such as pam_tally work (I wanted account lockout on 3 authentication failures).
I also incorporated an update in config.c that I think will make configuration
less confusing:
When compiled with pam support
default authentication = file name_of_passwordfilebecomes invalid and
default authentication = pam name_of_serviceis the expected syntax. This requires the configuration file to reflect the underlying behaviour introduced in patch version 0.2.
Lastly I changed some default file paths in Makefile and several
things in tac_plus.init in an attempt
to conform to newer unix and linux conventions.
As with CISCO's software these patches are provided ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Cisco - Pam adds great flexibility to tacacs+ so I hope you will consider merging this patch into your source code. If not perhaps someone out there (Max?) will find it a home so it may still be useful to others.
Red Hat - How about including a nice tacacs+ rpm WITH pam support in powertools?
-Chris
diff -Naur tac_plus.F4.0.4.alpha/Makefile tac_plus.F4.0.4.alpha-PAM-0.4/Makefile
--- tac_plus.F4.0.4.alpha/Makefile Sun Jun 18 13:26:54 2000
+++ tac_plus.F4.0.4.alpha-PAM-0.4/Makefile Fri Feb 2 17:27:15 2001
@@ -33,8 +33,8 @@
# OS=-DMIPS
# For Solaris (SUNOS 5.3, 5.4, 5.5, 5.6) uncomment the following two lines
-OS=-DSOLARIS
-OSLIBS=-lsocket -lnsl
+#OS=-DSOLARIS
+#OSLIBS=-lsocket -lnsl
# For FreeBSD
# OS=-DFREEBSD
@@ -49,9 +49,13 @@
#
# On REDHAT 5.0 systems, or systems that use the new glibc,
# you might instead need the following:
-# OS=-DLINUX -DGLIBC
-# OSLIBS=-lcrypt
+OS=-DLINUX -DGLIBC
+OSLIBS=-lcrypt
+
+#For PAM
+DEFINES = -DUSE_PAM
+OSLIBS+= -lpam -ldl -lc
# Athough invoked as root, most of the time you don't want tac_plus to
# be running as root. If USERID and GROUPID are set, tac_plus will
@@ -69,7 +73,7 @@
# INCLUDES = -I../crimelab/skey/src
# Debugging flags
-DEBUG = -g
+DEBUG = -O3
# Enforce a limit on maximum sessions per user. See the user's guide
# for more information.
@@ -85,7 +89,7 @@
# possible), containing its process id. Uncomment and modify the
# following line to change this filename
-# PIDFILE = -DTAC_PLUS_PIDFILE=\"/var/run/tac_plus.pid\"
+PIDFILE = -DTAC_PLUS_PIDFILE=\"/var/run/tac_plus.pid\"
#
# End of customisable section of Makefile
@@ -99,7 +103,7 @@
do_author.c dump.c encrypt.c expire.c $(MSCHAP_MD4_SRC) md5.c \
packet.c report.c sendauth.c tac_plus.c utils.c pw.c hash.c \
parse.c regexp.c programs.c enable.c pwlib.c default_fn.c \
- skey_fn.c default_v0_fn.c sendpass.c maxsess.c
+ skey_fn.c default_v0_fn.c sendpass.c maxsess.c tac_pam_auth.c
OBJS = $(SRCS:.c=.o)
@@ -130,8 +134,11 @@
-rm -f *.o *~ *.BAK tac_plus generate_passwd
install:
- cp tac_plus /usr/local/bin
- cp tac_plus.1 /usr/man/manl/tac_plus.1
+ cp tac_plus /usr/local/sbin
+ cp tac_plus.1 /usr/share/man/man1/tac_plus.1
+ @echo "You may also wish to:"
+ @echo " copy start script 'tac_plus.init' to /etc/init.d/tacacs and edit"
+ @echo " copy sample configuration 'tac_plus.conf' to /etc/tacacs/tac_plus.cfg and edit"
depend:
makedepend $(CFLAGS) $(SRCS)
diff -Naur tac_plus.F4.0.4.alpha/README.PAM tac_plus.F4.0.4.alpha-PAM-0.4/README.PAM
--- tac_plus.F4.0.4.alpha/README.PAM Wed Dec 31 19:00:00 1969
+++ tac_plus.F4.0.4.alpha-PAM-0.4/README.PAM Fri Feb 2 17:19:20 2001
@@ -0,0 +1,46 @@
+Tacacs+ with PAM Authentication support.
+
+patch version 0.4 (2 February 2001) by Chris Johnson <cjohnson@mint.net>
+Now calls pam_acct_mgmt so the 'account' configuration in pam can restrict access
+depending on your pam configuration. I added this to make pam_tally work for account
+lockout on n authentication failures. But others may wish to apply time-of-day, account
+expiration, etc. (Pam is great!)
+
+Also took default authentication a step further:
+ If -DUSE_PAM then it must be of the form "default authentication = pam name_of_service"
+ Else it must be of the form "default authentication = file name_of_passwordfile"
+This only affects configuration syntax - requiring the configuration to reflect the
+underlying behaviour.
+
+patch version 0.3 (17 November 1999)
+tac_pam_auth is MT-safe now.
+Tacacs reports pam error strings.
+More examples into the tac_plus.conf. Corrected the version number at start-up.
+
+patch version 0.2 (31 August 1999)
+Pammified default authentication. If -DUSE_PAM used, I hope that PAM is the default
+system authentication method. The PAM service used is the name of the passwd_file (???).
+Yes, it's terrible, but I have not time to spend for changing
+default authentication = file /etc/passwd
+ in
+default authentication = pam name_of_service..........any volunteer ???
+As usual, look at the tac_plus.conf file.
+
+patch version 0.1 (August 1999)
+I introduced an optional new authentication method via PAM. This makes authentication
+modular and external to the tacacs+ code.
+I have not implemented pam-authorization because the existence of pre and post authorization
+calls.
+In order to pass tacacs+ attributes to the pam modules, I use the PAM_RUSER item
+for storing the rem_addr. Have a look to tac_pam_auth.c for the details.
+As you can see, pam-authentication only works for pap and login authentication-types:
+I would like to extend it to the default authentication case, but the code seems so
+cryptic:-(.......
+You can choose the pam service by the configuration file, for a simple example look
+into tac_plus.conf.
+
+
+
+Max Liccardo <ravel@tiscalinet.it>
+
+
diff -Naur tac_plus.F4.0.4.alpha/config.c tac_plus.F4.0.4.alpha-PAM-0.4/config.c
--- tac_plus.F4.0.4.alpha/config.c Sun Jun 18 13:26:53 2000
+++ tac_plus.F4.0.4.alpha-PAM-0.4/config.c Fri Feb 2 18:09:06 2001
@@ -49,8 +49,11 @@
<password_spec> := file <filename> |
skey |
- cleartext <password> |
+ cleartext <password> |
des <password> |
+#ifdef USE_PAM
+ pam <pam_service> |
+#endif
nopassword
<user_attr> := name = <string> |
@@ -64,6 +67,9 @@
#endif
pap = cleartext <string> |
pap = des <string> |
+#ifdef USE_PAM
+ pap = pam <pam_service> |
+#endif
opap = cleartext <string> |
global = cleartext <string> |
msg = <string>
@@ -545,9 +551,32 @@
}
parse(S_authentication);
parse(S_separator);
- parse(S_file);
- authen_default = tac_strdup(sym_buf);
- sym_get();
+ switch (sym_code) {
+ default:
+ parse_error(
+#ifdef USE_PAM
+ "Expecting default authentication pam on line %d but found %s",
+ sym_line, sym_buf);
+#else
+ "Expecting default authentication file on line %d but found %s",
+ sym_line, sym_buf);
+#endif
+ return (1);
+
+#ifdef USE_PAM
+ case S_pam:
+ parse(S_pam);
+ authen_default = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+#else
+ case S_file:
+ parse(S_file);
+ authen_default = tac_strdup(sym_buf);
+ sym_get();
+ continue;
+#endif
+ }
continue;
case S_authorization:
@@ -707,16 +736,25 @@
case S_file:
case S_cleartext:
case S_des:
+#ifdef USE_PAM
+ case S_pam:
+#endif /* USE_PAM */
sprintf(buf, "%s ", sym_buf);
sym_get();
strcat(buf, sym_buf);
user->login = tac_strdup(buf);
break;
-
+
default:
+#ifdef USE_PAM
+ parse_error(
+ "expecting 'file', 'cleartext', 'pam', 'nopassword', 'skey', or 'des' keyword after 'login =' on line %d",
+ sym_line);
+#else
parse_error(
"expecting 'file', 'cleartext', 'nopassword', 'skey', or 'des' keyword after 'login =' on line %d",
sym_line);
+#endif /* USE_PAM */
}
sym_get();
continue;
@@ -734,16 +772,29 @@
case S_cleartext:
case S_des:
+#ifdef USE_PAM
+ case S_pam:
+#endif /*USE_PAM */
sprintf(buf, "%s ", sym_buf);
sym_get();
strcat(buf, sym_buf);
user->pap = tac_strdup(buf);
+ break;
+
+ sprintf(buf, "%s ", sym_buf);
+ user->pap = tac_strdup(buf);
break;
default:
+#ifdef USE_PAM
+ parse_error(
+ "expecting 'cleartext', 'pam', or 'des' keyword after 'pap =' on line %d",
+ sym_line);
+#else
parse_error(
"expecting 'cleartext', or 'des' keyword after 'pap =' on line %d",
sym_line);
+#endif /*USE_PAM */
}
sym_get();
continue;
diff -Naur tac_plus.F4.0.4.alpha/parse.c tac_plus.F4.0.4.alpha-PAM-0.4/parse.c
--- tac_plus.F4.0.4.alpha/parse.c Sun Jun 18 13:26:53 2000
+++ tac_plus.F4.0.4.alpha-PAM-0.4/parse.c Thu Feb 1 14:25:34 2001
@@ -72,6 +72,9 @@
declare("ms-chap", S_mschap);
#endif /* MSCHAP */
declare("cleartext", S_cleartext);
+#ifdef USE_PAM
+ declare("pam", S_pam);
+#endif /*USE_PAM */
declare("nopassword", S_nopasswd);
declare("cmd", S_cmd);
declare("default", S_default);
@@ -183,6 +186,10 @@
return ("opap");
case S_cleartext:
return ("cleartext");
+#ifdef USE_PAM
+ case S_pam:
+ return ("pam");
+#endif /*USE_PAM */
case S_nopasswd:
return("nopassword");
case S_des:
diff -Naur tac_plus.F4.0.4.alpha/parse.h tac_plus.F4.0.4.alpha-PAM-0.4/parse.h
--- tac_plus.F4.0.4.alpha/parse.h Sun Jun 18 13:26:54 2000
+++ tac_plus.F4.0.4.alpha-PAM-0.4/parse.h Thu Feb 1 14:25:35 2001
@@ -76,3 +76,6 @@
#ifdef MSCHAP
#define S_mschap 42
#endif /* MSCHAP */
+#ifdef USE_PAM
+#define S_pam 43
+#endif /*USE_PAM */
diff -Naur tac_plus.F4.0.4.alpha/pwlib.c tac_plus.F4.0.4.alpha-PAM-0.4/pwlib.c
--- tac_plus.F4.0.4.alpha/pwlib.c Sun Jun 18 13:26:54 2000
+++ tac_plus.F4.0.4.alpha-PAM-0.4/pwlib.c Thu Feb 1 14:25:35 2001
@@ -25,6 +25,11 @@
#include <shadow.h>
#endif
+#ifdef USE_PAM
+int
+tac_pam_auth(char *UserName,char *Password,struct authen_data *data,char *Service);
+#endif /* USE_PAM */
+
/* Generic password verification routines for des, file and cleartext
passwords */
@@ -120,7 +125,27 @@
if (!cfg_passwd) {
char *file = cfg_get_authen_default();
if (file) {
+#ifdef USE_PAM
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify daemon %s == NAS %s", p, passwd);
+ if (tac_pam_auth(name, passwd, data,file)) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is incorrect");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return(0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is correct");
+ }
+
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+#else
return (passwd_file_verify(name, passwd, data, file));
+#endif /* USE_PAM */
}
/* otherwise, we fail */
@@ -131,6 +156,7 @@
/* We have a configured password. Deal with it depending on its
type */
+
p = tac_find_substring("cleartext ", cfg_passwd);
if (p) {
if (debug & DEBUG_PASSWD_FLAG)
@@ -152,6 +178,31 @@
set_expiration_status(exp_date, data);
return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
}
+
+#ifdef USE_PAM
+ p = tac_find_substring("pam ", cfg_passwd);
+ if (p) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "verify daemon %s == NAS %s", p, passwd);
+
+ if (tac_pam_auth(name, passwd, data,p)) {
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is incorrect");
+ data->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ return(0);
+ } else {
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+
+ if (debug & DEBUG_PASSWD_FLAG)
+ report(LOG_DEBUG, "Password is correct");
+ }
+
+ exp_date = cfg_get_expires(name, recurse);
+ set_expiration_status(exp_date, data);
+ return (data->status == TAC_PLUS_AUTHEN_STATUS_PASS);
+ }
+
+#endif /* USE_PAM */
p = tac_find_substring("des ", cfg_passwd);
if (p) {
diff -Naur tac_plus.F4.0.4.alpha/tac_pam_auth.c tac_plus.F4.0.4.alpha-PAM-0.4/tac_pam_auth.c
--- tac_plus.F4.0.4.alpha/tac_pam_auth.c Wed Dec 31 19:00:00 1969
+++ tac_plus.F4.0.4.alpha-PAM-0.4/tac_pam_auth.c Fri Feb 2 16:39:32 2001
@@ -0,0 +1,148 @@
+/* tac_pam_auth.c
+ * A simple pam authentication routine written by Max Liccardo <ravel@tiscalinet.it>
+ * 2 February 2001 - Add pam_acct_mgmt() so account must also be permitted access to succeed
+ * - Chris Johnson <cjohnson@mint.net>
+ * PAM_RUSER=username/rem_addr.
+ */
+
+
+/*
+ This program was contributed by Shane Watts
+ [modifications by AGM]
+ [modifications by CKJ]
+
+ The following samples are based on references to 'login pam tacacs-nas' for
+ some user or group in the tac_plus configuration file. Any pam service that
+ is referenced in your tacacs configuration must have a definition in your pam
+ configuration.
+
+ You need to add the following (or equivalent) to the /etc/pam.conf file.
+ # check authorization
+ tacacs-nas auth required /usr/lib/security/pam_unix_auth.so
+ tacacs-nas account required /usr/lib/security/pam_unix_acct.so
+
+ Account restrictions now apply. The following is a more modern sample
+ /etc/pam.d/tacacs-nas file which forces an account lockout after 3 bad passwords:
+ #%PAM-1.0
+ auth required /lib/security/pam_tally.so no_magic_root
+ auth required /lib/security/pam_stack.so service=system-auth
+ account required /lib/security/pam_tally.so no_magic_root deny=3 reset
+ account required /lib/security/pam_stack.so service=system-auth
+
+ */
+
+#ifdef USE_PAM
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include "tac_plus.h"
+
+typedef struct
+{
+ char *UserName;
+ char *Passwd;
+} UserCred;
+
+
+static int fconv(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp,void *appdata_ptr)
+{
+ int i;
+ UserCred *lUserCred;
+
+
+ lUserCred = appdata_ptr;
+
+ if(lUserCred == NULL)
+ {
+ report(LOG_ERR,"argh....maybe a SunOs 5.6 ???");
+ return(PAM_CONV_ERR);
+ }
+
+
+ *resp = (struct pam_response *) calloc(num_msg,sizeof(struct pam_response));
+
+ for(i=0;i<num_msg;i++)
+ {
+ switch(msg[i]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_OFF:
+ resp[i]->resp = strdup(lUserCred->Passwd);
+ break;
+
+ case PAM_PROMPT_ECHO_ON:
+ resp[i]->resp = strdup(lUserCred->UserName);
+ break;
+
+ default:
+ report(LOG_DEBUG,"conv default");
+ break;
+ }
+ resp[i]->resp_retcode = 0;
+ }
+
+ return(PAM_SUCCESS);
+}
+
+
+
+
+int
+tac_pam_auth(char *aszUserName,char *aszPassword,struct authen_data *data,
+ char *aszService)
+{
+ pam_handle_t *pamh=NULL;
+ int retval;
+ char *lpszRemoteUser; /* Username/NAC address */
+ struct pam_conv s_conv;
+ UserCred s_UserCred;
+
+
+ s_UserCred.UserName = aszUserName;
+ s_UserCred.Passwd = aszPassword;
+
+ s_conv.conv = fconv;
+ s_conv.appdata_ptr = (void *) &s_UserCred;
+
+
+ if((lpszRemoteUser = calloc(strlen(aszUserName)+strlen(data->NAS_id->NAC_address)+2,sizeof(char))) == NULL)
+ {
+ report(LOG_ERR,"cannot malloc");
+ return(1);
+ }
+
+ retval = pam_start(aszService,aszUserName , &s_conv, &pamh);
+
+ if (retval != PAM_SUCCESS)
+ {
+ report(LOG_ERR, "cannot start pam-authentication");
+ pamh = NULL;
+ return(1);
+ }
+
+ sprintf(lpszRemoteUser,"%s:%s",aszUserName,data->NAS_id->NAC_address);
+
+ pam_set_item(pamh,PAM_RUSER,lpszRemoteUser);
+ pam_set_item(pamh,PAM_RHOST,data->NAS_id->NAS_name);
+ pam_set_item(pamh,PAM_TTY,data->NAS_id->NAS_port);
+
+ free(lpszRemoteUser);
+
+ retval = pam_authenticate(pamh,0); /* is user really user? */
+ if (retval == PAM_SUCCESS) retval = pam_acct_mgmt(pamh,0); /* permitted access? */
+
+ if(retval != PAM_SUCCESS)
+ report(LOG_ERR, "%s",pam_strerror(pamh,retval));
+
+ if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */
+ pamh = NULL;
+ return(1);
+ }
+
+ return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */
+}
+
+
+#endif /* USE_PAM */
diff -Naur tac_plus.F4.0.4.alpha/tac_plus.conf tac_plus.F4.0.4.alpha-PAM-0.4/tac_plus.conf
--- tac_plus.F4.0.4.alpha/tac_plus.conf Wed Dec 31 19:00:00 1969
+++ tac_plus.F4.0.4.alpha-PAM-0.4/tac_plus.conf Fri Feb 2 17:33:24 2001
@@ -0,0 +1,25 @@
+#tac_plus.conf - a simple configuration file
+
+key = "Cisco"
+
+# in PAM mode, we're using the pam tacacs-login service
+default authentication = pam tacacs-login
+# otherwise, we're using the mypw password file
+#default authentication = file /etc/tacacs/mypw
+
+accounting file = "/var/log/tactest.acct"
+
+user=DEFAULT {
+ default service = permit
+}
+
+# repeat as necessary for each user
+user=test {
+ default service = permit
+# in order to work, you need the file /etc/pam.d/tacacs-login or a "tacacs-login" entry into the /etc/pam.conf file
+ login = pam tacacs-login
+
+# in order to work, you need the file /etc/pam.d/tacacs-pap or a "tacacs-pap" entry into the /etc/pam.conf file
+ pap = pam tacacs-pap
+}
+
diff -Naur tac_plus.F4.0.4.alpha/tac_plus.h tac_plus.F4.0.4.alpha-PAM-0.4/tac_plus.h
--- tac_plus.F4.0.4.alpha/tac_plus.h Sun Jun 18 13:26:54 2000
+++ tac_plus.F4.0.4.alpha-PAM-0.4/tac_plus.h Thu Feb 1 14:25:35 2001
@@ -69,7 +69,7 @@
*/
/* #define REARMSIGNAL */
-#define VERSION "F4.0.4.alpha"
+#define VERSION "F4.0.4.alpha-PAM-0.3"
/*
* System definitions.
diff -Naur tac_plus.F4.0.4.alpha/tac_plus.init tac_plus.F4.0.4.alpha-PAM-0.4/tac_plus.init
--- tac_plus.F4.0.4.alpha/tac_plus.init Wed Dec 31 19:00:00 1969
+++ tac_plus.F4.0.4.alpha-PAM-0.4/tac_plus.init Fri Feb 2 18:08:42 2001
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# description: Cisco's tacacs+ access, authorization, and accounting server.
+# chkconfig: 345 15 85
+#
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -f /usr/local/sbin/tac_plus ] || exit 0
+
+[ -f /etc/tacacs/tac_plus.cfg ] || exit 0
+
+# ===========================================================================
+# Normally, only messages at error level or above are written to the log.
+# To write TACACS+ info messages to the log, this option takes a parameter
+# based on a sum of the following options:
+# number information about
+# 1 general
+# 2 parsing
+# 4 forking
+# 8 authorization
+# 16 authentication
+# 32 passwords
+# 64 accounting
+# 128 configuration
+# 256 packet
+# 512 hex
+# 1024 md5 hash
+# 2048 xor
+# 4096 clean
+# 8192 subst
+#
+# For more information on the various debug flags, see your Cisco manual.
+
+DEBUG_LEVEL=255
+#DEBUG_LEVEL=31
+# See how we were called.
+case "$1" in
+ start)
+ # Start daemons.
+ echo -n "Starting tacacs+: "
+ /usr/local/sbin/tac_plus -C /etc/tacacs/tac_plus.cfg \
+ -l /var/log/tacacs/tac_plus.log \
+ -w /var/log/tacacs/tac_plus.who \
+ -d $DEBUG_LEVEL && \
+ success || failure
+ echo
+ touch /var/lock/subsys/tacacs
+ ;;
+ stop)
+ # Stop daemons.
+ echo -n "Shutting down tacacs+: "
+ killproc tac_plus
+ rm -f /var/lock/subsys/tacacs
+ echo
+ ;;
+ status)
+ status tac_plus
+ exit $?
+ ;;
+ restart)
+ $0 stop
+ $0 start
+ exit $?
+ ;;
+ reload)
+ kill -USR1 `cat /var/run/tac_plus.pid`
+ ;;
+ *)
+ echo "Usage: tacacs {start|stop|status|restart|reload}"
+ exit 1
+esac