Created
June 25, 2018 17:23
-
-
Save helpdeskdan/1358d005ae1277bffdf67d66d956d919 to your computer and use it in GitHub Desktop.
Tacacs AFL Patch for 4.0.4.28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- ../../tacacs-F4.0.4.28/authen.c 2014-07-31 14:09:47.000000000 -0600 | |
| +++ ./authen.c 2018-05-02 13:12:24.316818539 -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-02 14:44:25.552735124 -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(); | |
| @@ -2468,6 +2510,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-02 19:22:56.173568266 -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-02 13:12:24.368819349 -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-02 13:12:24.376819474 -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-02 13:12:24.384819598 -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-02 13:12:24.392819723 -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-02 13:12:24.396819785 -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-02 13:12:24.364819287 -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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment