summaryrefslogtreecommitdiff
path: root/nss_tacplus.c
diff options
context:
space:
mode:
authorDave Olson <olson@cumulusnetworks.com>2016-09-30 13:53:33 -0700
committerDave Olson <olson@cumulusnetworks.com>2016-10-06 14:20:11 -0700
commit600f2be7da9c70fa416888c6d29fe94e5276a477 (patch)
tree40dc1b861d4a62ba5628308abe113c787b698d36 /nss_tacplus.c
parented775fc39f8603bd12fbad76d799ac29cda3a046 (diff)
downloadlibnss-tacplus-600f2be7da9c70fa416888c6d29fe94e5276a477.tar.gz
libnss-tacplus-600f2be7da9c70fa416888c6d29fe94e5276a477.zip
Better debugs for server, and try all servers in list
Ticket: CM-13049 Reviewed By: olson Testing Done: tried multiple servers. Debugging a customer issue was harder than it should be, so add more debugging on success and invalid user returns from server. Also try all servers in the list until success, because different servers can have different databases, so an invalid user return from one server should not be considered definitive.
Diffstat (limited to 'nss_tacplus.c')
-rw-r--r--nss_tacplus.c119
1 files changed, 70 insertions, 49 deletions
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