summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Olson <olson@cumulusnetworks.com>2017-05-12 11:43:01 -0700
committerDave Olson <olson@cumulusnetworks.com>2017-05-23 16:42:48 -0700
commitdab6c3bb9feb10b67f08b18656fe24d1f7b01d2b (patch)
treec841c02461e30d1dc4e194399e45293c883d1c68
parentf9f714b3b7b9f77c0165c0850bd816cac0d46292 (diff)
downloadlibnss-tacplus-dab6c3bb9feb10b67f08b18656fe24d1f7b01d2b.tar.gz
libnss-tacplus-dab6c3bb9feb10b67f08b18656fe24d1f7b01d2b.zip
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).
-rw-r--r--debian/changelog4
-rw-r--r--debian/copyright5
-rw-r--r--nss_tacplus.c80
3 files changed, 80 insertions, 9 deletions
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 <olson@cumulusnetworks.com> 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 <pawel.krawczyk@hush.com> and Jeroen Nijhof <jeroen@jeroennijhof.nl>
+Copyright: 2015, 2016, 2017 Cumulus Networks, Inc. All rights reserved.,
+ 2010 Pawel Krawczyk <pawel.krawczyk@hush.com> and
+ Jeroen Nijhof <jeroen@jeroennijhof.nl>
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 <olson@cumulusnetworks.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -37,6 +38,7 @@
#include <nss.h>
#include <libaudit.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <tacplus/libtac.h>
#include <tacplus/map_tacplus_user.h>
@@ -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))