From 2f51096c0765e52afd109ea81f6fa0fe1b35cb61 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Fri, 21 Oct 2016 16:01:03 -0700 Subject: Updated COPYING, corrected copyrights --- debian/copyright | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'debian') diff --git a/debian/copyright b/debian/copyright index 0cca9f3..48bfa52 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,8 +3,9 @@ Upstream-Name: libnss-tacplus Source: http://www.cumulusnetworks.com Files: * -Copyright: 2010 Pawel Krawczyk and Jeroen Nijhof , - 2015, 2016 Cumulus Networks, Inc. +Copyright: 2015, 2016 Cumulus Networks, Inc.. All rights reserved., + 2010 Pawel Krawczyk and Jeroen Nijhof + License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -- cgit v1.2.3 From f38818cdcec8f1f4b0ded6d74d49835d10d3e24e Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Wed, 26 Oct 2016 14:51:53 -0700 Subject: Fixed trailing line whitespace issues --- debian/copyright | 1 - nss_tacplus.c | 29 ++++++++++++++--------------- nss_tacplus.h | 2 +- 3 files changed, 15 insertions(+), 17 deletions(-) (limited to 'debian') diff --git a/debian/copyright b/debian/copyright index 48bfa52..6d103c9 100644 --- a/debian/copyright +++ b/debian/copyright @@ -5,7 +5,6 @@ Source: http://www.cumulusnetworks.com Files: * Copyright: 2015, 2016 Cumulus Networks, Inc.. All rights reserved., 2010 Pawel Krawczyk and Jeroen Nijhof - License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/nss_tacplus.c b/nss_tacplus.c index b82613e..4c62a82 100644 --- a/nss_tacplus.c +++ b/nss_tacplus.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2014, 2015, 2016 Cumulus Networks, Inc. All rights reserved. * Author: Dave Olson - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -85,7 +85,7 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) *errnop = errno; if(!conf_parsed && debug) /* debug because privileges may not allow */ syslog(LOG_DEBUG, "%s: can't open config file %s: %m", - nssname, cfile); + nssname, cfile); goto err; } @@ -101,7 +101,7 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) if(lbuf[8]) /* else treat as empty config, ignoring errors */ (void)nss_tacplus_config(errnop, &lbuf[8], top+1); } - else if(!strncmp(lbuf, "debug=", 6)) + else if(!strncmp(lbuf, "debug=", 6)) debug = strtoul(lbuf+6, NULL, 0); /* * This next group is here to prevent a warning in the @@ -123,7 +123,7 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) } } else if(!strncmp(lbuf, "exclude_users=", 14)) { - /* + /* * Don't lookup users in this comma-separated list for both * robustness and performnce. Typically root and other commonly * used local users. If set, we also look up the uids @@ -228,7 +228,7 @@ pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, if(!usename) usename = srcpw->pw_name; - needlen = usename ? strlen(usename) + 1 : 1 + + needlen = usename ? strlen(usename) + 1 : 1 + srcpw->pw_dir ? strlen(srcpw->pw_dir) + 1 : 1 + srcpw->pw_gecos ? strlen(srcpw->pw_gecos) + 1 : 1 + srcpw->pw_shell ? strlen(srcpw->pw_shell) + 1 : 1 + @@ -396,7 +396,7 @@ find_pw_user(const char *logname, const char *tacuser, struct pwbuf *pb) * We could optimize this for programs that do lots of lookups by leaving * the passwd file open and rewinding, but it doesn't seem worthwhile. */ -static bool +static bool lookup_local(char *name, uid_t uid) { FILE *pwfile; @@ -504,7 +504,7 @@ lookup_tacacs_user(struct pwbuf *pb) if (list) { bool islocal = 0; user = strtok(list, ","); - list = NULL; + list = NULL; while (user && !strcmp(user, pb->name)) { if(debug) syslog(LOG_DEBUG, "%s: check user=(%s)", nssname, user); @@ -555,7 +555,7 @@ lookup_tacacs_user(struct pwbuf *pb) tac_ntop(tac_srv[srvr].addr->ai_addr), ret, pb->name); } - tac_free_attrib(&attr); + tac_free_attrib(&attr); close(tac_fd); if(ret < 0) continue; @@ -565,7 +565,7 @@ lookup_tacacs_user(struct pwbuf *pb) ret = got_tacacs_user(arep.attr, pb); if(debug) syslog(LOG_DEBUG, "%s: TACACS+ server %s successful for user %s." - " local lookup %s", nssname, + " local lookup %s", nssname, tac_ntop(tac_srv[srvr].addr->ai_addr), pb->name, ret?"OK":"no match"); done = 1; /* break out of loop after arep cleanup */ @@ -574,7 +574,7 @@ lookup_tacacs_user(struct pwbuf *pb) ret = 1; /* in case last server */ if(debug) syslog(LOG_DEBUG, "%s: TACACS+ server %s replies user %s" - " invalid (%d)", nssname, + " invalid (%d)", nssname, tac_ntop(tac_srv[srvr].addr->ai_addr), pb->name, arep.status); } @@ -583,7 +583,7 @@ lookup_tacacs_user(struct pwbuf *pb) if(arep.attr) /* free returned attributes */ tac_free_attrib(&arep.attr); } - + return ret < 0? 1 : ret; } @@ -601,14 +601,14 @@ lookup_mapped_uid(struct pwbuf *pb, uid_t uid, uid_t auid, int session) } /* - * This is an NSS entry point. + * This is an NSS entry point. * We implement getpwnam(), because we remap from the tacacs login * to the local tacacs0 ... tacacs15 users for all other info, and so * the normal order of "passwd tacplus" (possibly with ldap or anything * else prior to tacplus) will mean we only get used when there isn't * a local user to be found. * - * We try the lookup to the tacacs server first. If we can't make a + * We try the lookup to the tacacs server first. If we can't make a * connection to the server for some reason, we also try looking up * the account name via the mapping file, primarily to handle cases * where we aren't running with privileges to read the tacacs configuration @@ -657,14 +657,13 @@ enum nss_status _nss_tacplus_getpwnam_r(const char *name, struct passwd *pw, char *mapname = lookup_mapname(name, -1, -1, NULL); if(mapname != name && !find_pw_user(name, mapname, &pbuf)) status = NSS_STATUS_SUCCESS; - } } return status; } /* - * This is an NSS entry point. + * This is an NSS entry point. * We implement getpwuid(), for anything that wants to get the original * login name from the uid. * If it matches an entry in the map, we use that data to replace diff --git a/nss_tacplus.h b/nss_tacplus.h index 72a1fb1..25e73e7 100644 --- a/nss_tacplus.h +++ b/nss_tacplus.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2014, 2015, 2016 Cumulus Networks, Inc. All rights reserved. - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or -- cgit v1.2.3 From b2cad0cf995560ea689be75d149e0fa030e2fe35 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Mon, 31 Oct 2016 13:04:42 -0700 Subject: Fixed copyright punctuation error --- debian/copyright | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'debian') diff --git a/debian/copyright b/debian/copyright index 6d103c9..9b1b34a 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,7 +3,7 @@ Upstream-Name: libnss-tacplus Source: http://www.cumulusnetworks.com Files: * -Copyright: 2015, 2016 Cumulus Networks, Inc.. All rights reserved., +Copyright: 2015, 2016 Cumulus Networks, Inc. All rights reserved., 2010 Pawel Krawczyk and Jeroen Nijhof License: GPL-2+ This package is free software; you can redistribute it and/or modify -- cgit v1.2.3 From 490882de7069623f427663340b27c77b97fecd40 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Tue, 29 Nov 2016 17:01:03 -0800 Subject: Updated changelog --- debian/changelog | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'debian') diff --git a/debian/changelog b/debian/changelog index d6199b2..ad8a1e1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,17 @@ +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 + delays when a TACACS server is unreachable. + * Fixed issues with ordering of multiple servers and secrets in config files. + libraries can connect to a TACACS+ server without being tacacs aware. + * Improved debugging messages. + * Minor corrections to Copyright and licensing + + -- Dave Olson Tue, 29 Nov 2016 16:55:16 -0800 + libnss-tacplus (1.0.2-1) unstable; urgency=low * Improve debugging on server connections, and always try all -- cgit v1.2.3 From 52aa2d434ed03f0a386eb3bb6a12cb83b0c005c6 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Tue, 7 Mar 2017 12:59:42 -0800 Subject: Add support for mgmt vrf When management vrf is enabled and vrf is present in the tacacs config, if we are unable to reach any configured tacacs server, try setting vrf context on the socket. Previously libnss-tacplus worked only with ssh@mgmt, now works with normal ssh in mgmt vrf Setting via the socket (rather than vrf context) is required so we don't set the VRF context for arbitrary processes that do uid or username lookups. --- debian/changelog | 3 ++- debian/control | 7 +++++-- nss_tacplus.c | 7 +++++-- 3 files changed, 12 insertions(+), 5 deletions(-) (limited to 'debian') diff --git a/debian/changelog b/debian/changelog index ad8a1e1..fefa524 100644 --- a/debian/changelog +++ b/debian/changelog @@ -9,8 +9,9 @@ libnss-tacplus (1.0.2) unstable; urgency=low libraries can connect to a TACACS+ server without being tacacs aware. * Improved debugging messages. * Minor corrections to Copyright and licensing + * Added vrf config variable, so NSS lookups work correctly$ - -- Dave Olson Tue, 29 Nov 2016 16:55:16 -0800 + -- Dave Olson Tue, 07 Mar 2017 12:58:03 -0800 libnss-tacplus (1.0.2-1) unstable; urgency=low diff --git a/debian/control b/debian/control index 3d95156..ea65d0b 100644 --- a/debian/control +++ b/debian/control @@ -1,14 +1,17 @@ Source: libnss-tacplus Priority: optional Maintainer: Dave Olson -Build-Depends: debhelper (>= 9), autotools-dev, libtac-dev, libtacplus-map-dev, libaudit-dev, autoconf, libpam-tacplus-dev, dpkg-dev (>= 1.16.1) +Build-Depends: debhelper (>= 9), autotools-dev, libtac-dev (>= 1.4.1~), + libtacplus-map-dev, libaudit-dev, autoconf, libpam-tacplus-dev, + dpkg-dev (>= 1.16.1), git Section: libs Standards-Version: 3.9.6 Homepage: http://www.cumulusnetworks.com Package: libnss-tacplus Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, libtac2, libtacplus-map1, libaudit1 +Depends: ${shlibs:Depends}, ${misc:Depends}, libtac2 (>= 1.4.1~), + libtacplus-map1, libaudit1 Description: NSS module for TACACS+ authentication without local passwd entry Performs getpwname and getpwuid lookups via NSS for users logged in via tacacs authentication, and mapping done with libtacplus_map diff --git a/nss_tacplus.c b/nss_tacplus.c index 75cbdb7..635327a 100644 --- a/nss_tacplus.c +++ b/nss_tacplus.c @@ -67,6 +67,7 @@ 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 vrfname[64]; static char *exclude_users; static uid_t min_uid = ~0U; /* largest possible */ static int debug; @@ -157,6 +158,8 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) if (valid > (lbuf+8)) min_uid = (uid_t)uid; } + else if(!strncmp(lbuf, "vrf=", 4)) + strncpy(vrfname, lbuf + 4, sizeof(vrfname)); else if(!strncmp(lbuf, "server=", 7)) { if(tac_srv_no < TAC_PLUS_MAXSERVERS) { struct addrinfo hints, *servers, *server; @@ -454,7 +457,8 @@ connect_tacacs(struct tac_attrib **attr, int srvr) if(!*tac_service) /* reported at config file processing */ return -1; - fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, NULL); + fd = tac_connect_single(tac_srv[srvr].addr, tac_srv[srvr].key, NULL, + vrfname[0]?vrfname:NULL); if(fd >= 0) { *attr = NULL; /* so tac_add_attr() allocates memory */ tac_add_attrib(attr, "service", tac_service); @@ -505,7 +509,6 @@ lookup_tacacs_user(struct pwbuf *pb) return 2; } } - for(srvr=0; srvr < tac_srv_no && !done; srvr++) { arep.msg = NULL; arep.attr = NULL; -- cgit v1.2.3 From f9f714b3b7b9f77c0165c0850bd816cac0d46292 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Thu, 30 Mar 2017 09:42:45 -0700 Subject: 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. --- debian/changelog | 3 ++- nss_tacplus.c | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'debian') 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 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 #include #include +#include #include #include @@ -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); +} -- cgit v1.2.3 From dab6c3bb9feb10b67f08b18656fe24d1f7b01d2b Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Fri, 12 May 2017 11:43:01 -0700 Subject: Track changes to config files, and reparse if any change This is done to handle the case where nss_tacplus.so is included in a long-lived daemon. It's desirable to have long-lived daemons reflect changes to the configuration, both to enable/disable debugging, and particularly if the server list or key changes. Clear all read config variables to defaults when re-parsing. This is complicated by nested configuration files via the include directive. At top level, we need to check all the previously used configuration files to see if any have changed. This also adds a limitation to no more than 8 deep include nesting. In practice, > 2 is going to be very rare, so it should be OK. Log a message when we re-initialize (without using debug qualifier). --- debian/changelog | 4 ++- debian/copyright | 5 ++-- nss_tacplus.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 9 deletions(-) (limited to 'debian') diff --git a/debian/changelog b/debian/changelog index 43d371e..cf33b24 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -libnss-tacplus (1.0.2) unstable; urgency=low +libnss-tacplus (1.0.3-1) unstable; urgency=low * added config variable "timeout" to limit time attempting to * added config variable "exclude_users" in /etc/tacplus_nss to avoid looking up "local" user accounts via TACACS servers. This @@ -11,6 +11,8 @@ libnss-tacplus (1.0.2) unstable; urgency=low * 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. + * configuration files should automatically be reparsed + if they change, for long-lived programs and daemons that use NSS. -- Dave Olson Tue, 07 Mar 2017 12:58:03 -0800 diff --git a/debian/copyright b/debian/copyright index 9b1b34a..710851e 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,8 +3,9 @@ Upstream-Name: libnss-tacplus Source: http://www.cumulusnetworks.com Files: * -Copyright: 2015, 2016 Cumulus Networks, Inc. All rights reserved., - 2010 Pawel Krawczyk and Jeroen Nijhof +Copyright: 2015, 2016, 2017 Cumulus Networks, Inc. All rights reserved., + 2010 Pawel Krawczyk and + Jeroen Nijhof License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/nss_tacplus.c b/nss_tacplus.c index 2d4f193..1cf99c5 100644 --- a/nss_tacplus.c +++ b/nss_tacplus.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2014, 2015, 2016 Cumulus Networks, Inc. All rights reserved. + * Copyright (C) 2014, 2015, 2016, 2017 Cumulus Networks, Inc. + * All rights reserved. * Author: Dave Olson * * This program is free software; you can redistribute it and/or modify @@ -37,6 +38,7 @@ #include #include #include +#include #include #include @@ -60,7 +62,7 @@ struct pwbuf { typedef struct { struct addrinfo *addr; - const char *key; + char *key; } tacplus_server_t; /* set from configuration file parsing */ @@ -77,14 +79,77 @@ static int conf_parsed = 0; static void get_remote_addr(void); +#define MAX_INCL 8 /* max config level nesting */ + +/* reset all config variables when we are going to re-parse */ +static void +reset_config(void) +{ + int i, nservers; + + /* reset the config variables that we use, freeing memory where needed */ + nservers = tac_srv_no; + tac_srv_no = 0; + tac_key_no = 0; + vrfname[0] = '\0'; + if(exclude_users[0]) + (void)free(exclude_users); + exclude_users = NULL; + debug = 0; + use_tachome = 0; + tac_timeout = 0; + min_uid = ~0U; + + for(i = 0; i < nservers; i++) { + if(tac_srv[i].key) { + free(tac_srv[i].key); + tac_srv[i].key = NULL; + } + tac_srv[i].addr = NULL; + } +} + static int nss_tacplus_config(int *errnop, const char *cfile, int top) { FILE *conf; char lbuf[256]; + static struct stat lastconf[MAX_INCL]; + static char *cfilelist[MAX_INCL]; + struct stat st, *lst; + + if(top > MAX_INCL) { + syslog(LOG_NOTICE, "%s: Config file include depth > %d, ignoring %s", + nssname, MAX_INCL, cfile); + return 1; + } - if(conf_parsed > 1) /* 1: we've tried and thrown errors, 2, OK */ - return 0; + lst = &lastconf[top-1]; + if(conf_parsed && top == 1) { + /* + * check to see if the config file(s) have changed since last time, + * in case we are part of a long-lived daemon. If any changed, + * reparse. If not, return the appropriate status (err or OK) + * This is somewhat complicated by the include file mechanism. + * When we have nested includes, we have to check all the config + * files we saw previously, not just the top level config file. + */ + int i; + for(i=0; i < MAX_INCL; i++) { + struct stat *cst; + cst = &lastconf[i]; + if(!cst->st_ino || !cfilelist[i]) /* end of files */ + return conf_parsed == 2 ? 0 : 1; + if (stat(cfilelist[i], &st) || st.st_ino != cst->st_ino || + st.st_mtime != cst->st_mtime || st.st_ctime != cst->st_ctime) + break; /* found removed or different file, so re-parse */ + } + reset_config(); + syslog(LOG_NOTICE, "%s: Configuration file(s) have changed, re-initializing", + nssname); + } + /* don't check for failures, we'll just skip, don't want to error out */ + cfilelist[top-1] = strdup(cfile); conf = fopen(cfile, "r"); if(conf == NULL) { *errnop = errno; @@ -93,6 +158,8 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) nssname, cfile); return 1; } + if (fstat(fileno(conf), lst) != 0) + memset(lst, 0, sizeof *lst); /* avoid stale data, no warning */ while(fgets(lbuf, sizeof lbuf, conf)) { if(*lbuf == '#' || isspace(*lbuf)) @@ -101,9 +168,10 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) if(!strncmp(lbuf, "include=", 8)) { /* * allow include files, useful for centralizing tacacs - * server IP address and secret. + * server IP address and secret. When running non-privileged, + * may not be able to read one or more config files. */ - if(lbuf[8]) /* else treat as empty config, ignoring errors */ + if(lbuf[8]) (void)nss_tacplus_config(errnop, &lbuf[8], top+1); } else if(!strncmp(lbuf, "debug=", 6)) -- cgit v1.2.3 From 9b056a2a66ec7006d86121509ef1049c7f6f0725 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Thu, 23 Mar 2017 22:42:24 -0700 Subject: Support using and returning per-tacacs user homedir Get setting from map on whether login was set up to use per-tacacs user homedir, rather than the homedir from the local tacacsN users. The mkhomedir_helper program is used in pam_tacplus to create home directory (like pam_mkhomedir.so) when user homedir is requested, but the home directory does not exist. The config file setting in this code is not used when using map and the user is found in map; we then use the setting from the map. When mapping doesn't exist, then use our own config setting. user_homedirs is ignored if shell is a restricted shell (as set up by tacplus-restrict) because we need to honor the per-command authorization setup in that case. Updated changelog Also fixed up the spelling of dev-support --- debian/changelog | 12 ++++++----- nss_tacplus.c | 65 ++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 19 deletions(-) (limited to 'debian') diff --git a/debian/changelog b/debian/changelog index cf33b24..6b20592 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ libnss-tacplus (1.0.3-1) unstable; urgency=low - * added config variable "timeout" to limit time attempting to - * added config variable "exclude_users" in /etc/tacplus_nss + * 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 delays when a TACACS server is unreachable. @@ -10,11 +11,12 @@ libnss-tacplus (1.0.3-1) unstable; urgency=low * 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. - * configuration files should automatically be reparsed + * Configuration files should automatically be reparsed if they change, for long-lived programs and daemons that use NSS. + * Added user_homedir config variable to allow per-user + home directories (unless per-command authorization is enabled) - -- Dave Olson Tue, 07 Mar 2017 12:58:03 -0800 + -- Dave Olson Thu, 23 Mar 2017 22:40:01 -0800 libnss-tacplus (1.0.2-1) unstable; urgency=low diff --git a/nss_tacplus.c b/nss_tacplus.c index 60a221f..201b329 100644 --- a/nss_tacplus.c +++ b/nss_tacplus.c @@ -75,6 +75,7 @@ static char vrfname[64]; static char *exclude_users; static uid_t min_uid = ~0U; /* largest possible */ static int debug; +uint16_t use_tachome; static int conf_parsed = 0; static void get_remote_addr(void); @@ -176,6 +177,8 @@ static int nss_tacplus_config(int *errnop, const char *cfile, int top) } else if(!strncmp(lbuf, "debug=", 6)) debug = strtoul(lbuf+6, NULL, 0); + else if (!strncmp (lbuf, "user_homedir=", 13)) + use_tachome = (uint16_t)strtoul(lbuf+13, NULL, 0); else if (!strncmp (lbuf, "timeout=", 8)) { tac_timeout = (int)strtoul(lbuf+8, NULL, 0); if (tac_timeout < 0) /* explict neg values disable poll() use */ @@ -319,12 +322,15 @@ static void print_servers(void) */ static int pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, - const char *usename) + const char *usename, uint16_t tachome) { - int needlen, cnt; + int needlen, cnt, origlen = len; + char *shell; - if(!usename) + if(!usename) { usename = srcpw->pw_name; + tachome = 0; /* early lookups; no tachome */ + } needlen = usename ? strlen(usename) + 1 : 1 + srcpw->pw_dir ? strlen(srcpw->pw_dir) + 1 : 1 + @@ -353,6 +359,14 @@ pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, len -= cnt; cnt = snprintf(buf, len, "%s", srcpw->pw_shell ? srcpw->pw_shell : ""); destpw->pw_shell = buf; + shell = strrchr(buf, '/'); + shell = shell ? shell+1 : buf; + if (tachome && *shell == 'r') { + tachome = 0; + if(debug > 1) + syslog(LOG_DEBUG, "%s tacacs login %s with user_homedir not allowed; " + "shell is %s", nssname, srcpw->pw_name, buf); + } cnt++; buf += cnt; len -= cnt; @@ -361,11 +375,28 @@ pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, cnt++; buf += cnt; len -= cnt; - cnt = snprintf(buf, len, "%s", srcpw->pw_dir ? srcpw->pw_dir : ""); + if (tachome && usename) { + char *slash, dbuf[strlen(srcpw->pw_dir) + strlen(usename)]; + snprintf(dbuf, sizeof dbuf, "%s", srcpw->pw_dir ? srcpw->pw_dir : ""); + slash = strrchr(dbuf, '/'); + if (slash) { + slash++; + snprintf(slash, sizeof dbuf - (slash-dbuf), "%s", usename); + } + cnt = snprintf(buf, len, "%s", dbuf); + } + else + cnt = snprintf(buf, len, "%s", srcpw->pw_dir ? srcpw->pw_dir : ""); destpw->pw_dir = buf; cnt++; buf += cnt; len -= cnt; + if(len < 0) { + if(debug) + syslog(LOG_DEBUG, "%s provided password buffer too small (%ld<%d)", + nssname, (long)origlen, origlen-(int)len); + return 1; + } return 0; } @@ -413,11 +444,11 @@ recheck: if(!ent->pw_name) continue; /* shouldn't happen */ if(!strcmp(ent->pw_name, pb->name)) { - retu = pwcopy(ubuf, sizeof(ubuf), ent, &upw, NULL); + retu = pwcopy(ubuf, sizeof(ubuf), ent, &upw, NULL, use_tachome); matches++; } else if(!strcmp(ent->pw_name, tacuser)) { - rett = pwcopy(tbuf, sizeof(tbuf), ent, &tpw, NULL); + rett = pwcopy(tbuf, sizeof(tbuf), ent, &tpw, NULL, use_tachome); matches++; } } @@ -433,9 +464,11 @@ recheck: syslog(LOG_DEBUG, "%s: local user not found at privilege=%u," " using %s", nssname, origpriv, tacuser); if(upw.pw_name && !retu) - ret = pwcopy(pb->buf, pb->buflen, &upw, pb->pw, pb->name); + ret = pwcopy(pb->buf, pb->buflen, &upw, pb->pw, pb->name, + use_tachome); else if(tpw.pw_name && !rett) - ret = pwcopy(pb->buf, pb->buflen, &tpw, pb->pw, pb->name); + ret = pwcopy(pb->buf, pb->buflen, &tpw, pb->pw, pb->name, + use_tachome); } if(ret) *pb->errnop = ERANGE; @@ -452,7 +485,8 @@ recheck: * returns 0 on success */ static int -find_pw_user(const char *logname, const char *tacuser, struct pwbuf *pb) +find_pw_user(const char *logname, const char *tacuser, struct pwbuf *pb, + uint16_t usetachome) { FILE *pwfile; struct passwd *ent; @@ -476,7 +510,7 @@ find_pw_user(const char *logname, const char *tacuser, struct pwbuf *pb) if(!ent->pw_name) continue; /* shouldn't happen */ if(!strcmp(ent->pw_name, tacuser)) { - ret = pwcopy(pb->buf, pb->buflen, ent, pb->pw, logname); + ret = pwcopy(pb->buf, pb->buflen, ent, pb->pw, logname, usetachome); break; } } @@ -663,12 +697,13 @@ static int lookup_mapped_uid(struct pwbuf *pb, uid_t uid, uid_t auid, int session) { char *loginname, mappedname[256]; + uint16_t flag; mappedname[0] = '\0'; loginname = lookup_mapuid(uid, auid, session, - mappedname, sizeof mappedname); + mappedname, sizeof mappedname, &flag); if(loginname) - return find_pw_user(loginname, mappedname, pb); + return find_pw_user(loginname, mappedname, pb, flag & MAP_USERHOMEDIR); return 1; } @@ -718,6 +753,7 @@ enum nss_status _nss_tacplus_getpwnam_r(const char *name, struct passwd *pw, if(!lookup) status = NSS_STATUS_SUCCESS; else if(lookup == 1) { /* 2 means exclude_users match */ + uint16_t flag; /* * If we can't contact a tacacs server (either not configured, or * more likely, we aren't running as root and the config for the @@ -728,8 +764,9 @@ enum nss_status _nss_tacplus_getpwnam_r(const char *name, struct passwd *pw, * common case of wanting to use the original login name by non-root * users. */ - char *mapname = lookup_mapname(name, -1, -1, NULL); - if(mapname != name && !find_pw_user(name, mapname, &pbuf)) + char *mapname = lookup_mapname(name, -1, -1, NULL, &flag); + if(mapname != name && !find_pw_user(name, mapname, &pbuf, + flag & MAP_USERHOMEDIR)) status = NSS_STATUS_SUCCESS; } } -- cgit v1.2.3