From a8b91db168be36606391eb0b96af0ee4aaa6812f Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Mon, 26 Feb 2018 09:52:09 -0800 Subject: Fixed exclude_users to work, added more users, alway skip tacacs[0-9]* Ticket: CM-19886 Reviewed By: nobody Testing Done: Somehow exclude_users wasn't implemented (or got deleted somewhere along the line). Make list match tacplus_client, except exclude our own mapped users by matching config items, and also skip any user starting with tacacs[0-9] inline instead of listing all 16 in exclude_users field in config file. Implemened for mapped_priv_user too, since that work is ongoing. Listed change in debian/changelog If debug is set to 2 or higher, print that the name lookup was skipped due to exclusion. --- debian/changelog | 7 +++++++ map_common.c | 9 +++++++++ map_common.h | 1 + nss_mapname.c | 39 +++++++++++++++++++++++++++++++++++++++ nss_mapuser.5 | 8 +++++++- nss_mapuser.conf | 5 ++++- 6 files changed, 67 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 7b60a63..f93e11d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +libnss-mapuser (1.0.0-cl3u3) RELEASED; urgency=low + + * Closes CM-19866 - Fixed exclude_users not skipped, and added + more system accounts to exclude_users: www-data,man, tacacs[0-9]*. + + -- dev-support Mon, 26 Feb 2018 09:51:44 -0800 + libnss-mapuser (1.0.0-cl3u2) RELEASED; urgency=low * Added more system accounts to exclude_users: daemon, quagga, diff --git a/map_common.c b/map_common.c index 1c3bfa9..f7ee038 100644 --- a/map_common.c +++ b/map_common.c @@ -35,6 +35,7 @@ static const char config_file[] = "/etc/nss_mapuser.conf"; * in build, so local to the shared lib. */ char *exclude_users; /* don't lookup these users */ char *mappeduser; +char *mapped_priv_user; uid_t min_uid = DEF_MIN_UID; int debug; @@ -54,6 +55,10 @@ reset_config(void) (void)free(mappeduser); mappeduser = NULL; } + if(mapped_priv_user) { + (void)free(mapped_priv_user); + mapped_priv_user = NULL; + } debug = 0; min_uid = DEF_MIN_UID; } @@ -117,6 +122,10 @@ nss_mapuser_config(int *errnop, const char *lname) /* the user we are mapping to */ mappeduser = strdup(lbuf+12); } + else if(!strncmp(lbuf, "mapped_priv_user=", 17)) { + /* the user we are mapping to */ + mapped_priv_user = strdup(lbuf+17); + } else if(!strncmp(lbuf, "min_uid=", 8)) { /* * Don't lookup uids that are local, typically set to either diff --git a/map_common.h b/map_common.h index b4213a5..ff136d3 100644 --- a/map_common.h +++ b/map_common.h @@ -50,6 +50,7 @@ struct pwbuf { /* configuration variables. */ extern char *exclude_users; extern char *mappeduser; +extern char *mapped_priv_user; extern uid_t min_uid; extern int debug; diff --git a/nss_mapname.c b/nss_mapname.c index 9132f6e..f795cf5 100644 --- a/nss_mapname.c +++ b/nss_mapname.c @@ -37,6 +37,7 @@ #include "map_common.h" +#include static const char *nssname = "nss_mapuser"; /* for syslogs */ @@ -62,6 +63,7 @@ enum nss_status _nss_mapname_getpwnam_r(const char *name, struct passwd *pw, { enum nss_status status = NSS_STATUS_NOTFOUND; struct pwbuf pbuf; + bool islocal = 0; /* * the useradd family will not add/mod/del users correctly with @@ -78,6 +80,43 @@ enum nss_status _nss_mapname_getpwnam_r(const char *name, struct passwd *pw, return status; } + /* + * Ignore any name starting with tacacs[0-9] in case a + * tacplus client is installed. Cleaner than listing + * all 16 in the exclude_users list or implementing + * some form of wildcard. Also ignore our own mappeduser + * and mapped_priv_user names if set. + */ + if ((mappeduser && !strcmp(mappeduser, name)) || + (mapped_priv_user && !strcmp(mapped_priv_user, name))) + islocal = 1; + else if (!strncmp("tacacs", name, 6) && isdigit(name[6])) + islocal = 1; + else if (exclude_users) { + char *user, *list; + list = strdup(exclude_users); + if (list) { + static const char *delim = ", \t\n"; + user = strtok(list, delim); + list = NULL; + while (user) { + if(!strcmp(user, name)) { + islocal = 1; + break; + } + user = strtok(NULL, delim); + } + free(list); + } + } + if (islocal) { + if(debug > 1) + syslog(LOG_DEBUG, "%s: skipped excluded user: %s", nssname, + name); + return 2; + } + + /* marshal the args for the lower level functions */ pbuf.name = (char *)name; pbuf.pw = pw; diff --git a/nss_mapuser.5 b/nss_mapuser.5 index 2bbabad..3e17aec 100644 --- a/nss_mapuser.5 +++ b/nss_mapuser.5 @@ -18,7 +18,13 @@ Output lookup debugging information via syslog(3). Usernames (accounts) comma separate list. This is used by mapname NSS plugin getpwuid() entry point. The account to be looked up is checked against this list. If a match is found, an immediate NOTFOUND status is returned. This reduces overhead for the standard -local user accounts. +local user accounts. The +.I mapped_user +and +.I mapped_priv_user +fields from the +configuration file are always skipped, as are any names starting with +.BR tacacs[0-9] . .TP .I mapped_user=NAME This is the local account which mapping uses as a template. It must be a local diff --git a/nss_mapuser.conf b/nss_mapuser.conf index 5adf5e8..2685ac0 100644 --- a/nss_mapuser.conf +++ b/nss_mapuser.conf @@ -27,7 +27,10 @@ min_uid=1001 # that during pathname completion, bash can do an NSS lookup on "*" # To avoid server round trip delays, or worse, unreachable server delays # on filename completion, we include "*" in the exclusion list. -exclude_users=root,daemon,cron,cumulus,quagga,frr,man,ntp,radius_user,sshd,snmp,nobody,* +# User names starting with "tacacs[0-9]" are also ignored, in case the +# tacplus client packages are installed. User names matching +# the mapped_user and mapped_priv_user configuration fields are also ignored. +exclude_users=root,daemon,nobody,cron,sshd,cumulus,quagga,frr,snmp,www-data,ntp,man,* # Map all usernames to the radius_user account (use the uid, gid, shell, and # base of the home directory from the cumulus entry in /etc/passwd). -- cgit v1.2.3 From 556625e62b692b723cc6809d2374c3da9616dc3d Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Mon, 2 Apr 2018 10:50:18 -0700 Subject: Cleanup whitespace by running 'indent -linux' on all the files There were no code changes, just the indent whitespace and formatting changes. --- map_common.c | 375 +++++++++++++++++++++++++++++----------------------------- map_common.h | 17 ++- nss_mapname.c | 130 ++++++++++---------- nss_mapuid.c | 261 ++++++++++++++++++++-------------------- 4 files changed, 387 insertions(+), 396 deletions(-) diff --git a/map_common.c b/map_common.c index f7ee038..6313c88 100644 --- a/map_common.c +++ b/map_common.c @@ -23,128 +23,123 @@ * during the linking phase (made internal only). */ - #include "map_common.h" #include static const char config_file[] = "/etc/nss_mapuser.conf"; -#define DEF_MIN_UID 1001 /* fail lookups on uid's below this value */ +#define DEF_MIN_UID 1001 /* fail lookups on uid's below this value */ /* set from configuration file parsing; stripped from exported symbols * in build, so local to the shared lib. */ -char *exclude_users; /* don't lookup these users */ +char *exclude_users; /* don't lookup these users */ char *mappeduser; char *mapped_priv_user; uid_t min_uid = DEF_MIN_UID; int debug; static int conf_parsed = 0; -static const char *libname; /* for syslogs, set in each library */ +static const char *libname; /* for syslogs, set in each library */ /* reset all config variables when we are going to re-parse */ -static void -reset_config(void) +static void reset_config(void) { - /* reset the config variables that we use, freeing memory where needed */ - if(exclude_users) { - (void)free(exclude_users); - exclude_users = NULL; - } - if(mappeduser) { - (void)free(mappeduser); - mappeduser = NULL; - } - if(mapped_priv_user) { - (void)free(mapped_priv_user); - mapped_priv_user = NULL; - } - debug = 0; - min_uid = DEF_MIN_UID; + /* reset the config variables that we use, freeing memory where needed */ + if (exclude_users) { + (void)free(exclude_users); + exclude_users = NULL; + } + if (mappeduser) { + (void)free(mappeduser); + mappeduser = NULL; + } + if (mapped_priv_user) { + (void)free(mapped_priv_user); + mapped_priv_user = NULL; + } + debug = 0; + min_uid = DEF_MIN_UID; } /* * return 0 on succesful parsing (at least no hard errors), 1 if * an error, and 2 if already parsed and no change to config file */ -int -nss_mapuser_config(int *errnop, const char *lname) +int nss_mapuser_config(int *errnop, const char *lname) { - FILE *conf; - char lbuf[256]; - static struct stat lastconf; + FILE *conf; + char lbuf[256]; + static struct stat lastconf; - if(conf_parsed) { - struct stat st, *lst = &lastconf; - /* - * 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) - */ - if (stat(config_file, &st) && st.st_ino == lst->st_ino && - st.st_mtime == lst->st_mtime && st.st_ctime == lst->st_ctime) - return 2; /* nothing to reparse */ - reset_config(); - conf_parsed = 0; - if (debug && conf_parsed) - syslog(LOG_DEBUG, "%s: Configuration file changed, re-initializing", - libname); - } + if (conf_parsed) { + struct stat st, *lst = &lastconf; + /* + * 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) + */ + if (stat(config_file, &st) && st.st_ino == lst->st_ino && + st.st_mtime == lst->st_mtime + && st.st_ctime == lst->st_ctime) + return 2; /* nothing to reparse */ + reset_config(); + conf_parsed = 0; + if (debug && conf_parsed) + syslog(LOG_DEBUG, + "%s: Configuration file changed, re-initializing", + libname); + } - libname = lname; + libname = lname; - conf = fopen(config_file, "r"); - if(conf == NULL) { - *errnop = errno; - syslog(LOG_NOTICE, "%s: can't open config file %s: %m", - libname, config_file); - return 1; - } - if (fstat(fileno(conf), &lastconf) != 0) - memset(&lastconf, 0, sizeof lastconf); /* avoid stale data, no warning */ + conf = fopen(config_file, "r"); + if (conf == NULL) { + *errnop = errno; + syslog(LOG_NOTICE, "%s: can't open config file %s: %m", + libname, config_file); + return 1; + } + if (fstat(fileno(conf), &lastconf) != 0) + memset(&lastconf, 0, sizeof lastconf); /* avoid stale data, no warning */ - while(fgets(lbuf, sizeof lbuf, conf)) { - if(*lbuf == '#' || isspace(*lbuf)) - continue; /* skip comments, white space lines, etc. */ - strtok(lbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ - if(!strncmp(lbuf, "debug=", 6)) - debug = strtoul(lbuf+6, NULL, 0); - 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 - * locally, and won't do remote lookup on those uids either. - */ - exclude_users = strdup(lbuf+14); - } - else if(!strncmp(lbuf, "mapped_user=", 12)) { - /* the user we are mapping to */ - mappeduser = strdup(lbuf+12); - } - else if(!strncmp(lbuf, "mapped_priv_user=", 17)) { - /* the user we are mapping to */ - mapped_priv_user = strdup(lbuf+17); - } - else if(!strncmp(lbuf, "min_uid=", 8)) { - /* - * Don't lookup uids that are local, typically set to either - * 0 or smallest always local user's uid - */ - unsigned long uid; - char *valid; - uid = strtoul(lbuf+8, &valid, 0); - if (valid > (lbuf+8)) - min_uid = (uid_t)uid; - } - else if(debug) /* ignore unrecognized lines, unless debug on */ - syslog(LOG_WARNING, "%s: unrecognized parameter: %s", - libname, lbuf); - } - fclose(conf); - conf_parsed = 1; + while (fgets(lbuf, sizeof lbuf, conf)) { + if (*lbuf == '#' || isspace(*lbuf)) + continue; /* skip comments, white space lines, etc. */ + strtok(lbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ + if (!strncmp(lbuf, "debug=", 6)) + debug = strtoul(lbuf + 6, NULL, 0); + 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 + * locally, and won't do remote lookup on those uids either. + */ + exclude_users = strdup(lbuf + 14); + } else if (!strncmp(lbuf, "mapped_user=", 12)) { + /* the user we are mapping to */ + mappeduser = strdup(lbuf + 12); + } else if (!strncmp(lbuf, "mapped_priv_user=", 17)) { + /* the user we are mapping to */ + mapped_priv_user = strdup(lbuf + 17); + } else if (!strncmp(lbuf, "min_uid=", 8)) { + /* + * Don't lookup uids that are local, typically set to either + * 0 or smallest always local user's uid + */ + unsigned long uid; + char *valid; + uid = strtoul(lbuf + 8, &valid, 0); + if (valid > (lbuf + 8)) + min_uid = (uid_t) uid; + } else if (debug) /* ignore unrecognized lines, unless debug on */ + syslog(LOG_WARNING, "%s: unrecognized parameter: %s", + libname, lbuf); + } + fclose(conf); + conf_parsed = 1; - return mappeduser ? 0 : 1; /* can't do anything without this */ + return mappeduser ? 0 : 1; /* can't do anything without this */ } /* @@ -159,81 +154,87 @@ int pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, const char *usename) { - int needlen, cnt, origlen = len; - char *shell; + int needlen, cnt, origlen = len; + char *shell; - if(!mappeduser) { - if(debug) - syslog(LOG_DEBUG, "%s: empty mapped_user, failing", libname); - return 1; - } - if(!usename) { /* this should never happen */ - if(debug) - syslog(LOG_DEBUG, "%s: empty username, failing", libname); - return 1; - } + if (!mappeduser) { + if (debug) + syslog(LOG_DEBUG, "%s: empty mapped_user, failing", + libname); + return 1; + } + if (!usename) { /* this should never happen */ + if (debug) + syslog(LOG_DEBUG, "%s: empty username, failing", + libname); + return 1; + } - needlen = 2 * strlen(usename) + 2 + /* pw_name and pw_gecos */ - srcpw->pw_dir ? strlen(srcpw->pw_dir) + 1 : 1 + - srcpw->pw_shell ? strlen(srcpw->pw_shell) + 1 : 1 + - 2 + /* for 'x' in the passwd field */ - 12; /* for the "Mapped user" in the gecos field */ - if(needlen > len) { - if(debug) - syslog(LOG_DEBUG, "%s provided password buffer too small (%ld<%d)", - libname, (long)len, needlen); - return 1; - } + needlen = 2 * strlen(usename) + 2 + /* pw_name and pw_gecos */ + srcpw->pw_dir ? strlen(srcpw->pw_dir) + 1 : 1 + srcpw->pw_shell ? + strlen(srcpw->pw_shell) + 1 : 1 + 2 + /* for 'x' in passwd */ + 12; /* for the "Mapped user" in gecos */ + if (needlen > len) { + if (debug) + syslog(LOG_DEBUG, + "%s provided password buffer too small (%ld<%d)", + libname, (long)len, needlen); + return 1; + } - destpw->pw_uid = srcpw->pw_uid; - destpw->pw_gid = srcpw->pw_gid; + destpw->pw_uid = srcpw->pw_uid; + destpw->pw_gid = srcpw->pw_gid; - cnt = snprintf(buf, len, "%s", usename); - destpw->pw_name = buf; - cnt++; /* allow for null byte also */ - buf += cnt; - len -= cnt; - cnt = snprintf(buf, len, "%s", "x"); - destpw->pw_passwd = buf; - cnt++; - buf += cnt; - 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; - cnt++; - buf += cnt; - len -= cnt; - cnt = snprintf(buf, len, "%s mapped user", usename); - destpw->pw_gecos = buf; - cnt++; - buf += cnt; - len -= cnt; - if (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)", - libname, (long)origlen, origlen-(int)len); - return 1; - } + cnt = snprintf(buf, len, "%s", usename); + destpw->pw_name = buf; + cnt++; /* allow for null byte also */ + buf += cnt; + len -= cnt; + cnt = snprintf(buf, len, "%s", "x"); + destpw->pw_passwd = buf; + cnt++; + buf += cnt; + 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; + cnt++; + buf += cnt; + len -= cnt; + cnt = snprintf(buf, len, "%s mapped user", usename); + destpw->pw_gecos = buf; + cnt++; + buf += cnt; + len -= cnt; + if (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)", + libname, (long)origlen, origlen - (int)len); + return 1; + } - return 0; + return 0; } /* @@ -242,33 +243,31 @@ pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, * map file. * returns 0 on success */ -int -get_pw_mapuser(const char *name, struct pwbuf *pb) +int get_pw_mapuser(const char *name, struct pwbuf *pb) { - FILE *pwfile; - struct passwd *ent; - int ret = 1; - + FILE *pwfile; + struct passwd *ent; + int ret = 1; - pwfile = fopen("/etc/passwd", "r"); - if(!pwfile) { - syslog(LOG_WARNING, "%s: failed to open /etc/passwd: %m", - libname); - return 1; - } + pwfile = fopen("/etc/passwd", "r"); + if (!pwfile) { + syslog(LOG_WARNING, "%s: failed to open /etc/passwd: %m", + libname); + return 1; + } - pb->pw->pw_name = NULL; /* be paranoid */ - for(ret = 1; ret && (ent = fgetpwent(pwfile)); ) { - if(!ent->pw_name) - continue; /* shouldn't happen */ - if(!strcmp(ent->pw_name, mappeduser)) { - ret = pwcopy(pb->buf, pb->buflen, ent, pb->pw, name); - break; - } - } - fclose(pwfile); - if(ret) - *pb->errnop = ERANGE; + pb->pw->pw_name = NULL; /* be paranoid */ + for (ret = 1; ret && (ent = fgetpwent(pwfile));) { + if (!ent->pw_name) + continue; /* shouldn't happen */ + if (!strcmp(ent->pw_name, mappeduser)) { + ret = pwcopy(pb->buf, pb->buflen, ent, pb->pw, name); + break; + } + } + fclose(pwfile); + if (ret) + *pb->errnop = ERANGE; - return ret; + return ret; } diff --git a/map_common.h b/map_common.h index ff136d3..b3401ea 100644 --- a/map_common.h +++ b/map_common.h @@ -23,7 +23,6 @@ * stripped during the linking phase (made internal only). */ - #include #include #include @@ -34,17 +33,16 @@ #include #include - /* * pwbuf is used to reduce number of arguments passed around; the strings in * the passwd struct need to point into this buffer. */ struct pwbuf { - char *name; - char *buf; - struct passwd *pw; - int *errnop; - size_t buflen; + char *name; + char *buf; + struct passwd *pw; + int *errnop; + size_t buflen; }; /* configuration variables. */ @@ -55,7 +53,6 @@ extern uid_t min_uid; extern int debug; extern int nss_mapuser_config(int *errnop, const char *lname); -extern int pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, - const char *usename); +extern int pwcopy(char *buf, size_t len, struct passwd *srcpw, + struct passwd *destpw, const char *usename); extern int get_pw_mapuser(const char *name, struct pwbuf *pb); - diff --git a/nss_mapname.c b/nss_mapname.c index f795cf5..e974b75 100644 --- a/nss_mapname.c +++ b/nss_mapname.c @@ -35,12 +35,10 @@ * See nss_mapuid.c for the matching getpwuid_r for UIDs. */ - #include "map_common.h" #include - -static const char *nssname = "nss_mapuser"; /* for syslogs */ +static const char *nssname = "nss_mapuser"; /* for syslogs */ /* * If you aren't using glibc or a variant that supports this, @@ -57,75 +55,75 @@ extern const char *__progname; * Because we always have a positive reply, it's important that this * be the last NSS module for passwd lookups. */ -__attribute__ ((visibility ("default"))) +__attribute__ ((visibility("default"))) enum nss_status _nss_mapname_getpwnam_r(const char *name, struct passwd *pw, - char *buffer, size_t buflen, int *errnop) + char *buffer, size_t buflen, + int *errnop) { - enum nss_status status = NSS_STATUS_NOTFOUND; - struct pwbuf pbuf; - bool islocal = 0; - - /* - * the useradd family will not add/mod/del users correctly with - * the mapuid functionality, so return immediately if we are - * running as part of those processes. - */ - if (__progname && (!strcmp(__progname, "useradd") || - !strcmp(__progname, "usermod") || - !strcmp(__progname, "userdel"))) - return status; + enum nss_status status = NSS_STATUS_NOTFOUND; + struct pwbuf pbuf; + bool islocal = 0; - if (nss_mapuser_config(errnop, nssname) == 1) { - syslog(LOG_NOTICE, "%s: bad configuration", nssname); - return status; - } + /* + * the useradd family will not add/mod/del users correctly with + * the mapuid functionality, so return immediately if we are + * running as part of those processes. + */ + if (__progname && (!strcmp(__progname, "useradd") || + !strcmp(__progname, "usermod") || + !strcmp(__progname, "userdel"))) + return status; - /* - * Ignore any name starting with tacacs[0-9] in case a - * tacplus client is installed. Cleaner than listing - * all 16 in the exclude_users list or implementing - * some form of wildcard. Also ignore our own mappeduser - * and mapped_priv_user names if set. - */ - if ((mappeduser && !strcmp(mappeduser, name)) || - (mapped_priv_user && !strcmp(mapped_priv_user, name))) - islocal = 1; - else if (!strncmp("tacacs", name, 6) && isdigit(name[6])) - islocal = 1; - else if (exclude_users) { - char *user, *list; - list = strdup(exclude_users); - if (list) { - static const char *delim = ", \t\n"; - user = strtok(list, delim); - list = NULL; - while (user) { - if(!strcmp(user, name)) { - islocal = 1; - break; - } - user = strtok(NULL, delim); - } - free(list); - } - } - if (islocal) { - if(debug > 1) - syslog(LOG_DEBUG, "%s: skipped excluded user: %s", nssname, - name); - return 2; - } + if (nss_mapuser_config(errnop, nssname) == 1) { + syslog(LOG_NOTICE, "%s: bad configuration", nssname); + return status; + } + /* + * Ignore any name starting with tacacs[0-9] in case a + * tacplus client is installed. Cleaner than listing + * all 16 in the exclude_users list or implementing + * some form of wildcard. Also ignore our own mappeduser + * and mapped_priv_user names if set. + */ + if ((mappeduser && !strcmp(mappeduser, name)) || + (mapped_priv_user && !strcmp(mapped_priv_user, name))) + islocal = 1; + else if (!strncmp("tacacs", name, 6) && isdigit(name[6])) + islocal = 1; + else if (exclude_users) { + char *user, *list; + list = strdup(exclude_users); + if (list) { + static const char *delim = ", \t\n"; + user = strtok(list, delim); + list = NULL; + while (user) { + if (!strcmp(user, name)) { + islocal = 1; + break; + } + user = strtok(NULL, delim); + } + free(list); + } + } + if (islocal) { + if (debug > 1) + syslog(LOG_DEBUG, "%s: skipped excluded user: %s", + nssname, name); + return 2; + } - /* marshal the args for the lower level functions */ - pbuf.name = (char *)name; - pbuf.pw = pw; - pbuf.buf = buffer; - pbuf.buflen = buflen; - pbuf.errnop = errnop; + /* marshal the args for the lower level functions */ + pbuf.name = (char *)name; + pbuf.pw = pw; + pbuf.buf = buffer; + pbuf.buflen = buflen; + pbuf.errnop = errnop; - if(!get_pw_mapuser(name, &pbuf)) - status = NSS_STATUS_SUCCESS; + if (!get_pw_mapuser(name, &pbuf)) + status = NSS_STATUS_SUCCESS; - return status; + return status; } diff --git a/nss_mapuid.c b/nss_mapuid.c index f97b28e..cc6c5ec 100644 --- a/nss_mapuid.c +++ b/nss_mapuid.c @@ -17,7 +17,6 @@ * along with this program - see the file COPYING. */ - /* * This plugin implements getpwuid_r for NSS to map a UID back to * a mapped username account, set up via nss_mapuser. @@ -48,8 +47,7 @@ #include #include - -static const char *nssname = "nss_mapuid"; /* for syslogs */ +static const char *nssname = "nss_mapuid"; /* for syslogs */ static const char dbdir[] = "/run/mapuser/"; /* @@ -66,98 +64,97 @@ extern const char *__progname; */ static int chk_session_file(char *sfile, uid_t uid, struct pwbuf *pb) { - char rbuf[256], user[64]; - FILE *mapf; - uid_t auid = 0; - int ret = 1; - - mapf = fopen(sfile, "r"); - if (!mapf) { - if( debug) - syslog(LOG_DEBUG, "%s: session map file %s open fails: %m", - nssname, sfile); - return ret; - } - user[0] = '\0'; - while(fgets(rbuf, sizeof rbuf, mapf)) { - strtok(rbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ - if(!strncmp("user=", rbuf, 5)) { /* should precede auid */ - snprintf(user, sizeof user, "%s", rbuf+5); - if (auid) /* found out of order, but now have both */ - break; - } - else if(!strncmp("auid=", rbuf, 5)) { - uid_t fuid = (uid_t)strtoul(rbuf+5, NULL, 10); - if (fuid && uid == fuid) { - auid = fuid; - if (user[0]) - break; /* normal ordering, else keep looking for user */ - } - } - } - fclose(mapf); - - if (auid && user[0]) /* otherwise not a match */ - ret = get_pw_mapuser(user, pb); /* should always succeed */ - - return ret; + char rbuf[256], user[64]; + FILE *mapf; + uid_t auid = 0; + int ret = 1; + + mapf = fopen(sfile, "r"); + if (!mapf) { + if (debug) + syslog(LOG_DEBUG, + "%s: session map file %s open fails: %m", + nssname, sfile); + return ret; + } + user[0] = '\0'; + while (fgets(rbuf, sizeof rbuf, mapf)) { + strtok(rbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ + if (!strncmp("user=", rbuf, 5)) { /* should precede auid */ + snprintf(user, sizeof user, "%s", rbuf + 5); + if (auid) /* found out of order, but now have both */ + break; + } else if (!strncmp("auid=", rbuf, 5)) { + uid_t fuid = (uid_t) strtoul(rbuf + 5, NULL, 10); + if (fuid && uid == fuid) { + auid = fuid; + if (user[0]) + break; /* normal ordering, else keep looking for user */ + } + } + } + fclose(mapf); + + if (auid && user[0]) /* otherwise not a match */ + ret = get_pw_mapuser(user, pb); /* should always succeed */ + + return ret; } /* find mapping for this sessionid */ -static int -find_mapped_name(struct pwbuf *pb, uid_t uid, uint32_t session) +static int find_mapped_name(struct pwbuf *pb, uid_t uid, uint32_t session) { - char sessfile[sizeof dbdir + 11]; + char sessfile[sizeof dbdir + 11]; - snprintf(sessfile, sizeof sessfile, "%s%u", dbdir, session); - return chk_session_file(sessfile, uid, pb); + snprintf(sessfile, sizeof sessfile, "%s%u", dbdir, session); + return chk_session_file(sessfile, uid, pb); } static int find_mappingfile(struct pwbuf *pb, uid_t uid) { - DIR *dir; - struct dirent *ent; - int ret = 1; - - dir = opendir(dbdir); - if (!dir) { /* can happen if no mapped users logged in */ - if (debug > 1) - syslog(LOG_DEBUG, "%s: Unable to open mapping directory %s: %m", - nssname, dbdir); - return 1; - } - - /* Loop through all numeric files in dbdir, check for matching uid */ - while (ret && (ent = readdir(dir))) { - char sessfile[sizeof dbdir + 11]; - if (!isdigit(ent->d_name[0])) /* sanity check on session file */ - continue; - snprintf(sessfile, sizeof sessfile, "%s%s", dbdir, ent->d_name); - ret = chk_session_file(sessfile, uid, pb); - } - if (ret && debug) - syslog(LOG_DEBUG, "%s: uid %u mapping not found in map files", - nssname, uid); - closedir(dir); - return ret; + DIR *dir; + struct dirent *ent; + int ret = 1; + + dir = opendir(dbdir); + if (!dir) { /* can happen if no mapped users logged in */ + if (debug > 1) + syslog(LOG_DEBUG, + "%s: Unable to open mapping directory %s: %m", + nssname, dbdir); + return 1; + } + + /* Loop through all numeric files in dbdir, check for matching uid */ + while (ret && (ent = readdir(dir))) { + char sessfile[sizeof dbdir + 11]; + if (!isdigit(ent->d_name[0])) /* sanity check on session file */ + continue; + snprintf(sessfile, sizeof sessfile, "%s%s", dbdir, ent->d_name); + ret = chk_session_file(sessfile, uid, pb); + } + if (ret && debug) + syslog(LOG_DEBUG, "%s: uid %u mapping not found in map files", + nssname, uid); + closedir(dir); + return ret; } -static uint32_t -get_sessionid(void) +static uint32_t get_sessionid(void) { - int fd = -1, cnt; - uint32_t id = 0U; - static char buf[12]; - - fd = open("/proc/self/sessionid", O_RDONLY); - if(fd != -1) { - cnt = read(fd, buf, sizeof(buf)); - close(fd); - } - if(fd != -1 && cnt > 0) { - id = strtoul(buf, NULL, 0); - } - return id; + int fd = -1, cnt; + uint32_t id = 0U; + static char buf[12]; + + fd = open("/proc/self/sessionid", O_RDONLY); + if (fd != -1) { + cnt = read(fd, buf, sizeof(buf)); + close(fd); + } + if (fd != -1 && cnt > 0) { + id = strtoul(buf, NULL, 0); + } + return id; } /* @@ -184,55 +181,55 @@ get_sessionid(void) * just get the files, ldap, etc. entry for the UID * Returns the first match if multiple mapped users. */ -__attribute__ ((visibility ("default"))) +__attribute__ ((visibility("default"))) enum nss_status _nss_mapuid_getpwuid_r(uid_t uid, struct passwd *pw, - char *buffer, size_t buflen, int *errnop) + char *buffer, size_t buflen, int *errnop) { - struct pwbuf pb; - enum nss_status status = NSS_STATUS_NOTFOUND; - uint32_t session; - - /* - * the useradd family will not add/mod/del users correctly with - * the mapuid functionality, so return immediately if we are - * running as part of those processes. - */ - if (__progname && (!strcmp(__progname, "useradd") || - !strcmp(__progname, "usermod") || - !strcmp(__progname, "userdel"))) - return status; - - /* this can happen for permission reasons, do don't complain except - * at debug */ - if (nss_mapuser_config(errnop, nssname) == 1) { - return status; /* syslog already done */ - } - - - if (min_uid != ~0U && uid < min_uid) { - if(debug > 1) - syslog(LOG_DEBUG, "%s: uid %u < min_uid %u, don't lookup", - nssname, uid, min_uid); - return status; - } - - /* marshal the args for the lower level functions */ - pb.pw = pw; - pb.buf = buffer; - pb.buflen = buflen; - pb.errnop = errnop; - pb.name = NULL; - - /* session needs to be set to lookup this user. May also be set - * for other users. - */ - session = get_sessionid(); - if(session && !find_mapped_name(&pb, uid, session)) - status = NSS_STATUS_SUCCESS; - if(status != NSS_STATUS_SUCCESS) { - /* lookup by some other user or unrelated process, try dir lookup */ - if (!find_mappingfile(&pb, uid)) - status = NSS_STATUS_SUCCESS; - } - return status; + struct pwbuf pb; + enum nss_status status = NSS_STATUS_NOTFOUND; + uint32_t session; + + /* + * the useradd family will not add/mod/del users correctly with + * the mapuid functionality, so return immediately if we are + * running as part of those processes. + */ + if (__progname && (!strcmp(__progname, "useradd") || + !strcmp(__progname, "usermod") || + !strcmp(__progname, "userdel"))) + return status; + + /* this can happen for permission reasons, do don't complain except + * at debug */ + if (nss_mapuser_config(errnop, nssname) == 1) { + return status; /* syslog already done */ + } + + if (min_uid != ~0U && uid < min_uid) { + if (debug > 1) + syslog(LOG_DEBUG, + "%s: uid %u < min_uid %u, don't lookup", nssname, + uid, min_uid); + return status; + } + + /* marshal the args for the lower level functions */ + pb.pw = pw; + pb.buf = buffer; + pb.buflen = buflen; + pb.errnop = errnop; + pb.name = NULL; + + /* session needs to be set to lookup this user. May also be set + * for other users. + */ + session = get_sessionid(); + if (session && !find_mapped_name(&pb, uid, session)) + status = NSS_STATUS_SUCCESS; + if (status != NSS_STATUS_SUCCESS) { + /* lookup by some other user or unrelated process, try dir lookup */ + if (!find_mappingfile(&pb, uid)) + status = NSS_STATUS_SUCCESS; + } + return status; } -- cgit v1.2.3 From 1e5742369aedc8708d5dbe4411ffd5bf4b10537a Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Mon, 2 Apr 2018 11:01:09 -0700 Subject: Add VSA shell:priv-lvl support for privileged radius user logins Ticket: CM-19457 Reviewed By: roopa Testing Done: lots of variations of login, su, sudo, automated radius tests Now we always read the map files. If session is set, we try that file first, so that a user always sees their name, same as tacplus. If that's the wrong file, read through all of the map files, look for the correct match based on either name+session or auid+session, depending on getpwnam or getpwuid entry point Ignore same set of users as tacacs, including new radius_priv_user account for the privileged RADIUS user. create and delete the mapuser files from libpam-radius-auth now; we need to have the mapping file written early enough for the pam interfaces to get the correct info. Using the pam_script is too limiting, and since we are creating the database in libpam-radius-auth now, we'll delete it there as well to keep things symmetric, so delete the script and the references to the scripts A significant part of this effort was adding getgrent, getgrgid, and getgrnam support, so that the radius users are put into the netshow (unprivileged) and netedit and sudo (privileged) groups at login. A lot of restructuring went in as part of that, and cleaned up some longstanding bugs, including return values for the getpw* routines. Also cleaned up some whitespace issues. Also renamed some globals (debug, min_uid, init_common()) that might collide with other programs, so that when I build unstripped and normal visibility shared libs, they won't collide with programs calling the functions (saw this with "debug" and bgpd, for example). --- Makefile | 3 +- debian/changelog | 4 + debian/control | 4 +- debian/libnss-mapuser.postinst | 8 + debian/mapuser | 6 - debian/rules | 6 - map_common.c | 476 +++++++++++++++++++++++++++++++++++++---- map_common.h | 19 +- nss_mapname.c | 325 +++++++++++++++++++++++++--- nss_mapuid.c | 134 +----------- nss_mapuser.5 | 83 +++++-- nss_mapuser.8 | 87 ++++++-- nss_mapuser.conf | 1 + pam_script_ses_close | 85 -------- pam_script_ses_open | 65 ------ 15 files changed, 907 insertions(+), 399 deletions(-) delete mode 100644 debian/mapuser delete mode 100755 pam_script_ses_close delete mode 100755 pam_script_ses_open diff --git a/Makefile b/Makefile index d32c8f5..f2fd8f9 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,9 @@ endif CPPFLAGS = -D_FORTIFY_SOURCE=2 CFLAGS = $(CPPFLAGS) ${OPTFLAGS} -fPIC -fstack-protector-strong \ -Wformat -Werror=format-security -Wall $(FVISIBILITY) +LDLIBS = -laudit LDFLAGS = -shared -fPIC -DPIC \ - -Wl,-z -Wl,relro -Wl,-z -Wl,now -Wl,-soname -Wl,$@ + -Wl,-z -Wl,relro -Wl,-z -Wl,now -Wl,-soname -Wl,$@ $(LDLIBS) all: $(NSSNAMELIB) $(NSSUIDLIB) diff --git a/debian/changelog b/debian/changelog index f93e11d..50032b5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,10 @@ libnss-mapuser (1.0.0-cl3u3) RELEASED; urgency=low * Closes CM-19866 - Fixed exclude_users not skipped, and added more system accounts to exclude_users: www-data,man, tacacs[0-9]*. + * New Enabled - When Vendor Specific Option containing shell:priv-lvl + is present, and the value is 15, map to user radius_priv_user, and + give that user account more privileges, similar to tacplus client + privilege 15. -- dev-support Mon, 26 Feb 2018 09:51:44 -0800 diff --git a/debian/control b/debian/control index c383d86..d0ad69d 100644 --- a/debian/control +++ b/debian/control @@ -1,14 +1,14 @@ Source: libnss-mapuser Priority: optional Maintainer: dev-support -Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.16.1), git +Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.16.1), libaudit-dev, git Section: libs Standards-Version: 3.9.6 Homepage: http://www.cumulusnetworks.com Package: libnss-mapuser Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, adduser +Depends: ${shlibs:Depends}, ${misc:Depends}, libaudit1, adduser Description: NSS modules to map any requested username to a local account Performs getpwname and getpwuid lookups via NSS for systems like RADIUS where it is not possible to do a username lookup without authentication diff --git a/debian/libnss-mapuser.postinst b/debian/libnss-mapuser.postinst index 2e9b04f..ee6a70d 100644 --- a/debian/libnss-mapuser.postinst +++ b/debian/libnss-mapuser.postinst @@ -19,11 +19,19 @@ case "$1" in -e '/^passwd:/s/\s\s*/&mapuid /' \ -e '/^passwd:.*#/s/#.*/ mapname &/' \ -e '/^passwd:[^#]*$/s/$/ mapname &/' \ + -e '/^group:.*#/s/#.*/ mapname &/' \ + -e '/^group:[^#]*$/s/: */& mapname /' \ /etc/nsswitch.conf fi addgroup --quiet $rgroup 2>&1 | grep -v 'already exists' adduser --quiet --firstuid 1000 --disabled-login --ingroup $rgroup \ --gecos "radius user" radius_user 2>&1 | grep -v 'already exists' + adduser --quiet --firstuid 1000 --disabled-login --ingroup $rgroup \ + --gecos "radius privileged user" radius_priv_user 2>&1 | grep -v 'already exists' + # regular radius logins can run net show commands + adduser --quiet radius_user netshow + # privileged radius logins can run net config commands, as well as show + adduser --quiet radius_priv_user netedit exit 0 ) ;; diff --git a/debian/mapuser b/debian/mapuser deleted file mode 100644 index 69d2137..0000000 --- a/debian/mapuser +++ /dev/null @@ -1,6 +0,0 @@ -Name: libnss-mapuser uses this to maintain the session uid => user mapping -Default: yes -Priority: 257 -Session-Type: Additional -Session: - optional pam_script.so dir=/usr/share/mapuser diff --git a/debian/rules b/debian/rules index ed7dbc0..cb1f417 100755 --- a/debian/rules +++ b/debian/rules @@ -16,9 +16,3 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all # No configuration needed override_dh_auto_configure: -override_dh_install: - dh_installdirs /usr/share/pam-configs /usr/share/mapuser - install -p -m 755 pam_script_ses* debian/libnss-mapuser/usr/share/mapuser - install -p -m 444 debian/mapuser \ - debian/libnss-mapuser/usr/share/pam-configs/ - dh_install diff --git a/map_common.c b/map_common.c index 6313c88..830d85a 100644 --- a/map_common.c +++ b/map_common.c @@ -25,6 +25,11 @@ #include "map_common.h" #include +#include +#include +#include +#include +#include static const char config_file[] = "/etc/nss_mapuser.conf"; @@ -35,30 +40,43 @@ static const char config_file[] = "/etc/nss_mapuser.conf"; char *exclude_users; /* don't lookup these users */ char *mappeduser; char *mapped_priv_user; -uid_t min_uid = DEF_MIN_UID; -int debug; +uid_t map_min_uid = DEF_MIN_UID; +int map_debug; static int conf_parsed = 0; static const char *libname; /* for syslogs, set in each library */ +static const char dbdir[] = "/run/mapuser/"; + +/* + * If you aren't using glibc or a variant that supports this, + * and you have a system that supports the BSD getprogname(), + * you can replace this use with getprogname() + */ +extern const char *__progname; /* reset all config variables when we are going to re-parse */ static void reset_config(void) { + void *p; + /* reset the config variables that we use, freeing memory where needed */ if (exclude_users) { - (void)free(exclude_users); + p = exclude_users; exclude_users = NULL; + (void)free(p); } if (mappeduser) { - (void)free(mappeduser); + p = mappeduser; mappeduser = NULL; + (void)free(p); } if (mapped_priv_user) { - (void)free(mapped_priv_user); + p = mapped_priv_user; mapped_priv_user = NULL; + (void)free(p); } - debug = 0; - min_uid = DEF_MIN_UID; + map_debug = 0; + map_min_uid = DEF_MIN_UID; } /* @@ -84,7 +102,7 @@ int nss_mapuser_config(int *errnop, const char *lname) return 2; /* nothing to reparse */ reset_config(); conf_parsed = 0; - if (debug && conf_parsed) + if (map_debug && conf_parsed) syslog(LOG_DEBUG, "%s: Configuration file changed, re-initializing", libname); @@ -106,9 +124,9 @@ int nss_mapuser_config(int *errnop, const char *lname) if (*lbuf == '#' || isspace(*lbuf)) continue; /* skip comments, white space lines, etc. */ strtok(lbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ - if (!strncmp(lbuf, "debug=", 6)) - debug = strtoul(lbuf + 6, NULL, 0); - else if (!strncmp(lbuf, "exclude_users=", 14)) { + if (!strncmp(lbuf, "debug=", 6)) { + map_debug = strtoul(lbuf + 6, NULL, 0); + } 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 @@ -122,7 +140,7 @@ int nss_mapuser_config(int *errnop, const char *lname) } else if (!strncmp(lbuf, "mapped_priv_user=", 17)) { /* the user we are mapping to */ mapped_priv_user = strdup(lbuf + 17); - } else if (!strncmp(lbuf, "min_uid=", 8)) { + } else if (!strncmp(lbuf, "map_min_uid=", 8)) { /* * Don't lookup uids that are local, typically set to either * 0 or smallest always local user's uid @@ -131,40 +149,52 @@ int nss_mapuser_config(int *errnop, const char *lname) char *valid; uid = strtoul(lbuf + 8, &valid, 0); if (valid > (lbuf + 8)) - min_uid = (uid_t) uid; - } else if (debug) /* ignore unrecognized lines, unless debug on */ + map_min_uid = (uid_t) uid; + } else if (map_debug) /* ignore unrecognized lines, unless map_debug on */ syslog(LOG_WARNING, "%s: unrecognized parameter: %s", libname, lbuf); } fclose(conf); conf_parsed = 1; - return mappeduser ? 0 : 1; /* can't do anything without this */ + /* can't do anything without at least one of these */ + return (mappeduser || mapped_priv_user) ? 0 : 1; +} + +uint32_t get_sessionid(void) +{ + int fd = -1, cnt; + uint32_t id = 0U; + static char buf[12]; + + fd = open("/proc/self/sessionid", O_RDONLY); + if (fd != -1) { + cnt = read(fd, buf, sizeof(buf)); + close(fd); + } + if (fd != -1 && cnt > 0) { + id = strtoul(buf, NULL, 0); + } + return id; } /* * copy a passwd structure and it's strings, using the provided buffer * for the strings. - * usename is used for the new pw_name, the last part of the homedir, + * user name is used for the new pw_name, the last part of the homedir, * and the GECOS field. * For strings, if pointer is null, use an empty string. * Returns 0 if everything fit, otherwise 1. */ int -pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, - const char *usename) +pwcopy(char *buf, size_t len, const char *usename, struct passwd *srcpw, + struct passwd *destpw) { int needlen, cnt, origlen = len; char *shell; - if (!mappeduser) { - if (debug) - syslog(LOG_DEBUG, "%s: empty mapped_user, failing", - libname); - return 1; - } if (!usename) { /* this should never happen */ - if (debug) + if (map_debug) syslog(LOG_DEBUG, "%s: empty username, failing", libname); return 1; @@ -175,7 +205,7 @@ pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, strlen(srcpw->pw_shell) + 1 : 1 + 2 + /* for 'x' in passwd */ 12; /* for the "Mapped user" in gecos */ if (needlen > len) { - if (debug) + if (map_debug) syslog(LOG_DEBUG, "%s provided password buffer too small (%ld<%d)", libname, (long)len, needlen); @@ -227,7 +257,7 @@ pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, buf += cnt; len -= cnt; if (len < 0) { - if (debug) + if (map_debug) syslog(LOG_DEBUG, "%s provided password buffer too small (%ld<%d)", libname, (long)origlen, origlen - (int)len); @@ -238,12 +268,16 @@ pwcopy(char *buf, size_t len, struct passwd *srcpw, struct passwd *destpw, } /* - * This passes in a fixed - * name for UID lookups, where we have the mapped name from the - * map file. - * returns 0 on success + * pb->name is non-NULL when we have the name and want to look it up + * from the mapping. mapuid will be the auid if we found it in the + * files, otherwise will be what was passed down, which should + * be the UID we are looking up when pb->name is NULL, when it's + * the uid lookup, and otherwise should be -1 when pb->name is not NULL. + * Returns 0 on success, 1 if uid not found in mapping files (even if + * uid matches the radius mapping users; let nss_files handle that). */ -int get_pw_mapuser(const char *name, struct pwbuf *pb) +static int +get_pw_mapuser(const char *name, struct pwbuf *pb, uid_t mapuid, int privileged) { FILE *pwfile; struct passwd *ent; @@ -256,18 +290,388 @@ int get_pw_mapuser(const char *name, struct pwbuf *pb) return 1; } - pb->pw->pw_name = NULL; /* be paranoid */ for (ret = 1; ret && (ent = fgetpwent(pwfile));) { if (!ent->pw_name) continue; /* shouldn't happen */ - if (!strcmp(ent->pw_name, mappeduser)) { - ret = pwcopy(pb->buf, pb->buflen, ent, pb->pw, name); + if (!strcmp(ent->pw_name, name) || + !strcmp(ent->pw_name, privileged ? mapped_priv_user : + mappeduser) || ent->pw_uid == mapuid) { + ret = + pwcopy(pb->buf, pb->buflen, pb->name, ent, pb->pw); break; } } fclose(pwfile); - if (ret) + if (ret) { *pb->errnop = ERANGE; + } + + return ret; +} + +/* + * Read the requested session file (in the dbdir by intent), verify the + * uid matches, and setup the passwd structure with the username found + * in the file. + */ +static int chk_session_file(char *session, uid_t uid, struct pwbuf *pb) +{ + char rbuf[256], user[64], sessfile[sizeof dbdir + 12]; + FILE *mapf; + uid_t auid = 0; + int ret = 1, privileged = 0;; + int gotinfo = 0; /* user, session, auid */ + + snprintf(sessfile, sizeof sessfile, "%s%s", dbdir, session); + + mapf = fopen(sessfile, "r"); + if (!mapf) { + if (map_debug > 2) + syslog(LOG_DEBUG, + "%s: session map file %s open fails: %m", + libname, sessfile); + return ret; + } + user[0] = '\0'; + while (gotinfo != 4 && fgets(rbuf, sizeof rbuf, mapf)) { + strtok(rbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ + if (!strncmp("user=", rbuf, 5)) { + if (pb->name && strcmp(rbuf + 5, pb->name)) + break; + snprintf(user, sizeof user, "%s", rbuf + 5); + gotinfo++; + } else if (!strncmp("auid=", rbuf, 5)) { + char *ok; + uid_t fuid = (uid_t) strtoul(rbuf + 5, &ok, 10); + if (ok != (rbuf + 5)) { + gotinfo++; + if (uid != -1 && fuid != uid) { + auid = fuid; + break; /* getpwuid, but uid/auid mismatch, nogo */ + } else + auid = fuid; + } + } else if (!strcasecmp("privileged=yes", rbuf)) { + privileged = 1; + gotinfo++; + } else if (!strcasecmp("privileged=no", rbuf)) + gotinfo++; + else if (!strncmp("session=", rbuf, 8)) { + /* structural problem, so log warning */ + if (strcmp(session, rbuf + 8)) { + syslog(LOG_WARNING, + "%s: session field \"%s\" mismatch in %s", + libname, rbuf, sessfile); + } else + gotinfo++; + } + } + fclose(mapf); + + if (auid && user[0]) { /* otherwise not a match */ + if (!pb->name) + pb->name = user; /* uid lookups */ + ret = get_pw_mapuser(user, pb, auid, privileged); + } + + return ret; +} + +/* + * find mapping for this sessionid; if uid == -1, we are doing name lookup + * and will find in uid; else we are doing name lookup. + */ +int find_mapped_name(struct pwbuf *pb, uid_t uid, uint32_t session) +{ + char sess[11]; + + snprintf(sess, sizeof sess, "%u", session); + return chk_session_file(sess, uid, pb); +} + +/* + * Called when we don't have a sessionid, or the sessionid we have + * doesn't match a mapped user (from find_mapped_name() above), + * so we need to look through all the mapping files. + * As with find_mapped_name(), if uid == -1, we are looking up from + * the name, otherwise we are looking up from the uid. + */ +int find_mappingfile(struct pwbuf *pb, uid_t uid) +{ + DIR *dir; + struct dirent *ent; + int ret = 1; + + dir = opendir(dbdir); + if (!dir) { /* can happen if no mapped users logged in */ + if (map_debug > 1) + syslog(LOG_DEBUG, + "%s: Unable to open mapping directory %s: %m", + libname, dbdir); + return 1; + } + /* Loop through all numeric files in dbdir, check for matching uid */ + while (ret && (ent = readdir(dir))) { + if (!isdigit(ent->d_name[0]) || ent->d_type != DT_REG) + continue; /* sanity check on session file */ + ret = chk_session_file(ent->d_name, uid, pb); + } + closedir(dir); return ret; } + +/* + * Used when there are no mapping entries, just create an entry from + * the default radius user + * This is needed so that ssh and login accept the username, and continue. + */ +int make_mapuser(struct pwbuf *pb, const char *name) +{ + int ret; + ret = get_pw_mapuser(mappeduser, pb, (uid_t) - 1, 0); + return ret; +} + +static char +*_getcmdname(void) +{ + static char buf[64]; + char *rv = NULL; + int ret, fd; + + if (*buf) + return buf; + + fd = open("/proc/self/comm", O_RDONLY); + if (fd == -1) { + if (map_debug) + syslog(LOG_DEBUG, + "%s: failed to open /proc/self/comm: %m", + libname); + } else { + ret = read(fd, buf, sizeof buf); + if (ret <= 0) { + if (map_debug) + syslog(LOG_DEBUG, + "%s: read /proc/self/comm ret %d: %m", + libname, ret); + } else { + (void)strtok(buf, "\n\r "); + rv = buf; + } + } + + return rv; +} + +static int chk_progs(const char *pname) +{ + static const char *progs[] = + { "useradd", "usermod", "userdel", "adduser", + "deluser", NULL + }; + const char **prog; + int ret = 0; + + for (prog = &progs[0]; pname && *prog && !ret; prog++) { + if (!strcmp(pname, *prog)) { + if (map_debug > 1) + syslog(LOG_DEBUG, + "%s: running from %s, skip lookup", + libname, *prog); + ret = 1; + } + } + return ret; +} + +/* + * the useradd family will not add/mod/del users correctly with + * the mapuid functionality, so return immediately if we are + * running as part of those processes. Same for adduser, deluser + * adduser and deluser are often perl scripts, so check for "comm" + * name from /proc, also, unless already matched from progname. + */ +int skip_program(void) +{ + return chk_progs(__progname) || chk_progs(_getcmdname()); +} + +/* + * All the entry points have this same common prolog, so put it here + */ +int map_init_common(int *errnop, const char *plugname) +{ + if (skip_program()) + return 1; + + if (nss_mapuser_config(errnop, plugname) == 1) { + *errnop = ENOENT; + syslog(LOG_NOTICE, "%s: bad configuration", plugname); + return 1; + } + return 0; +} + +/* Open a session file, get the username and the privilege level, + * and if the privilege level matches, fill in usrbuf with name. + * 0 on errors or no match, 1 if match and usrbuf is valid + */ +int get_priv_usr(const char *fname, unsigned prbits, char *usrbuf) +{ + char buf[256], user[64], sessfile[sizeof dbdir + 12]; + FILE *map; + int privmatch = 0; + + snprintf(sessfile, sizeof sessfile, "%s%s", dbdir, fname); + + map = fopen(sessfile, "r"); + if (!map) { + syslog(LOG_WARNING, "%s: session map file %s open fails: %m", + libname, sessfile); + return 0; + } + user[0] = '\0'; + while (fgets(buf, sizeof buf, map)) { + strtok(buf, " \t\n\r\f"); /* terminate buffer at first whitespace */ + if (!strncmp("user=", buf, 5)) { + snprintf(user, sizeof user, "%s", buf + 5); + } else if (!strcasecmp("privileged=yes", buf)) { + if (prbits & PRIV_MATCH) + privmatch = 1; + } else if (!strcasecmp("privileged=no", buf)) { + if (prbits & UNPRIV_MATCH) + privmatch = 1; + } + } + fclose(map); + if (privmatch && user[0] && usrbuf) + strcpy(usrbuf, user); + return privmatch == 1; +} + + +/* +* Return a char ** list of strings of usernames from the mapping files +* that match priviliged=yes|no, for replacing the gr_mem field for +* getgrent(), etc. +* Makes one sanity check to be sure the listed PID is still active +* before adding the username to the list. +* Passed the original gr_mem array, which will +* include the mappeduser or mapped_priv_user (or both). +* All strings go into buf, and we return ERANGE in *err if there +* isn't enough room. +* The allocated memory will leak, but it's re-used on each call, so +* not too signficant, and if endgrent() gets called, we'll clean up. +*/ +char **fixup_gr_mem(const char *grnam, const char **gr_in, char *buf, + size_t * lenp, int *err, unsigned privbits) +{ + DIR *dir = NULL; + struct dirent *ent; + int ret = 1, nmemb, nadded = 0, midx, j; + const int nmax = 64; /* max members we'll add per group */ + long long l = 0, len = *lenp; /* size_t unsigned on some systems */ + const char **in; + char **out, *mem = buf; + char **gr_mem; + char newmembers[nmax][128]; + const unsigned align = sizeof(void *) - 1; + + *err = 0; + if (!gr_in) + goto done; + + dir = opendir(dbdir); + if (!dir) { + /* + * Usually because no mapped users logged in. Could + * return ENOENT, but may as well just act like compat + */ + goto done; + } + + /* Loop through all numeric files in dbdir, check for matching uid */ + while (ret && nadded < nmax && (ent = readdir(dir))) { + char usr[64]; + if (!isdigit(ent->d_name[0]) || ent->d_type != DT_REG) + continue; /* sanity check on session file */ + if (get_priv_usr(ent->d_name, privbits, usr)) { + int n; + int dup = 0; + for (n=0; n < nadded; n++) { + if (!strcmp(newmembers[n], usr)) { + dup++; + break; + } + } + if (dup) + continue; + l = snprintf(newmembers[nadded++], sizeof newmembers[0], + "%s", usr); + if (l >= sizeof newmembers[0]) + syslog(LOG_WARNING, + "%s: group %s, member %s truncated to" + " %ld characters", libname, grnam, usr, + sizeof newmembers[0]); + } + } + + if (!nadded) + goto done; + + if (nadded == nmax && ent) { + syslog(LOG_WARNING, + "%s: Only adding %d members to" + " group %s", libname, nmax, grnam); + } + for (nmemb=0, in=gr_in; in && *in; in++) + nmemb++; + + /* copy the original list first; maybe make a common routine later */ + l = (((ptrdiff_t)mem + align) & align); + len -= align; + mem += align; + gr_mem = (char **)mem; + l = sizeof *gr_mem * (nmemb+nadded+1); + len -= l; + mem += l; + + for (midx=0, in=gr_in; in && *in; in++) { + l = strlen(*in) + 1; + len -= l; + if (len < 0) { + *err = ERANGE; + goto done; + } + gr_mem[midx] = mem; + mem += l; + strcpy(gr_mem[midx++], *in); + } + /* now same for users we are adding */ + for(j=0; j * @@ -19,12 +19,18 @@ /* * This plugin implements getpwnam_r for NSS to map any user - * name to a fixed account (from the configuration file). The - * fixed account is used to get the base of the home directory, + * name to one of two account (from the configuration file). + * The base mapping is done if no shell:prv-lvl attribute is + * received, or if the value is less than 15. If the attribute + * is present with the value 15, then we map to the privileged + * mapping account, which typically has the ability to run + * configuration commands. + * The fixed account is used to get the base of the home directory, * and for the uid and gid. All other fields are replaced, and * the password is always returned as 'x' (disabled). The assumption * is that any authentication and authorization will be done via PAM - * using some mechanism other than the local password file. + * using some mechanism other than the local password file, such as + * RADIUS * * Because it will match any account, this should always be the * last module in /etc/nsswitch.conf for the passwd entry @@ -37,16 +43,11 @@ #include "map_common.h" #include +#include +#include static const char *nssname = "nss_mapuser"; /* for syslogs */ -/* - * If you aren't using glibc or a variant that supports this, - * and you have a system that supports the BSD getprogname(), - * you can replace this use with getprogname() - */ -extern const char *__progname; - /* * This is an NSS entry point. * We map any username given to the account listed in the configuration file @@ -63,21 +64,11 @@ enum nss_status _nss_mapname_getpwnam_r(const char *name, struct passwd *pw, enum nss_status status = NSS_STATUS_NOTFOUND; struct pwbuf pbuf; bool islocal = 0; + unsigned session; - /* - * the useradd family will not add/mod/del users correctly with - * the mapuid functionality, so return immediately if we are - * running as part of those processes. - */ - if (__progname && (!strcmp(__progname, "useradd") || - !strcmp(__progname, "usermod") || - !strcmp(__progname, "userdel"))) - return status; - - if (nss_mapuser_config(errnop, nssname) == 1) { - syslog(LOG_NOTICE, "%s: bad configuration", nssname); - return status; - } + if (map_init_common(errnop, nssname)) + return errnop + && *errnop == ENOENT ? NSS_STATUS_UNAVAIL : status; /* * Ignore any name starting with tacacs[0-9] in case a @@ -109,7 +100,7 @@ enum nss_status _nss_mapname_getpwnam_r(const char *name, struct passwd *pw, } } if (islocal) { - if (debug > 1) + if (map_debug > 1) syslog(LOG_DEBUG, "%s: skipped excluded user: %s", nssname, name); return 2; @@ -122,8 +113,288 @@ enum nss_status _nss_mapname_getpwnam_r(const char *name, struct passwd *pw, pbuf.buflen = buflen; pbuf.errnop = errnop; - if (!get_pw_mapuser(name, &pbuf)) + session = get_sessionid(); + if (session && !find_mapped_name(&pbuf, (uid_t) - 1, session)) + status = NSS_STATUS_SUCCESS; + if (status != NSS_STATUS_SUCCESS) { + /* lookup by some unrelated process, try dir lookup */ + if (!find_mappingfile(&pbuf, (uid_t) - 1)) + status = NSS_STATUS_SUCCESS; + else if (!make_mapuser(&pbuf, name)) + status = NSS_STATUS_SUCCESS; + } + + return status; +} + +/* + * The group routines are here so we can substitute mappings for radius_user + * and radius_priv_user when reading /etc/group, so that we can make + * users members of the appropriate groups for various privileged + * (and unprivileged) tasks. + * Ideally, we'd be able to use the getgr* routines specifying compat, + * but the NSS plugin infrastructure doesn't support that, so we have to + * read /etc/group directly, and then do our substitutions. + * + * This won't work if the RADIUS users are in LDAP group and/or password + * files, but that's the way it goes. + * + * For the intended purpose, it works well enough. + * + * We need getgrent() for this one, because initgroups needs it, unlike + * the password file. + */ + +static FILE *grent; + +__attribute__ ((visibility("default"))) +enum nss_status _nss_mapname_setgrent(void) +{ + enum nss_status status = NSS_STATUS_NOTFOUND; + static const char *grpname = "/etc/group"; + int error, *errnop = &error; + + if (map_init_common(errnop, nssname)) + return errnop + && *errnop == ENOENT ? NSS_STATUS_UNAVAIL : status; + + if (grent) { + rewind(grent); status = NSS_STATUS_SUCCESS; + goto done; + } + + grent = fopen(grpname, "r"); + if (!grent) { + syslog(LOG_WARNING, "%s: failed to open %s: %m", + nssname, grpname); + status = NSS_STATUS_UNAVAIL; + } else { + status = NSS_STATUS_SUCCESS; + /* don't leave fd open across execs */ + (void)fcntl(fileno(grent), F_SETFD, FD_CLOEXEC); + } + done: + return status; +} + +__attribute__ ((visibility("default"))) +enum nss_status _nss_mapname_endgrent(void) +{ + if (grent) { + FILE *f = grent; + grent = NULL; + (void)fclose(f); + } + return NSS_STATUS_SUCCESS; +} + +/* + * do the fixups and copies, using the passed in buffer. result must + * have been checked to be sure it's non-NULL before calling. + */ +static int fixup_grent(struct group *entry, struct group *result, char *buf, + size_t lenbuf, int *errp) +{ + char **grusr, **new_grmem = NULL; + struct group *newg; + long long l, len; /* size_t unsigned on some systems */ + int err, members, memlen; + int ret = NSS_STATUS_NOTFOUND; + unsigned pmatch = 0; + char *nm = entry->gr_name ? entry->gr_name : "(nil)"; + + if (!result) /* should always be non-NULL, just cautious */ + return ret; + + len = lenbuf; + if (!errp) /* to reduce checks below */ + errp = &err; + *errp = 0; + + newg = (struct group *)buf; + len -= sizeof *newg; + buf += sizeof *newg; + if (len < 0) { + *errp = ENOMEM; + return ret; + } + newg->gr_gid = entry->gr_gid; + l = snprintf(buf, len, "%s", entry->gr_name); + newg->gr_name = buf; + len -= l + 1; + buf += l + 1; + if (len > 0) { + l = snprintf(buf, len, "%s", entry->gr_passwd); + newg->gr_passwd = buf; + len -= l + 1; + buf += l + 1; + } + if (len < 0) { + *errp = ENOMEM; + return NSS_STATUS_TRYAGAIN; + } + + for (memlen = members = 0, grusr = entry->gr_mem; grusr && *grusr; + grusr++) { + if (mapped_priv_user && !strcmp(mapped_priv_user, *grusr)) + pmatch |= PRIV_MATCH; + else if (mappeduser && !strcmp(mappeduser, *grusr)) + pmatch |= UNPRIV_MATCH; + members++; + memlen += strlen(*grusr) + 1; + } + if (pmatch) { /* one or both mapped users are in gr_mem */ + size_t usedbuf = len; + new_grmem = fixup_gr_mem(nm, (const char **)entry->gr_mem, + buf, &usedbuf, errp, pmatch); + buf += usedbuf; + len -= usedbuf; + if (errp) { + if (*errp == ERANGE) + ret = NSS_STATUS_TRYAGAIN; + else if (*errp == ENOENT) + ret = NSS_STATUS_UNAVAIL; + + } else if (len < 0) { + *errp = ERANGE; + ret = NSS_STATUS_TRYAGAIN; + } + } + if (*errp) + goto done; + *result = *newg; + if (new_grmem) + result->gr_mem = new_grmem; + else { + char **sav, **entgr, *usrbuf; + len -= (members + 1) * sizeof *new_grmem; + len -= memlen; + if (len < 0) { + *errp = ERANGE; + ret = NSS_STATUS_TRYAGAIN; + goto done; + } + sav = result->gr_mem = (char **)buf; + buf += (members + 1) * sizeof *new_grmem; + usrbuf = buf; + + for (entgr = entry->gr_mem; entgr && *entgr; entgr++, sav++) { + *sav = usrbuf; + usrbuf += strlen(*entgr) + 1; + strcpy(*sav, *entgr); + } + + *sav = NULL; + } + ret = NSS_STATUS_SUCCESS; + done: + return ret; +} + +/* + * No locking needed because our only global is the dirent * for + * the runuser directory, and our use of that should be thread safe + */ +__attribute__ ((visibility("default"))) +enum nss_status _nss_mapname_getgrent_r(struct group *gr_result, + char *buffer, size_t buflen, + int *errnop) +{ + enum nss_status status = NSS_STATUS_NOTFOUND; + struct group *ent; + int ret = 1; + + if (!gr_result) { + if (errnop) + *errnop = EFAULT; + return status; + } + + if (map_init_common(errnop, nssname)) + return errnop + && *errnop == ENOENT ? NSS_STATUS_UNAVAIL : status; + + if (!grent) { + status = _nss_mapname_setgrent(); + if (status != NSS_STATUS_SUCCESS) + return status; + } + + ent = fgetgrent(grent); + if (!ent) { + int e = errno; + if (ferror(grent)) { + syslog(LOG_WARNING, + "%s: error reading group information: %m", + nssname); + errno = e; + } else + errno = 0; + return status; + } + ret = fixup_grent(ent, gr_result, buffer, buflen, errnop); + return ret; +} + +__attribute__ ((visibility("default"))) +enum nss_status _nss_mapname_getgrnam_r(const char *name, struct group *gr, + char *buffer, size_t buflen, + int *errnop) +{ + enum nss_status status = NSS_STATUS_NOTFOUND; + struct group *ent; + + if (!gr) { + if (errnop) + *errnop = EFAULT; + return status; + } + + if (map_init_common(errnop, nssname)) + return errnop + && *errnop == ENOENT ? NSS_STATUS_UNAVAIL : status; + + if (_nss_mapname_setgrent() != NSS_STATUS_SUCCESS) + return status; + + for (ent = fgetgrent(grent); ent; ent = fgetgrent(grent)) { + if (!strcmp(ent->gr_name, name)) { + status = fixup_grent(ent, gr, buffer, buflen, errnop); + break; + } + } + + return status; +} + +__attribute__ ((visibility("default"))) +enum nss_status _nss_mapname_getgrgid_r(gid_t gid, struct group *gr, + char *buffer, size_t buflen, + int *errnop) +{ + enum nss_status status = NSS_STATUS_NOTFOUND; + struct group *ent; + + if (!gr) { + if (errnop) + *errnop = EFAULT; + return status; + } + + if (map_init_common(errnop, nssname)) + return errnop + && *errnop == ENOENT ? NSS_STATUS_UNAVAIL : status; + + if (_nss_mapname_setgrent() != NSS_STATUS_SUCCESS) + return status; + + for (ent = fgetgrent(grent); ent; ent = fgetgrent(grent)) { + if (ent->gr_gid == gid) { + status = fixup_grent(ent, gr, buffer, buflen, errnop); + break; + } + } return status; } diff --git a/nss_mapuid.c b/nss_mapuid.c index cc6c5ec..acee3f5 100644 --- a/nss_mapuid.c +++ b/nss_mapuid.c @@ -43,119 +43,8 @@ #include "map_common.h" #include -#include -#include -#include static const char *nssname = "nss_mapuid"; /* for syslogs */ -static const char dbdir[] = "/run/mapuser/"; - -/* - * If you aren't using glibc or a variant that supports this, - * and you have a system that supports the BSD getprogname(), - * you can replace this use with getprogname() - */ -extern const char *__progname; - -/* - * Read the requested session file (in the dbdir by intent), verify the - * uid matches, and setup the passwd structure with the username found - * in the file. - */ -static int chk_session_file(char *sfile, uid_t uid, struct pwbuf *pb) -{ - char rbuf[256], user[64]; - FILE *mapf; - uid_t auid = 0; - int ret = 1; - - mapf = fopen(sfile, "r"); - if (!mapf) { - if (debug) - syslog(LOG_DEBUG, - "%s: session map file %s open fails: %m", - nssname, sfile); - return ret; - } - user[0] = '\0'; - while (fgets(rbuf, sizeof rbuf, mapf)) { - strtok(rbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ - if (!strncmp("user=", rbuf, 5)) { /* should precede auid */ - snprintf(user, sizeof user, "%s", rbuf + 5); - if (auid) /* found out of order, but now have both */ - break; - } else if (!strncmp("auid=", rbuf, 5)) { - uid_t fuid = (uid_t) strtoul(rbuf + 5, NULL, 10); - if (fuid && uid == fuid) { - auid = fuid; - if (user[0]) - break; /* normal ordering, else keep looking for user */ - } - } - } - fclose(mapf); - - if (auid && user[0]) /* otherwise not a match */ - ret = get_pw_mapuser(user, pb); /* should always succeed */ - - return ret; -} - -/* find mapping for this sessionid */ -static int find_mapped_name(struct pwbuf *pb, uid_t uid, uint32_t session) -{ - char sessfile[sizeof dbdir + 11]; - - snprintf(sessfile, sizeof sessfile, "%s%u", dbdir, session); - return chk_session_file(sessfile, uid, pb); -} - -static int find_mappingfile(struct pwbuf *pb, uid_t uid) -{ - DIR *dir; - struct dirent *ent; - int ret = 1; - - dir = opendir(dbdir); - if (!dir) { /* can happen if no mapped users logged in */ - if (debug > 1) - syslog(LOG_DEBUG, - "%s: Unable to open mapping directory %s: %m", - nssname, dbdir); - return 1; - } - - /* Loop through all numeric files in dbdir, check for matching uid */ - while (ret && (ent = readdir(dir))) { - char sessfile[sizeof dbdir + 11]; - if (!isdigit(ent->d_name[0])) /* sanity check on session file */ - continue; - snprintf(sessfile, sizeof sessfile, "%s%s", dbdir, ent->d_name); - ret = chk_session_file(sessfile, uid, pb); - } - if (ret && debug) - syslog(LOG_DEBUG, "%s: uid %u mapping not found in map files", - nssname, uid); - closedir(dir); - return ret; -} - -static uint32_t get_sessionid(void) -{ - int fd = -1, cnt; - uint32_t id = 0U; - static char buf[12]; - - fd = open("/proc/self/sessionid", O_RDONLY); - if (fd != -1) { - cnt = read(fd, buf, sizeof(buf)); - close(fd); - } - if (fd != -1 && cnt > 0) { - id = strtoul(buf, NULL, 0); - } - return id; -} /* * This is an NSS entry point. @@ -189,27 +78,14 @@ enum nss_status _nss_mapuid_getpwuid_r(uid_t uid, struct passwd *pw, enum nss_status status = NSS_STATUS_NOTFOUND; uint32_t session; - /* - * the useradd family will not add/mod/del users correctly with - * the mapuid functionality, so return immediately if we are - * running as part of those processes. - */ - if (__progname && (!strcmp(__progname, "useradd") || - !strcmp(__progname, "usermod") || - !strcmp(__progname, "userdel"))) + if (map_init_common(errnop, nssname)) return status; - /* this can happen for permission reasons, do don't complain except - * at debug */ - if (nss_mapuser_config(errnop, nssname) == 1) { - return status; /* syslog already done */ - } - - if (min_uid != ~0U && uid < min_uid) { - if (debug > 1) + if (map_min_uid != ~0U && uid < map_min_uid) { + if (map_debug > 1) syslog(LOG_DEBUG, - "%s: uid %u < min_uid %u, don't lookup", nssname, - uid, min_uid); + "%s: uid %u < map_min_uid %u, don't lookup", + nssname, uid, map_min_uid); return status; } diff --git a/nss_mapuser.5 b/nss_mapuser.5 index 3e17aec..6e63138 100644 --- a/nss_mapuser.5 +++ b/nss_mapuser.5 @@ -1,21 +1,22 @@ .TH nss_mapuser 5 -.\" Copyright 2017 Cumulus Networks, Inc. All rights reserved. +.\" Copyright 2017, 2018 Cumulus Networks, Inc. All rights reserved. .SH NAME nss_mapuser.conf \- NSS mapuser configuration file .SH DESCRIPTION -This is the configuration file for the NSS mapuser plugins. +This is the configuration file for the NSS mapuser and mapuid plugins. See the .BR nss_mapuser (8) -manpage for more general information on the plugin. -This configuration file controls debug settings, the local account used -for mapping users, and which usernames (accounts) and uids are skipped. -.PP +manpage for more general information on the plugins. +.P +This configuration file controls debug settings, the local accounts used for mapping +users, and the list of usernames (accounts) and uids that are skipped (not looked up by +this plugin). .TP .I debug=NUMBER Output lookup debugging information via syslog(3). .TP .I exclude_users=user1,user2... -Usernames (accounts) comma separate list. This is used by mapname NSS plugin getpwuid() +Usernames (accounts) comma separate list. This is used by the NSS mapuser plugin getpwuid() entry point. The account to be looked up is checked against this list. If a match is found, an immediate NOTFOUND status is returned. This reduces overhead for the standard local user accounts. The @@ -23,19 +24,48 @@ local user accounts. The and .I mapped_priv_user fields from the -configuration file are always skipped, as are any names starting with +configuration file are always skipped, as are any names starting with .BR tacacs[0-9] . .TP +.I min_uid=NUMBER +UID's passed to the NSS mapuid plugin getpwuid() entry point that are below this value +cause an immediate NOTFOUND status to be returned. This reduces +overhead for the standard local user accounts. +.BR NOTE : +The value must be less than the uid of the mapped account names below. +.TP +.I mapped_priv_user=NAME +This is the local account used as a template for privileged logins +(the RADIUS VSA +.BR shell:priv-lvl=15 ) +was returned by the server. +This must be a local account (found in +.IR /etc/passwd ). +When a uid or name match is found, this account information is read from +.I /etc/passwd +and used as a template for the matching account. The default at installation is +.BR radius_priv_user . +.TP .I mapped_user=NAME -This is the local account which mapping uses as a template. It must be a local -account (found in +This is the local account used as a template for unprivileged logins +(either no privilege attribute was returned by the server, or the privilege level is +is in the range 0-14. This must be a local account (found in .IR /etc/passwd ). When a uid or name match is found, this account information is read from .I /etc/passwd -and used as a template for the matching account. The +and used as a template for the matching account. The default at installation is +.BR radius_user . +.P +For the +.I map_user +and +.I map_priv_user +accounts, the user information that is returned via the +.BR getpwnam (3) +group of account lookup routines has the .B pw_name field (user account name) -is replaced with the original (login) name, and the original name is +replaced with the original (login) name, and the original name is inserted at the beginning of the .B pw_gecos field. The @@ -43,8 +73,12 @@ field. The (home directory) field replaces the last component of the directory path with the original login name. -.IP -When changing this field to a different name than the default, be sure the account exists in +.P +When changing the +.I map_user +or +.I map_priv_user +fields to a different account than the default, be sure the account exists in .IR /etc/passwd , and that the account was created as disabled or locked (does not have a legal password, so the @@ -54,11 +88,22 @@ account can not be used for logins. When using to create these accounts, use the .B --disabled-login argument to disable login for the account. -.TP -.I min_uid=NUMBER -UID's passed to the mapuid NSS plugin getpwuid() entry point that are below this value -cause an immediate NOTFOUND status to be returned. This reduces -overhead for the standard local user accounts. +.P +At installation, the +.I map_user +user account +.B radius_user +is added to the +.I netshow +group so that the user can run NCLU +.B net show +commands. Similarly, the +.I map_priv_user +user account +.B radius_priv_user +is added to the +.I netedit +group so that the user can run NCLU configuration commands. .SH "SEE ALSO" .BR adduser (8), .BR pam_radius_auth (8), diff --git a/nss_mapuser.8 b/nss_mapuser.8 index 1b258c5..73e0275 100644 --- a/nss_mapuser.8 +++ b/nss_mapuser.8 @@ -1,5 +1,5 @@ .TH nss_mapuser 8 -.\" Copyright 2017 Cumulus Networks, Inc. All rights reserved. +.\" Copyright 2017, 2018 Cumulus Networks, Inc. All rights reserved. .SH NAME libnss_mapname.so.2 \- NSS mapuser plugin .br @@ -14,16 +14,50 @@ provide enough information to define a linux account (uid, gid, home directory). The traditional method was to add all RADIUS users to the local .I /etc/passwd file, or to enable them via other means such as LDAP. +.P These plugins allow RADIUS users to login with no configuration other than the initial setup of the RADIUS client, and these plugins. -.PP +.P The plugins work by mapping user accounts to a named account in a configuration file, and using the named account as a template for the requested account. -.PP -The named account (default is -.I radius_user) +.P +The named accounts must be present in -.IR /etc/passwd . +.IR /etc/passwd , +and the groups set up correctly in +.IR /etc/group +for these plugins to work correctly. +.P +The default accounts are +.I radius_priv_user +for privileged logins with +the RADIUS VSA +.BR shell:priv-lvl=15 ) +attribute, and +.I radius_user +for logins without that attribute, or with the privilege level 0-14. +The accounts are created when the debian package is installed. +.P +The mapname plugin also supplies NSS functions for the group file, in +order to map RADIUS logins into appropriate groups. For this to work, +the two RADIUS accounts above are added to the +.BR sudo , +.BR netshow , +and +.B netedit +groups during the installation of the debian packge. The privileged account +is made a member of the +.B sudo +and +.B netedit +groups, while the unprivileged account is made a member of the +.B netshow +group. This can be verified after logging in by using the +.IR id (1), +or +.IR groups (1) +command to list the groups of which you are a member. +.P The .B pw_name field (user account name) @@ -67,19 +101,31 @@ file with the same UID. There are two separate plugins, .B libnss_mapname for user account names -.RI ( getpwnam() (3)), +.RI ( getpwnam() (3) +and +.RI ( getpwnam_r() (3)), +as well as +.RI ( getgrnam() (3), +.RI ( getpgram_r() (3)), +and +.RI ( getpgrent() (3)), and .B libnss_mapuid for uid -.RI ( getpwuid() (3)), -Two separate plugins are required. -.PP +.RI ( getpwuid() (3) +and +.RI ( getpwuid_r() (3)). +.P +Two separate plugins are required due to ordering requirements in +.IR /etc/nsswitch.conf . +.P The name lookup .B mapuser must be the last method used (last plugin on the .B passwd database), because it will always produce a successful lookup on -any user account name, unless there are configuration or other errors. +any user account name, unless the name has has been excluded, or if +there are configuration or other errors. .PP The uid lookup .B mapuid @@ -87,24 +133,29 @@ must be the first method used (first plugin on the .B passwd database), because the uid will always match a local account from .IR /etc/passwd , -any user account name, unless there are configuration or other errors. +any user account name, unless limited by the minimum uid configuration, or +if there are configuration or other errors. .PP -The flat file database is created using the -.B pam_script -plugin. In addition to creating and deleting files at session start and -end, the open script will also create the home directory using -.IR mkhomedir_helper . +The flat file database used by these plugins is created and removed by the +.B pam_radius_auth +plugin from the libpam-radius-auth package. +In addition to creating and deleting files at session start and end, the +.B pam_radius_auth +plugin will also create the home directory using the +.I mkhomedir_helper +program. .SH "SEE ALSO" .BR adduser (8), .BR mkhomedir_helper (8), .BR pam_radius_auth (8), -.BR pam_script (8), .BR nss_mapuser (5), .BR nsswitch.conf (5), .BR getpwuid (3), .BR getpwnam (3), .BR getent (1). .SH FILES +.I /etc/nsswitch.conf +- configuration file for NSS plugins. It is modified at package installation .I /etc/nss_mapuser.conf - mapuser NSS plugin configuration parameters. .br diff --git a/nss_mapuser.conf b/nss_mapuser.conf index 2685ac0..c5f2098 100644 --- a/nss_mapuser.conf +++ b/nss_mapuser.conf @@ -35,3 +35,4 @@ exclude_users=root,daemon,nobody,cron,sshd,cumulus,quagga,frr,snmp,www-data,ntp, # Map all usernames to the radius_user account (use the uid, gid, shell, and # base of the home directory from the cumulus entry in /etc/passwd). mapped_user=radius_user +mapped_priv_user=radius_priv_user diff --git a/pam_script_ses_close b/pam_script_ses_close deleted file mode 100755 index 8340543..0000000 --- a/pam_script_ses_close +++ /dev/null @@ -1,85 +0,0 @@ -#! /bin/bash -# Copyright 2017 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 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -# This script is invoked via pam_script.so for session close, to -# clean up the mapping setup on session open. The info is used -# in the libnss_mapuser getpwuid() entry point. - -# auid is currently unused, but must match the uid of the mapped_user -# in the libnss_mapuser database for this to be valid - -# For this to work, pam_loginuid.so must be used, so both the -# loginuid and the sessionid are unique values > 0 - -dbdir=/run/mapuser -mkdir -p $dbdir - -read sess < /proc/$$/sessionid -read auid < /proc/$$/loginuid - -# never map root user, or when loginuid isn't set, or when -# we aren't doing mapping (env variable not set) -if [ "$auid" -eq 0 ]; then exit 0; fi - -# for debugging, if needed -#DEBUG logger -t mapuser $0 user=$PAM_USER pid=$$ session="$sess" auid="$auid" - -if [ "$sess" -le 0 ] ; then - logger -t $0 sessionid not set, no mapuser cleanup for \ - PID $$ user $PAM_USER - exit 0 # never trigger an error -fi - -file=$dbdir/$sess -if [ -e $file ]; then - IFS='= -' read tag fauid <<< $(grep '^auid=' $file) - IFS='= -' read tag fsess <<< $(grep '^session=' $file) - # If info doesn't match, report it, and don't clean up - if [ "$auid" != "$fauid" -o "$sess" != "$fsess" ]; then - logger -t $0 "Session $sess mismatch auid $auid,$fauid session $sess,$fsess" - else - uid=$(id -u) - if [ "$uid" -ne 0 ]; then # shouldn't happen from pam_script - logger -t $0 called with UID=$uid, no cleanup - exit 0 - fi - pids=( $(egrep -w $fsess /proc/[1-9]*/sessionid | \ - sed -e 's,/proc/,,' -e 's,/.*,,') ) - clean=1 - for pid in ${pids[*]}; do - [ $pid -eq $$ ] && continue # skip ourselve - read cmd 2>/dev/null < /proc/$pid/comm # ignore exited egrep, sed - [ -z "$cmd" ] && continue # pid exited - msg="$msg PID $pid comm=$cmd" - case "$cmd" in - sshd|sudo|login|su|telnetd) ;; - *) clean=0 ; cleancmd="$cmd" ;; - esac - done - #DEBUG logger -t $0 sess=$fsess clean=$clean cmd=$cleancmd has $msg active - [ $clean -eq 1 ] && { - #DEBUG logger -t $0 cleanup session $fsess - rm -f $file - } - fi -fi - -# always succeed, this should not cause sessions shutdown errors -exit 0 diff --git a/pam_script_ses_open b/pam_script_ses_open deleted file mode 100755 index 731e250..0000000 --- a/pam_script_ses_open +++ /dev/null @@ -1,65 +0,0 @@ -#! /bin/bash -# Copyright 2017 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 3 of the License, or -# (at your option) any later version. - -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. - -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - -# This script is invoked via pam_script.so for session open, used for mapping -# RADIUS usernames to the mapped uid, for libnss_mapuser getpwuid() entry -# point. - -# auid is currently unused, but must match the uid of the mapped_user -# in the libnss_mapuser database for this to be valid - -# For this to work, pam_loginuid.so must be used, so both the -# loginuid and the sessionid are unique values > 0 - -umask 022 # want everything world-readable. - -dbdir=/run/mapuser -mkdir -p $dbdir - -read sess < /proc/$$/sessionid -read auid < /proc/$$/loginuid - -# for debugging, if needed -# logger -t mapuser $0 called with $PAM_USER pid=$$ session="$sess" auid="$auid" - -# never map root user, or when loginuid isn't set, or when -# we aren't doing mapping (env variable not set) -if [ "$auid" -eq 0 ]; then exit 0; fi - -# handle this one differently, since it means something is -# configured wrong. -if [ "$sess" -le 0 ] ; then - logger -t $0 sessionid not set, no mapping possible for \ - PID $$ user $PAM_USER - exit 0 # still allow the session -fi - -# if user's home directory doesn't exist, create it and populate -# it with the standard skeleton files. -hdir=$(eval echo ~$PAM_USER) -[ -d "$hdir" ] || /sbin/mkhomedir_helper $PAM_USER - -# don't overwrite if it already exists. Happens when sudo or su -# is run from an existing mapped session. -[ -s $dbdir/$sess ] || \ - date +"%FT%T.%N%nuser=$PAM_USER%npid=$$%nauid=$auid%nsession=$sess%nhome=$hdir" \ - > $dbdir/$sess - -# always succeed, this should not block sessions on errors -exit 0 - -- cgit v1.2.3 From 19e1c2b3cfd99d7ea62e4e8a827febcd7ec15e19 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Tue, 3 Apr 2018 00:23:16 -0700 Subject: bumped version number to 1.1.0 given significant new features --- debian/changelog | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/debian/changelog b/debian/changelog index 50032b5..d11304d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,13 +1,14 @@ -libnss-mapuser (1.0.0-cl3u3) RELEASED; urgency=low +libnss-mapuser (1.1.0-cl3u1) RELEASED; urgency=low * Closes CM-19866 - Fixed exclude_users not skipped, and added more system accounts to exclude_users: www-data,man, tacacs[0-9]*. - * New Enabled - When Vendor Specific Option containing shell:priv-lvl - is present, and the value is 15, map to user radius_priv_user, and - give that user account more privileges, similar to tacplus client - privilege 15. + * New Enabled - When Vendor Specific Option containing shell:priv-lvl is + present, and the user is privileged (determined by libpam-radius-auth) + group membership is modified to add the radius accounts to the groups that + have radius_user and/or radius_priv_user, to give that that user account + more privileges, similar to tacplus client privilege 15. - -- dev-support Mon, 26 Feb 2018 09:51:44 -0800 + -- dev-support Tue, 03 Apr 2018 00:13:47 -0700 libnss-mapuser (1.0.0-cl3u2) RELEASED; urgency=low -- cgit v1.2.3 From 4b24261d4a28ed1cd994939d6fa658b44c30a0ac Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Fri, 6 Apr 2018 15:06:27 -0700 Subject: cleanup group mapname in nsswitch on remove; add priv user to sudo group Needed to add sed code to remove mapname from nsswitch.conf group search line, similar to passwd line. Somehow forgot that when I added the code to add it in postinst I also somehow forgot to checkin the adduser line to add the radius_priv_user to the sudo group, so fixed that too. --- debian/changelog | 2 +- debian/libnss-mapuser.postinst | 1 + debian/libnss-mapuser.prerm | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index d11304d..190d66d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,7 +8,7 @@ libnss-mapuser (1.1.0-cl3u1) RELEASED; urgency=low have radius_user and/or radius_priv_user, to give that that user account more privileges, similar to tacplus client privilege 15. - -- dev-support Tue, 03 Apr 2018 00:13:47 -0700 + -- dev-support Fri, 06 Apr 2018 15:46:56 -0700 libnss-mapuser (1.0.0-cl3u2) RELEASED; urgency=low diff --git a/debian/libnss-mapuser.postinst b/debian/libnss-mapuser.postinst index ee6a70d..13bf0d0 100644 --- a/debian/libnss-mapuser.postinst +++ b/debian/libnss-mapuser.postinst @@ -32,6 +32,7 @@ case "$1" in adduser --quiet radius_user netshow # privileged radius logins can run net config commands, as well as show adduser --quiet radius_priv_user netedit + adduser --quiet radius_priv_user sudo exit 0 ) ;; diff --git a/debian/libnss-mapuser.prerm b/debian/libnss-mapuser.prerm index af98f1b..c4f259f 100644 --- a/debian/libnss-mapuser.prerm +++ b/debian/libnss-mapuser.prerm @@ -8,6 +8,7 @@ if [ "$1" = remove ]; then if [ -e "/etc/nsswitch.conf" ]; then sed -i -e '/^passwd:.*mapuid[ \t]/s/mapuid[ \t]//' \ -e '/^passwd:.*[ \t]mapname/s/[ \t]mapname//' \ + -e '/^group:.*[ \t]mapname/s/[ \t]mapname//' \ /etc/nsswitch.conf || true # don't prevent remove on error fi fi -- cgit v1.2.3 From 613f1949208809a116bd1b3737e39a1599bf7d43 Mon Sep 17 00:00:00 2001 From: Dave Olson Date: Fri, 13 Apr 2018 01:04:39 -0700 Subject: Changes and cleanups around the privileged user. Ticket: CM-19457 Reviewed By: nobody Testing Done: lots of logins, and login combinations Validate that the mapuser database files are valid by using kill 0 on the pids. If not valid, try to unlink, and if we do, report it as a DEBUG. If we can't unlink (not root) report that we are skipping at INFO. As part of that, don't count valid fields and read entire file. Document in man page and config file that the mapped_priv_user account is known to libpam-radius-auth as well, and must be updated in both places if it changes. Updated the public symbols (the getgr additions) from previous commit Fixed some white space and line length issues. --- debian/changelog | 2 +- debian/libnss-mapuser.postinst | 7 ++++-- debian/libnss-mapuser.symbols | 5 ++++ map_common.c | 53 ++++++++++++++++++++++++++++-------------- nss_mapuser.5 | 3 +++ nss_mapuser.conf | 2 ++ 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/debian/changelog b/debian/changelog index 190d66d..d0ea75f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,7 +8,7 @@ libnss-mapuser (1.1.0-cl3u1) RELEASED; urgency=low have radius_user and/or radius_priv_user, to give that that user account more privileges, similar to tacplus client privilege 15. - -- dev-support Fri, 06 Apr 2018 15:46:56 -0700 + -- dev-support Fri, 13 Apr 2018 16:19:08 -0700 libnss-mapuser (1.0.0-cl3u2) RELEASED; urgency=low diff --git a/debian/libnss-mapuser.postinst b/debian/libnss-mapuser.postinst index 13bf0d0..54c661b 100644 --- a/debian/libnss-mapuser.postinst +++ b/debian/libnss-mapuser.postinst @@ -24,10 +24,13 @@ case "$1" in /etc/nsswitch.conf fi addgroup --quiet $rgroup 2>&1 | grep -v 'already exists' + ourshell=/bin/bash # not radius_shell, has pkg ordering issues. adduser --quiet --firstuid 1000 --disabled-login --ingroup $rgroup \ - --gecos "radius user" radius_user 2>&1 | grep -v 'already exists' + --gecos "radius user" --shell $ourshell radius_user 2>&1 | + grep -v 'already exists' adduser --quiet --firstuid 1000 --disabled-login --ingroup $rgroup \ - --gecos "radius privileged user" radius_priv_user 2>&1 | grep -v 'already exists' + --gecos "radius privileged user" --shell $ourshell radius_priv_user 2>&1 | + grep -v 'already exists' # regular radius logins can run net show commands adduser --quiet radius_user netshow # privileged radius logins can run net config commands, as well as show diff --git a/debian/libnss-mapuser.symbols b/debian/libnss-mapuser.symbols index 2254c2e..c5a3ed0 100644 --- a/debian/libnss-mapuser.symbols +++ b/debian/libnss-mapuser.symbols @@ -1,4 +1,9 @@ libnss_mapname.so.2 libnss-mapuser #MINVER# + _nss_mapname_setgrent@Base 1.1.0-cl3u1 + _nss_mapname_endgrent@Base 1.1.0-cl3u1 + _nss_mapname_getgrent_r@Base 1.1.0-cl3u1 + _nss_mapname_getgrgid_r@Base 1.1.0-cl3u1 + _nss_mapname_getgrnam_r@Base 1.1.0 _nss_mapname_getpwnam_r@Base 1.0.0 libnss_mapuid.so.2 libnss-mapuser #MINVER# diff --git a/map_common.c b/map_common.c index 830d85a..b6fe3bc 100644 --- a/map_common.c +++ b/map_common.c @@ -26,6 +26,7 @@ #include "map_common.h" #include #include +#include #include #include #include @@ -293,7 +294,7 @@ get_pw_mapuser(const char *name, struct pwbuf *pb, uid_t mapuid, int privileged) for (ret = 1; ret && (ent = fgetpwent(pwfile));) { if (!ent->pw_name) continue; /* shouldn't happen */ - if (!strcmp(ent->pw_name, name) || + if (!strcmp(ent->pw_name, name) || /* added locally */ !strcmp(ent->pw_name, privileged ? mapped_priv_user : mappeduser) || ent->pw_uid == mapuid) { ret = @@ -320,7 +321,6 @@ static int chk_session_file(char *session, uid_t uid, struct pwbuf *pb) FILE *mapf; uid_t auid = 0; int ret = 1, privileged = 0;; - int gotinfo = 0; /* user, session, auid */ snprintf(sessfile, sizeof sessfile, "%s%s", dbdir, session); @@ -333,46 +333,65 @@ static int chk_session_file(char *session, uid_t uid, struct pwbuf *pb) return ret; } user[0] = '\0'; - while (gotinfo != 4 && fgets(rbuf, sizeof rbuf, mapf)) { - strtok(rbuf, " \t\n\r\f"); /* terminate buffer at first whitespace */ + while (fgets(rbuf, sizeof rbuf, mapf)) { + /* terminate buffer at first whitespace */ + strtok(rbuf, " \t\n\r\f"); if (!strncmp("user=", rbuf, 5)) { if (pb->name && strcmp(rbuf + 5, pb->name)) break; snprintf(user, sizeof user, "%s", rbuf + 5); - gotinfo++; + } else if (!strncmp("pid=", rbuf, 4)) { + char *ok; + unsigned pid = (unsigned) strtoul(rbuf + 4, &ok, 10); + if (ok != (rbuf + 4) && pid > 0 && kill(pid, 0) && + errno == ESRCH) { + /* ESRCH instead of any error because perms as + * non-root. Try to unlink, since we often + * run as root; report as DEBUG if we unlink, + * report as INFO if not */ + if (unlink(sessfile) == 0) + syslog(LOG_DEBUG, "session file %s" + " PID=%u no longer active," + " removed", sessfile, pid); + else + syslog(LOG_INFO, "session file %s" + " PID=%u no longer active, skip", + sessfile, pid); + auid = 0; /* force fail */ + break; + } } else if (!strncmp("auid=", rbuf, 5)) { char *ok; uid_t fuid = (uid_t) strtoul(rbuf + 5, &ok, 10); if (ok != (rbuf + 5)) { - gotinfo++; if (uid != -1 && fuid != uid) { - auid = fuid; - break; /* getpwuid, but uid/auid mismatch, nogo */ + /* getpwuid call but mismatch, nogo */ + break; } else auid = fuid; } } else if (!strcasecmp("privileged=yes", rbuf)) { privileged = 1; - gotinfo++; - } else if (!strcasecmp("privileged=no", rbuf)) - gotinfo++; - else if (!strncmp("session=", rbuf, 8)) { + } else if (!strcasecmp("privileged=no", rbuf)) { + privileged = 0; + } else if (!strncmp("session=", rbuf, 8)) { /* structural problem, so log warning */ if (strcmp(session, rbuf + 8)) { syslog(LOG_WARNING, - "%s: session field \"%s\" mismatch in %s", + "%s: session \"%s\" mismatch in %s", libname, rbuf, sessfile); - } else - gotinfo++; + auid = 0; /* force a skip */ + } } } fclose(mapf); - if (auid && user[0]) { /* otherwise not a match */ + if (auid && (uid == (uid_t)-1 || auid == uid) && user[0]) { if (!pb->name) pb->name = user; /* uid lookups */ ret = get_pw_mapuser(user, pb, auid, privileged); } + /* otherwise not a match */ return ret; } @@ -429,7 +448,7 @@ int find_mappingfile(struct pwbuf *pb, uid_t uid) int make_mapuser(struct pwbuf *pb, const char *name) { int ret; - ret = get_pw_mapuser(mappeduser, pb, (uid_t) - 1, 0); + ret = get_pw_mapuser(mappeduser, pb, (uid_t)-1, 0); return ret; } diff --git a/nss_mapuser.5 b/nss_mapuser.5 index 6e63138..92aff16 100644 --- a/nss_mapuser.5 +++ b/nss_mapuser.5 @@ -45,6 +45,9 @@ When a uid or name match is found, this account information is read from .I /etc/passwd and used as a template for the matching account. The default at installation is .BR radius_priv_user . +It must match the privileged account name in +.IR pam_radius_auth (5) +for correct operation. .TP .I mapped_user=NAME This is the local account used as a template for unprivileged logins diff --git a/nss_mapuser.conf b/nss_mapuser.conf index c5f2098..1ed70d1 100644 --- a/nss_mapuser.conf +++ b/nss_mapuser.conf @@ -35,4 +35,6 @@ exclude_users=root,daemon,nobody,cron,sshd,cumulus,quagga,frr,snmp,www-data,ntp, # Map all usernames to the radius_user account (use the uid, gid, shell, and # base of the home directory from the cumulus entry in /etc/passwd). mapped_user=radius_user +# if you change the account for mapped_priv_user, you must change it in +# /etc/pam_radius_auth.conf as well. mapped_priv_user=radius_priv_user -- cgit v1.2.3