Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| diff -Naur ../tacacs+-F4.0.4.19.orig/authen.c ./authen.c | |
| --- ../tacacs+-F4.0.4.19.orig/authen.c 2009-07-17 13:34:32.000000000 -0400 | |
| +++ ./authen.c 2009-09-10 15:17:32.000000000 -0400 | |
| @@ -316,10 +316,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) { | |
| default: | |
| @@ -345,6 +356,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, | |
| diff -Naur ../tacacs+-F4.0.4.19.orig/config.c ./config.c | |
| --- ../tacacs+-F4.0.4.19.orig/config.c 2009-07-17 13:34:30.000000000 -0400 | |
| +++ ./config.c 2009-09-10 15:17:32.000000000 -0400 | |
| @@ -114,6 +114,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. | |
| @@ -178,6 +183,9 @@ | |
| #ifdef MAXSESS | |
| int maxsess; /* Max sessions/user */ | |
| #endif /* MAXSESS */ | |
| +#ifdef AFL | |
| + int shm_offset; | |
| +#endif | |
| } USER; | |
| #ifdef ACLS | |
| @@ -213,6 +221,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 | |
| @@ -521,6 +539,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++) { | |
| @@ -746,7 +772,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(); | |
| @@ -2433,6 +2475,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.19.orig/config.h.in ./config.h.in | |
| --- ../tacacs+-F4.0.4.19.orig/config.h.in 2009-07-17 16:02:18.000000000 -0400 | |
| +++ ./config.h.in 2009-09-10 15:17:32.000000000 -0400 | |
| @@ -288,5 +288,7 @@ | |
| # define socklen_t int | |
| #endif | |
| +#undef AFL | |
| + | |
| #endif /* CONFIG_H */ | |
| diff -Naur ../tacacs+-F4.0.4.19.orig/configure.in ./configure.in | |
| --- ../tacacs+-F4.0.4.19.orig/configure.in 2009-07-17 13:54:16.000000000 -0400 | |
| +++ ./configure.in 2009-09-10 15:17:32.000000000 -0400 | |
| @@ -620,6 +620,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.19.orig/parse.c ./parse.c | |
| --- ../tacacs+-F4.0.4.19.orig/parse.c 2009-07-17 13:34:32.000000000 -0400 | |
| +++ ./parse.c 2009-09-10 15:17:32.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.19.orig/parse.h ./parse.h | |
| --- ../tacacs+-F4.0.4.19.orig/parse.h 2009-07-17 13:34:32.000000000 -0400 | |
| +++ ./parse.h 2009-09-10 15:17:32.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.19.orig/tac_plus.c ./tac_plus.c | |
| --- ../tacacs+-F4.0.4.19.orig/tac_plus.c 2009-07-28 11:50:24.000000000 -0400 | |
| +++ ./tac_plus.c 2009-09-10 15:17:32.000000000 -0400 | |
| @@ -79,6 +79,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); | |
| } | |
| @@ -95,6 +99,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); | |
| @@ -104,6 +112,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); | |
| @@ -336,6 +349,9 @@ | |
| signal(SIGUSR1, handler); | |
| signal(SIGHUP, handler); | |
| signal(SIGTERM, die); | |
| +#ifdef AFL | |
| + signal(SIGINT, die); | |
| +#endif | |
| signal(SIGPIPE, SIG_IGN); | |
| if (parse_only) | |
| @@ -911,6 +927,9 @@ | |
| #if __STDC__ | |
| fprintf(stdout, "__STDC__\n"); | |
| #endif | |
| +#if AFL | |
| + fprintf(stdout, "AFL\n"); | |
| +#endif | |
| return; | |
| } | |
| diff -Naur ../tacacs+-F4.0.4.19.orig/tac_plus.h ./tac_plus.h | |
| --- ../tacacs+-F4.0.4.19.orig/tac_plus.h 2009-07-27 20:11:53.000000000 -0400 | |
| +++ ./tac_plus.h 2009-09-10 15:18:33.000000000 -0400 | |
| @@ -153,6 +153,12 @@ | |
| #include "pathsl.h" | |
| #include "md5.h" | |
| +#ifdef AFL | |
| +#include <sys/ipc.h> | |
| +#include <sys/shm.h> | |
| +#include <sys/sem.h> | |
| +#endif | |
| + | |
| /* | |
| * You probably shouldn't be changing much below this line unless you really | |
| * know what you are doing. | |
| @@ -272,6 +278,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? */ | |
| @@ -290,6 +304,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 */ | |
| @@ -606,6 +623,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 *); |