diff options
-rw-r--r-- | debian/changelog | 8 | ||||
-rw-r--r-- | nss_tacplus.c | 119 |
2 files changed, 78 insertions, 49 deletions
diff --git a/debian/changelog b/debian/changelog index a9b433b..3a366d1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +libnss-tacplus (1.0.2-1) unstable; urgency=low + + * Improve debugging on server connections, and always try all + servers in list until successful response, in case different + servers have different user databaes. + + -- Dave Olson <olson@cumulusnetworks.com> Fri, 30 Sep 2016 13:56:05 -0700 + libnss-tacplus (1.0.1-1) unstable; urgency=low * Initial version with NSS lookups for tacacs users using mapping diff --git a/nss_tacplus.c b/nss_tacplus.c index 3fbb4f1..d401bcb 100644 --- a/nss_tacplus.c +++ b/nss_tacplus.c @@ -173,8 +173,9 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) (*tac_service ? "server" : "service and no server")); for(n = 0; debug && n < tac_srv_no; n++) - syslog(LOG_DEBUG, "%s: server[%d] { addr=%s, key='%s' }", - nssname, n, tac_ntop(tac_srv[n].addr->ai_addr), tac_srv[n].key); + syslog(LOG_DEBUG, "%s: server[%d] { addr=%s, key='%s' }", nssname, + n, tac_srv[n].addr ? tac_ntop(tac_srv[n].addr->ai_addr) + : "unknown", tac_srv[n].key); } return 0; @@ -402,78 +403,98 @@ got_tacacs_user(struct tac_attrib *attr, struct pwbuf *pb) } /* - * find the first responding tacacs server, and return the fd. - * Since we may be looking up multiple users, we leave the connection open, - * once found. + * Attempt to connect to the requested tacacs server. * Returns fd for connection, or -1 on failure */ + static int -connect_tacacs(struct tac_attrib **attr) +connect_tacacs(struct tac_attrib **attr, int srvr) { - int srvr, fd; + int fd; if(!*tac_service) /* reported at config file processing */ return -1; - for(srvr = 0; srvr < tac_srv_no; srvr++) { - fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, NULL); - if(fd >= 0) { - *attr = NULL; /* so tac_add_attr() allocates memory */ - tac_add_attrib(attr, "service", tac_service); - if(tac_protocol[0]) - tac_add_attrib(attr, "protocol", tac_protocol); - /* empty cmd is required, at least for linux tac_plus */ - tac_add_attrib(attr, "cmd", ""); - return fd; - } + + fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, NULL); + if(fd >= 0) { + *attr = NULL; /* so tac_add_attr() allocates memory */ + tac_add_attrib(attr, "service", tac_service); + if(tac_protocol[0]) + tac_add_attrib(attr, "protocol", tac_protocol); + /* empty cmd is required, at least for linux tac_plus */ + tac_add_attrib(attr, "cmd", ""); } - return -1; + return fd; } /* * lookup the user on a TACACS server. Returns 0 on successful lookup, else 1 * - * We have to make a new connection each time, because libtac is single threaded - * (doesn't support multiple connects at the same time due to use of globals)), + * Make a new connection each time, because libtac is single threaded and + * doesn't support multiple connects at the same time due to use of globals, * and doesn't have support for persistent connections. That's fixable, but * not worth the effort at this point. + * Step through all servers until success or end of list, because different + * servers can have different databases. */ static int lookup_tacacs_user(struct pwbuf *pb) { struct areply arep; - int ret; + int ret = 1, done = 0; struct tac_attrib *attr; - int tac_fd; - - if((tac_fd = connect_tacacs(&attr)) == -1) - return 1; + int tac_fd, srvr; + + for(srvr=0; srvr < tac_srv_no && !done; srvr++) { + arep.msg = NULL; + arep.attr = NULL; + arep.status = TAC_PLUS_AUTHOR_STATUS_ERROR; /* if author_send fails */ + tac_fd = connect_tacacs(&attr, srvr); + if (tac_fd < 0) { + if(debug) + syslog(LOG_WARNING, "%s: failed to connect TACACS+ server %s," + " ret=%d: %m", nssname, tac_srv[srvr].addr ? + tac_ntop(tac_srv[srvr].addr->ai_addr) : "unknown", tac_fd); + continue; + } + ret = tac_author_send(tac_fd, pb->name, "", "", attr); + if(ret < 0) { + if(debug) + syslog(LOG_WARNING, "%s: TACACS+ server %s send failed (%d) for" + " user %s: %m", nssname, + tac_ntop(tac_srv[srvr].addr->ai_addr), ret, pb->name); + } - ret = tac_author_send(tac_fd, pb->name, "", "", attr); - if(ret < 0) { - if(debug) - syslog(LOG_WARNING, "%s: TACACS+ send failed (%d) for [%s]: %m", - nssname, ret, pb->name); + tac_free_attrib(&attr); + close(tac_fd); + if(ret < 0) + continue; + + if(arep.status == AUTHOR_STATUS_PASS_ADD || + arep.status == AUTHOR_STATUS_PASS_REPL) { + ret = got_tacacs_user(arep.attr, pb); + if(debug) + syslog(LOG_DEBUG, "%s: TACACS+ server %s successful for user %s." + " got_user=%d", nssname, + tac_ntop(tac_srv[srvr].addr->ai_addr), pb->name, ret); + done = 1; /* break out of loop after arep cleanup */ + } + else { + ret = 1; /* in case last server */ + if(debug) + syslog(LOG_DEBUG, "%s: TACACS+ server %s replies user %s" + " invalid (%d)", nssname, + tac_ntop(tac_srv[srvr].addr->ai_addr), pb->name, + arep.status); + } + if(arep.msg) + free(arep.msg); + if(arep.attr) /* free returned attributes */ + tac_free_attrib(&arep.attr); } - else - tac_author_read(tac_fd, &arep); - - tac_free_attrib(&attr); - close(tac_fd); - if(ret < 0) - return 1; - - if(arep.status == AUTHOR_STATUS_PASS_ADD || - arep.status == AUTHOR_STATUS_PASS_REPL) - ret = got_tacacs_user(arep.attr, pb); - else - ret = 1; - if(arep.msg) - free(arep.msg); - if(arep.attr) /* free returned attributes */ - tac_free_attrib(&arep.attr); - return ret; + return ret < 0? 1 : ret; } static int |