[tac_plus] Re: bad password/ban
Mark Ellzey Thomas
mark.thomas at corp.aol.com
Tue Jun 2 20:53:42 UTC 2009
On Tue, Jun 02, 2009 at 02:04:53PM -0400, Mark Ellzey Thomas wrote:
> On Mon, Jun 01, 2009 at 05:47:43PM -0700, Mehrdad wrote:
> > Hello,
> >
> > I'm using the Tacacs+ 4.0.4.16 and I'm looking for logging bad password or
> > ban IP address after specified bad password is there any feature in this
> > regard?
> > That's my suggestion if there isn't
> >
> >
>
> Greetings Mehrdad,
>
> I wrote a patch to lock an account after the daemon notices a bunch of
> auth failures from one user. Giving a quick glance to recent releases
> it does not look as if this was accepted.
>
> You can find the original post/patch here:
> http://www.shrubbery.net/pipermail/tac_plus/2008-June/000248.html
>
FYI, I have attached the code patched against the latest tac_plus
release.
-------------- next part --------------
diff -Naur ../tacacs+-F4.0.4.18/authen.c ./authen.c
--- ../tacacs+-F4.0.4.18/authen.c 2009-03-18 17:11:36.000000000 -0400
+++ ./authen.c 2009-06-02 14:55:06.000000000 -0400
@@ -324,10 +324,21 @@
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) {
@@ -357,6 +368,10 @@
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,
diff -Naur ../tacacs+-F4.0.4.18/config.c ./config.c
--- ../tacacs+-F4.0.4.18/config.c 2009-03-18 19:24:54.000000000 -0400
+++ ./config.c 2009-06-02 14:56:39.000000000 -0400
@@ -113,6 +113,11 @@
static int no_user_dflt = 0; /* default if user doesn't exist */
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.
@@ -177,6 +182,9 @@
#ifdef MAXSESS
int maxsess; /* Max sessions/user */
#endif /* MAXSESS */
+#ifdef AFL
+ int shm_offset;
+#endif
} USER;
#ifdef ACLS
@@ -212,6 +220,16 @@
typedef union hash 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
@@ -519,6 +537,14 @@
session.acctfile = NULL;
}
+#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++) {
@@ -744,7 +770,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();
@@ -2427,6 +2469,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
diff -Naur ../tacacs+-F4.0.4.18/config.h.in ./config.h.in
--- ../tacacs+-F4.0.4.18/config.h.in 2009-03-18 15:07:21.000000000 -0400
+++ ./config.h.in 2009-06-02 14:37:54.000000000 -0400
@@ -253,5 +253,7 @@
# define socklen_t int
#endif
+#undef AFL
+
#endif /* CONFIG_H */
diff -Naur ../tacacs+-F4.0.4.18/configure.in ./configure.in
--- ../tacacs+-F4.0.4.18/configure.in 2009-03-18 15:07:21.000000000 -0400
+++ ./configure.in 2009-06-02 14:53:29.000000000 -0400
@@ -559,6 +559,37 @@
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],
diff -Naur ../tacacs+-F4.0.4.18/parse.c ./parse.c
--- ../tacacs+-F4.0.4.18/parse.c 2009-03-17 14:40:29.000000000 -0400
+++ ./parse.c 2009-06-02 14:37:54.000000000 -0400
@@ -118,6 +118,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 */
diff -Naur ../tacacs+-F4.0.4.18/parse.h ./parse.h
--- ../tacacs+-F4.0.4.18/parse.h 2009-03-18 19:24:54.000000000 -0400
+++ ./parse.h 2009-06-02 14:51:39.000000000 -0400
@@ -90,3 +90,6 @@
# define S_pam 49
#endif
#define S_syslog 50
+#ifdef AFL
+#define S_auth_fail_lock 51
+#endif
diff -Naur ../tacacs+-F4.0.4.18/tac_plus.c ./tac_plus.c
--- ../tacacs+-F4.0.4.18/tac_plus.c 2009-03-18 17:04:29.000000000 -0400
+++ ./tac_plus.c 2009-06-02 15:18:58.000000000 -0400
@@ -99,6 +99,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);
}
@@ -115,6 +119,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);
@@ -124,6 +132,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++;
report(LOG_NOTICE, "Version %s Initialized %d", version, initialised);
@@ -332,6 +345,9 @@
signal(SIGUSR1, handler);
signal(SIGHUP, handler);
signal(SIGTERM, die);
+#ifdef AFL
+ signal(SIGINT, die);
+#endif
signal(SIGPIPE, SIG_IGN);
if (parse_only)
@@ -883,6 +899,9 @@
#if __STDC__
fprintf(stdout, "__STDC__\n");
#endif
+#if AFL
+ fprintf(stdout, "AFL\n");
+#endif
return;
}
diff -Naur ../tacacs+-F4.0.4.18/tac_plus.h ./tac_plus.h
--- ../tacacs+-F4.0.4.18/tac_plus.h 2009-03-18 19:24:54.000000000 -0400
+++ ./tac_plus.h 2009-06-02 14:55:49.000000000 -0400
@@ -149,6 +149,12 @@
# include <fcntl.h>
#endif
+#ifdef AFL
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/sem.h>
+#endif
+
#include "pathsl.h"
#include "md5.h"
@@ -278,6 +284,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? */
@@ -294,6 +308,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 */
@@ -652,6 +669,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 *);
More information about the tac_plus
mailing list