Instantly share code, notes, and snippets.

Embed
What would you like to do?
Tacacs AFL Patch for 4.0.4.28 with pamenable.patch by ragzilla
--- ../../tacacs-F4.0.4.28/authen.c 2014-07-31 14:09:47.000000000 -0600
+++ ./authen.c 2018-05-08 11:17:09.056622318 -0600
@@ -329,10 +329,22 @@
return;
}
- if ((*func)(datap)) {
+#ifdef AFL
+ if ((session.afl_cfg) &&
+ cfg_is_user_disabled(datap->NAS_id->username) == 1)
+ datap->status = TAC_PLUS_AUTHEN_STATUS_FAIL;
+ else if ((*func) (datap)) {
send_authen_error("Unexpected authentication function failure");
return;
}
+#else
+ if((*func) (datap))
+ {
+ send_authen_error("Unexpected authentication function failure");
+ return;
+ }
+#endif
+
switch (datap->status) {
default:
@@ -358,6 +370,10 @@
return;
case TAC_PLUS_AUTHEN_STATUS_FAIL:
/* An invalid user/password combination */
+#ifdef AFL
+ if(session.afl_cfg)
+ cfg_increment_failure(datap->NAS_id->username);
+#endif
send_authen_reply(TAC_PLUS_AUTHEN_STATUS_FAIL,
datap->server_msg,
datap->server_msg ? strlen(datap->server_msg) : 0,
--- ../../tacacs-F4.0.4.28/config.c 2012-06-06 16:11:30.000000000 -0600
+++ ./config.c 2018-05-08 11:28:40.059011239 -0600
@@ -132,6 +132,11 @@
static int sym_error = 0; /* a parsing error occurred */
static char *authen_default = NULL; /* top level authentication default */
static char *nopasswd_str = "nopassword";
+#ifdef AFL
+static unsigned int user_count = 0; /* the number of users in the config */
+static key_t failed_key; /* the shm key for AFL */
+#endif
+
/*
* A host definition structure.
@@ -196,6 +201,9 @@
#ifdef MAXSESS
int maxsess; /* Max sessions/user */
#endif /* MAXSESS */
+#ifdef AFL
+ int shm_offset;
+#endif
} USER;
typedef USER GROUP;
@@ -227,6 +235,16 @@
struct host h;
} HASH;
+#ifdef AFL
+struct failed_node {
+ char username[65];
+ unsigned int failures;
+ time_t first_failure;
+ time_t locked_time; /* the time the lock was set */
+ char disabled;
+};
+#endif
+
void *grouptable[HASH_TAB_SIZE];/* Table of group declarations */
void *usertable[HASH_TAB_SIZE]; /* Table of user declarations */
#ifdef ACLS
@@ -535,6 +553,14 @@
}
session.flags = 0;
+#ifdef AFL
+ if (session.afl_cfg) {
+ cfg_destroy_failure_shm();
+ free(session.afl_cfg);
+ session.afl_cfg = NULL;
+ }
+#endif
+
#ifdef ACLS
/* clean the acltable */
for (i = 0; i < HASH_TAB_SIZE; i++) {
@@ -765,7 +791,23 @@
switch (sym_code) {
case S_eof:
return(0);
-
+#ifdef AFL
+ case S_auth_fail_lock:
+ /* faillock a b c
+ * a = number of failures
+ * b = in this many seconds
+ * c = lock for this many seconds */
+ session.afl_cfg = (struct afl_cfg *)tac_malloc(sizeof(struct afl_cfg));
+ bzero(session.afl_cfg, sizeof(struct afl_cfg));
+ sym_get();
+ session.afl_cfg->num_failures = atoi(sym_buf);
+ sym_get();
+ session.afl_cfg->seconds = atoi(sym_buf);
+ sym_get();
+ session.afl_cfg->lock_time = atoi(sym_buf);
+ sym_get();
+ break;
+#endif
case S_accounting:
sym_get();
@@ -1220,9 +1262,17 @@
user->enable = tac_strdup(sym_buf);
break;
#endif
+#ifdef HAVE_PAM
+ case S_pam:
+ user->enable = tac_strdup(sym_buf);
+ break;
+#endif
default:
parse_error("expecting 'file', 'cleartext', 'nopassword', "
+#ifdef HAVE_PAM
+ "'PAM', "
+#endif
#ifdef SKEY
"'skey', "
#endif
@@ -1900,9 +1950,15 @@
user = (USER *)hash_lookup(isuser ? usertable : grouptable, name);
if (!user) {
+ /* look up default user */
+ user = (USER *)hash_lookup(isuser ? usertable : grouptable, "DEFAULT");
+ if (!user) {
+ if (debug & DEBUG_CONFIG_FLAG)
+ report(LOG_DEBUG, "cfg_get_value: no user/group named %s", name);
+ return(value);
+ }
if (debug & DEBUG_CONFIG_FLAG)
- report(LOG_DEBUG, "cfg_get_value: no user/group named %s", name);
- return(value);
+ report(LOG_DEBUG, "cfg_get_value: falling back to DEFAULT for user/group named %s", name);
}
/* found the entry. Lookup value from attr=value */
@@ -2468,6 +2524,306 @@
return(authen_default);
}
+#ifdef AFL
+static unsigned int fetch_user_count(void)
+{
+ int i;
+ unsigned int count;
+ USER *entry;
+
+ count = 0;
+
+ for (i=0; i < HASH_TAB_SIZE; i++)
+ {
+ entry = (USER *)usertable[i];
+ while (entry)
+ {
+ count++;
+ entry = entry->hash;
+ }
+ }
+ return count;
+}
+
+/*
+ * Create a user-table in shared memory for AFL.
+ */
+void cfg_create_failure_shm(const char *path, int id)
+{
+ unsigned int shm_sz;
+ int offset;
+ int i, shmid;
+ char *shm = NULL;
+
+ user_count = shm_sz = offset = 0;
+
+ user_count = fetch_user_count();
+ shm_sz = user_count * sizeof(struct failed_node);
+
+ if((failed_key = ftok(path, id))<0)
+ {
+ report(LOG_ERR, "ftok unable to create key: %s",
+ strerror(errno));
+ tac_exit(1);
+ }
+
+ if ((shmid = shmget(failed_key, shm_sz, IPC_CREAT|0666)) < 0)
+ {
+ report(LOG_ERR, "shmget unable to get memory: %s",
+ strerror(errno));
+ tac_exit(1);
+ }
+
+ if ((shm = (char *)shmat(shmid, NULL, 0)) == (char *)-1)
+ {
+ report(LOG_ERR, "shmat: %s", strerror(errno));
+ tac_exit(1);
+ }
+
+ /* iterate over all the users and add them a failed_node
+ * structure to the shared memory */
+ for (i=0; i < HASH_TAB_SIZE; i++)
+ {
+ struct failed_node *failed_node;
+ USER *entry = (USER *)usertable[i];
+
+ while(entry)
+ {
+ entry->shm_offset = offset;
+ failed_node = (struct failed_node *)&shm[offset];
+ bzero(failed_node, sizeof(struct failed_node));
+
+ if (strlen(entry->name) > 64)
+ {
+ report(LOG_ERR, "username %s > length of 64",
+ entry->name);
+ tac_exit(1);
+ }
+
+ strncpy(failed_node->username, entry->name, 64);
+ offset += sizeof(struct failed_node);
+
+ if (offset > user_count * sizeof(struct failed_node))
+ {
+ report(LOG_ERR, "More users than previously allocated");
+ tac_exit(1);
+ }
+
+ entry = entry->hash;
+ }
+ }
+
+ /* now create a semaphore for locking */
+ if(semget(failed_key, 1, 0666 | IPC_CREAT) < 0)
+ {
+ report(LOG_ERR, "Unable to create semaphore: %s", strerror(errno));
+ tac_exit(1);
+ }
+}
+
+char *shm_fetch_and_lock(key_t key, unsigned int sz)
+{
+ int shmid, semid;
+ struct sembuf sem[2];
+ char *shm = NULL;
+
+ if((semid = semget(key, 1, 0666)) < 0)
+ {
+ report(LOG_ERR, "unable to fetch semaphore: %s", strerror(errno));
+ tac_exit(1);
+ }
+
+ sem[0].sem_num = 0;
+ sem[1].sem_num = 0;
+ sem[0].sem_flg = SEM_UNDO;
+ sem[1].sem_flg = SEM_UNDO;
+ sem[0].sem_op = 0;
+ sem[1].sem_op = 1;
+
+ semop(semid, sem, 2);
+
+ if((shmid = shmget(key, sz, 0666)) < 0)
+ {
+ report(LOG_ERR, "shm_fetchnlock unable to get shmid: %s",
+ strerror(errno));
+ tac_exit(1);
+ }
+
+ if ((shm = shmat(shmid, NULL, 0)) == (char *) -1)
+ {
+ report(LOG_ERR, "shm_fetchnlock Unable to find shm segment: %s",
+ strerror(errno));
+ tac_exit(1);
+ }
+
+ return shm;
+}
+
+static void shm_unlock(key_t key)
+{
+ int semid;
+ struct sembuf sem;
+
+ if ((semid = semget(key, 1, 0666)) < 0)
+ tac_exit(1);
+
+ sem.sem_num = 0;
+ sem.sem_flg = SEM_UNDO;
+ sem.sem_op = -1;
+
+ semop(semid, &sem, 1);
+}
+
+static void destroy_semaphore(key_t key)
+{
+ int semid;
+ semid = semget(key, 0, 0666);
+ semctl(semid, 0, IPC_RMID, NULL);
+}
+
+static void destroy_shm(key_t key)
+{
+ int shmid;
+ shmid = shmget(key, 1, 0666);
+ shmctl(shmid, IPC_RMID, NULL);
+}
+
+
+void cfg_destroy_failure_shm(void)
+{
+ destroy_semaphore(failed_key);
+ destroy_shm(failed_key);
+}
+
+static int shm_failed_offset(char *username, void *arg)
+{
+ USER *user;
+
+ if (arg == NULL)
+ user = (USER *)hash_lookup(usertable, username);
+ else
+ user = (USER *)arg;
+
+ return (user ? user->shm_offset:-1);
+}
+
+void cfg_increment_failure(char *username)
+{
+ USER *user;
+ int offset;
+ char *data;
+ struct failed_node *node;
+ time_t now;
+
+ user = hash_lookup(usertable, username);
+
+ if (user == NULL)
+ return;
+
+ if ((offset = shm_failed_offset(username, user)) < 0)
+ return;
+
+ data = shm_fetch_and_lock(failed_key,
+ user_count * sizeof(struct failed_node));
+
+ node = (struct failed_node *)&data[user->shm_offset];
+
+ if (strcmp(node->username, username) != 0)
+ {
+ report(LOG_ERR, "Shared memory has something amiss (%s!=%s)",
+ node->username, username);
+ shm_unlock(failed_key);
+ return;
+ }
+
+ time(&now);
+
+ if (!node->first_failure)
+ node->first_failure = now;
+
+ /* determine if this fail has exceeded the number of failures within
+ * the time window. If it has then lock the account.
+ */
+ if((!node->disabled) && ++node->failures >= session.afl_cfg->num_failures)
+ {
+ report(LOG_WARNING, "User %s has been disabled for %d seconds",
+ username, session.afl_cfg->lock_time);
+ node->locked_time = now;
+ node->disabled = 1;
+ }
+
+ shm_unlock(failed_key);
+}
+
+/*
+ * Attempt to determine whether a user is locked out or not,
+ * this function also does timer expiration.
+ */
+int cfg_is_user_disabled(char *username)
+{
+ USER *user;
+ int offset;
+ char *data;
+ struct failed_node *node;
+ int ret = 0;
+ time_t now;
+
+ user = hash_lookup(usertable, username);
+
+ if (user == NULL)
+ return -1;
+
+ if ((offset = shm_failed_offset(username, user))<0)
+ return -1;
+
+ data = shm_fetch_and_lock(failed_key,
+ user_count * sizeof(struct failed_node));
+
+ node = (struct failed_node *)&data[user->shm_offset];
+
+ /* check to make sure what we have is true */
+ if (strcmp(node->username, username) != 0)
+ {
+ report(LOG_ERR, "Shared memory has something amiss (%s!=%s)",
+ node->username, username);
+ shm_unlock(failed_key);
+ return -1;
+ }
+
+ ret = node->disabled?1:0;
+ time(&now);
+
+ if (ret)
+ {
+ /* Check locked account expiration. Unlock if expired. */
+ if (difftime(now, node->locked_time) > session.afl_cfg->lock_time)
+ {
+ report(LOG_WARNING, "Re-enabling account: %s", username);
+ node->first_failure = 0;
+ node->disabled = 0;
+ node->failures = 0;
+ node->locked_time = 0;
+ ret = 0;
+ }
+ }
+ else {
+ /* Check to see if the auth-fail window has expired. */
+ if ((node->first_failure) &&
+ difftime(now, node->first_failure) > session.afl_cfg->seconds)
+ {
+ report(LOG_INFO,"Resetting failure clock for user: %s\n", username);
+ node->first_failure = 0;
+ node->disabled = 0;
+ node->failures = 0;
+ node->locked_time = 0;
+ }
+ }
+
+ shm_unlock(failed_key);
+ return ret;
+}
+#endif /* AFL */
+
/*
* Return 1 if this user has any ppp services configured. Used for
* authorizing ppp/lcp requests
--- ../../tacacs-F4.0.4.28/config.h.in 2015-01-06 14:55:32.000000000 -0700
+++ ./config.h.in 2018-05-08 11:17:09.080622541 -0600
@@ -318,6 +318,8 @@
# define socklen_t int
#endif
+#undef AFL
+
/* host specifics */
/* Define this if your password file does not contain age and comment fields. */
#define NO_PWAGE
--- ../../tacacs-F4.0.4.28/configure.ac 2015-01-06 14:22:16.000000000 -0700
+++ ./configure.ac 2018-05-08 11:17:09.080622541 -0600
@@ -720,6 +720,36 @@
AC_SUBST(PROFLAGS)
AC_SUBST(PROFLIBS)
+dnl
+dnl DISABLE AFL Support (Authentication Failure Locking)
+dnl
+AC_MSG_CHECKING(whether to enable AFL support)
+AH_TEMPLATE(AFL, [define this to disable Authentication Failure Locking support])
+AC_ARG_ENABLE(afl,
+[ --enable-afl Enable AFL support (default)],
+[ case "$enable_afl" in
+ no)
+ AC_MSG_RESULT(no)
+ use_afl=0
+ ;;
+ yes)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(AFL)
+ use_afl=1
+ ;;
+ *)
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(AFL)
+ use_afl=1
+ ;;
+ esac ],
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(AFL)
+ use_afl=1
+)
+AC_SUBST(AFL)
+
+
# look for PAM
AH_TEMPLATE(HAVE_PAM, [define if your system has libpam])
AC_CHECK_LIB([pam], [pam_start],
--- ../../tacacs-F4.0.4.28/parse.c 2012-04-10 12:34:40.000000000 -0600
+++ ./parse.c 2018-05-08 11:17:09.080622541 -0600
@@ -121,6 +121,9 @@
declare("PAM", S_pam);
#endif
declare("syslog", S_syslog);
+#ifdef AFL
+ declare("auth-fail-lock", S_auth_fail_lock);
+#endif
}
/* Return a keyword code if a keyword is recognized. 0 otherwise */
--- ../../tacacs-F4.0.4.28/parse.h 2012-04-10 12:34:40.000000000 -0600
+++ ./parse.h 2018-05-08 11:17:09.080622541 -0600
@@ -91,3 +91,6 @@
#endif
#define S_syslog 50
#define S_aceclnt 51
+#ifdef AFL
+#define S_auth_fail_lock 52
+#endif
--- ../../tacacs-F4.0.4.28/tac_plus.c 2014-12-30 12:58:49.000000000 -0700
+++ ./tac_plus.c 2018-05-08 11:17:09.084622578 -0600
@@ -86,6 +86,10 @@
report(LOG_NOTICE, "Received signal %d, shutting down", signum);
if (childpid > 0)
unlink(pidfilebuf);
+
+#ifdef AFL
+ cfg_destroy_failure_shm();
+#endif
tac_exit(0);
}
@@ -102,6 +106,10 @@
tac_exit(1);
}
+#ifdef AFL
+ session.afl_cfg = NULL;
+#endif
+
/* read the config file */
if (cfg_read_config(session.cfgfile)) {
report(LOG_ERR, "Parsing %s", session.cfgfile);
@@ -111,6 +119,11 @@
if (session.acctfile == NULL && !(session.flags & SESS_FLAG_ACCTSYSL))
session.acctfile = tac_strdup(TACPLUS_ACCTFILE);
+#ifdef AFL
+ if(session.afl_cfg)
+ cfg_create_failure_shm(session.cfgfile, 'A');
+#endif
+
initialised++;
reinitialize = 0;
report(LOG_NOTICE, "Version %s Initialized %d", version, initialised);
@@ -381,6 +394,9 @@
signal(SIGUSR1, handler);
signal(SIGHUP, handler);
signal(SIGTERM, die);
+#ifdef AFL
+ signal(SIGINT, die);
+#endif
signal(SIGPIPE, SIG_IGN);
if (parse_only)
@@ -998,6 +1014,9 @@
#if __STDC__
fprintf(stdout, "__STDC__\n");
#endif
+#if AFL
+ fprintf(stdout, "AFL\n");
+#endif
return;
}
--- ../../tacacs-F4.0.4.28/tac_plus.h 2012-04-10 13:38:45.000000000 -0600
+++ ./tac_plus.h 2018-05-08 11:17:09.084622578 -0600
@@ -90,6 +90,12 @@
#include "pathsl.h"
#include "md5.h"
+#ifdef AFL
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#endif
+
typedef struct tac_plus_pak_hdr HDR;
/*
@@ -182,6 +188,14 @@
#define NAS_PORT_MAX_LEN 255
+#ifdef AFL
+struct afl_cfg {
+ int num_failures;
+ int seconds;
+ int lock_time;
+};
+#endif
+
struct session {
int session_id; /* host specific unique session id */
int aborted; /* have we received an abort flag? */
@@ -200,6 +214,9 @@
char *acctfile; /* name of accounting file */
char port[NAS_PORT_MAX_LEN+1]; /* For error reporting */
u_char version; /* version of last packet read */
+#ifdef AFL
+ struct afl_cfg *afl_cfg; /* authentication failure lock cfg */
+#endif
};
extern struct session session; /* the session */
@@ -402,6 +419,12 @@
int cfg_read_config(char *);
int cfg_user_exists(char *);
int cfg_user_svc_default_is_permit(char *);
+#ifdef AFL
+void cfg_create_failure_shm(const char *, int);
+void cfg_destroy_failure_shm(void);
+void cfg_increment_failure(char *);
+int cfg_is_user_disabled(char *);
+#endif
/* default_fn.c */
int default_fn(struct authen_data *);
--- ../../tacacs-F4.0.4.28/configure 2015-01-06 14:55:35.000000000 -0700
+++ ./configure 2018-05-08 11:17:09.140623098 -0600
@@ -637,6 +637,7 @@
PERLV_PATH
INST_PROGS
TAR
+AFL
PROFLIBS
PROFLAGS
TACPLUS_WHOLOGFILE
@@ -819,6 +820,7 @@
with_logfile
with_whologfile
with_prof
+enable_afl
'
ac_precious_vars='build_alias
host_alias
@@ -1469,6 +1471,7 @@
--enable-uenable tacacs config per-user enable support (default)
--enable-maxsess Enforce a limit on maximum sessions per user
--enable-finger finger NAS for number of sessions a user is using
+ --enable-afl Enable AFL support (default)
--enable-arapdes enable DES for ARAP
--enable-mschap enable MSCHAP
--enable-mschapdes enable DES for MSCHAP
@@ -14959,6 +14962,40 @@
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable AFL support" >&6
+$as_echo_n "checking whether to enable AFL support... " >&6; }
+
+# Check whether --enable-afl was given.
+if test "${enable_afl+set}" = set; then :
+ enableval=$enable_afl; case "$enable_afl" in
+ no)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ use_afl=0
+ ;;
+ yes)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ $as_echo "#define AFL 1" >>confdefs.h
+
+ use_afl=1
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ $as_echo "#define AFL 1" >>confdefs.h
+
+ use_afl=1
+ ;;
+ esac
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ $as_echo "#define AFL 1" >>confdefs.h
+
+ use_afl=1
+
+fi
# look for PAM
--- ../../tacacs-F4.0.4.28/aceclnt_fn.c 2012-06-06 12:31:25.000000000 -0600
+++ ./aceclnt_fn.c 2018-05-08 11:17:46.156965966 -0600
@@ -193,6 +193,7 @@
return(1);
}
+ data->flags = TAC_PLUS_AUTHEN_FLAG_NOECHO;
snprintf(buf, ACEBUFSZ, "Enter PASSCODE: ");
data->server_msg = tac_strdup(buf);
data->status = TAC_PLUS_AUTHEN_STATUS_GETPASS;
--- ../../tacacs-F4.0.4.28/enable.c 2012-03-27 12:37:57.000000000 -0600
+++ ./enable.c 2018-05-08 11:34:12.398375523 -0600
@@ -53,6 +53,16 @@
/* if the user has a user-specific enable password, check it */
cfg_passwd = cfg_get_enable_secret(username, TAC_PLUS_RECURSE);
if (cfg_passwd != NULL) {
+# ifdef HAVE_PAM
+ if (strcmp(cfg_passwd, "PAM") == 0) {
+ if (!pam_verify(username, passwd))
+ goto FAIL;
+ data->status = TAC_PLUS_AUTHEN_STATUS_PASS;
+ exp_date = cfg_get_expires(username, TAC_PLUS_RECURSE);
+ set_expiration_status(exp_date, data);
+ goto SUCCESS;
+ }
+# endif
if ((verify_pwd(username, passwd, data, cfg_passwd))) {
exp_date = cfg_get_expires(username, TAC_PLUS_RECURSE);
set_expiration_status(exp_date, data);
--- ../../tacacs-F4.0.4.28/pwlib.c 2013-08-01 10:05:20.000000000 -0600
+++ ./pwlib.c 2018-05-08 12:05:14.060471533 -0600
@@ -49,9 +49,6 @@
*/
static int etc_passwd_file_verify(char *, char *, struct authen_data *);
static int des_verify(char *, char *);
-#if HAVE_PAM
-static int pam_verify(char *, char *);
-#endif
static int passwd_file_verify(char *, char *, struct authen_data *, char *);
extern char *progname;
@@ -595,7 +592,7 @@
* verify a provided user/password via PAM.
* return 1 if verified, 0 otherwise.
*/
-static int
+int
pam_verify(char *user, char *passwd)
{
int err;
--- ../../tacacs-F4.0.4.28/tacacs.h 2013-08-04 07:51:47.000000000 -0600
+++ ./tacacs.h 2018-05-08 11:44:00.636216329 -0600
@@ -482,6 +482,9 @@
void set_expiration_status(char *, struct authen_data *);
int verify(char *, char *, struct authen_data *, int);
int verify_pwd(char *, char *, struct authen_data *, char *);
+#if HAVE_PAM
+int pam_verify(char *, char *);
+#endif
int aceclnt_fn(struct authen_data *data);
int default_v0_fn(struct authen_data *data);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment