summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Olson <olson@cumulusnetworks.com>2017-03-30 09:42:45 -0700
committerDave Olson <olson@cumulusnetworks.com>2017-05-23 16:39:52 -0700
commitf9f714b3b7b9f77c0165c0850bd816cac0d46292 (patch)
treef98dfa2ad98e4c6a0ceb734d106a0a1eb80fba9c
parent1e18c99eada15bb8efa0ecf0c6600d358f11b48e (diff)
downloadlibnss-tacplus-f9f714b3b7b9f77c0165c0850bd816cac0d46292.tar.gz
libnss-tacplus-f9f714b3b7b9f77c0165c0850bd816cac0d46292.zip
During login from ssh, send remote host IP address in AUTH request
The hack is to run getpeername on fd 0, because during ssh connections, it is a socket from the remote host. This is a bit fragile... Normally fd 0 interactively will be a pty or tty, so getpeername() will fail. There may be some daemons where fd0 is a socket, and returns a local or some other remote IP address, and if so, it could lead to some confusion, but it shouldn't ever break anything. I ran with tshark watching the packet exchange, and verified that the remote address field is set for ssh sessions at the start of the ssh session, and not when run in other uses. The customer ran a 3.2.1 package with this change, and it resolved their issue.
-rw-r--r--debian/changelog3
-rw-r--r--nss_tacplus.c36
2 files changed, 37 insertions, 2 deletions
diff --git a/debian/changelog b/debian/changelog
index fefa524..43d371e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,6 +1,5 @@
libnss-tacplus (1.0.2) unstable; urgency=low
* added config variable "timeout" to limit time attempting to
- connect to non-responding TACACS server.
* added config variable "exclude_users" in /etc/tacplus_nss
to avoid looking up "local" user accounts via TACACS servers. This
improves overall system performance for local users, and avoids significant
@@ -10,6 +9,8 @@ libnss-tacplus (1.0.2) unstable; urgency=low
* Improved debugging messages.
* Minor corrections to Copyright and licensing
* Added vrf config variable, so NSS lookups work correctly$
+ * During login, send remote add IP address in AUTH request
+ connect to non-responding TACACS server.
-- Dave Olson <olson@cumulusnetworks.com> Tue, 07 Mar 2017 12:58:03 -0800
diff --git a/nss_tacplus.c b/nss_tacplus.c
index 4fa652e..2d4f193 100644
--- a/nss_tacplus.c
+++ b/nss_tacplus.c
@@ -36,6 +36,7 @@
#include <ctype.h>
#include <nss.h>
#include <libaudit.h>
+#include <sys/socket.h>
#include <tacplus/libtac.h>
#include <tacplus/map_tacplus_user.h>
@@ -67,12 +68,15 @@ static tacplus_server_t tac_srv[TAC_PLUS_MAXSERVERS];
static int tac_srv_no, tac_key_no;
static char tac_service[] = "shell";
static char tac_protocol[] = "ssh";
+static char tac_rhost[INET6_ADDRSTRLEN];
static char vrfname[64];
static char *exclude_users;
static uid_t min_uid = ~0U; /* largest possible */
static int debug;
static int conf_parsed = 0;
+static void get_remote_addr(void);
+
static int nss_tacplus_config(int *errnop, const char *cfile, int top)
{
FILE *conf;
@@ -531,7 +535,7 @@ lookup_tacacs_user(struct pwbuf *pb)
tac_ntop(tac_srv[srvr].addr->ai_addr) : "unknown", tac_fd);
continue;
}
- ret = tac_author_send(tac_fd, pb->name, "", "", attr);
+ ret = tac_author_send(tac_fd, pb->name, "", tac_rhost, attr);
if(ret < 0) {
if(debug)
syslog(LOG_WARNING, "%s: TACACS+ server %s send failed (%d) for"
@@ -621,6 +625,8 @@ enum nss_status _nss_tacplus_getpwnam_r(const char *name, struct passwd *pw,
result = nss_tacplus_config(errnop, config_file, 1);
conf_parsed = result == 0 ? 2 : 1;
+ get_remote_addr();
+
if(result) { /* no config file, no servers, etc. */
/* this is a debug because privileges may not allow access */
if(debug)
@@ -730,3 +736,31 @@ enum nss_status _nss_tacplus_getpwuid_r(uid_t uid, struct passwd *pw,
status = NSS_STATUS_SUCCESS;
return status;
}
+
+static void get_remote_addr(void)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof addr;
+ char ipstr[INET6_ADDRSTRLEN];
+
+ /* This is so we can fill in the rhost field when we talk to the
+ * TACACS+ server, when it's an ssh connection, so sites that refuse
+ * authorization unless from specific IP addresses will get that
+ * information. It's pretty much of a hack, but it works.
+ */
+ if (getpeername(0, (struct sockaddr*)&addr, &len) == -1)
+ return;
+
+ *ipstr = 0;
+ if (addr.ss_family == AF_INET) {
+ struct sockaddr_in *s = (struct sockaddr_in *)&addr;
+ inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
+ } else {
+ struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
+ inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
+ }
+
+ snprintf(tac_rhost, sizeof tac_rhost, "%s", ipstr);
+ if(debug > 1 && tac_rhost[0])
+ syslog(LOG_DEBUG, "%s: rhost=%s", nssname, tac_rhost);
+}