diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | debian/changelog | 4 | ||||
-rw-r--r-- | debian/control | 4 | ||||
-rw-r--r-- | debian/libnss-mapuser.postinst | 8 | ||||
-rw-r--r-- | debian/mapuser | 6 | ||||
-rwxr-xr-x | debian/rules | 6 | ||||
-rw-r--r-- | map_common.c | 476 | ||||
-rw-r--r-- | map_common.h | 19 | ||||
-rw-r--r-- | nss_mapname.c | 325 | ||||
-rw-r--r-- | nss_mapuid.c | 134 | ||||
-rw-r--r-- | nss_mapuser.5 | 83 | ||||
-rw-r--r-- | nss_mapuser.8 | 87 | ||||
-rw-r--r-- | nss_mapuser.conf | 1 | ||||
-rwxr-xr-x | pam_script_ses_close | 85 | ||||
-rwxr-xr-x | pam_script_ses_open | 65 |
15 files changed, 907 insertions, 399 deletions
@@ -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 <dev-support@cumulusnetworks.com> 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 <dev-support@cumulusnetworks.com> -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 <sys/stat.h> +#include <stddef.h> +#include <fcntl.h> +#include <dirent.h> +#include <ctype.h> +#include <libaudit.h> 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<nadded; j++) { + l = strlen(newmembers[j]) + 1; + len -= l; + if (len < 0) { + *err = ERANGE; + goto done; + } + gr_mem[midx] = mem; + mem += l; + strcpy(gr_mem[midx++], newmembers[j]); + } + gr_mem[midx] = NULL; /* terminate the list */ + done: + + if (dir) + closedir(dir); + if (*err || !nadded) { + out = NULL; + *lenp = 0; + } else { + out = gr_mem; + *lenp = mem - buf; + } + return out; +} diff --git a/map_common.h b/map_common.h index b3401ea..e8e051d 100644 --- a/map_common.h +++ b/map_common.h @@ -49,10 +49,19 @@ struct pwbuf { extern char *exclude_users; extern char *mappeduser; extern char *mapped_priv_user; -extern uid_t min_uid; -extern int debug; +extern uid_t map_min_uid; +extern int map_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 get_pw_mapuser(const char *name, struct pwbuf *pb); +extern uint32_t get_sessionid(void); +extern int skip_program(void); +extern int find_mappingfile(struct pwbuf *pb, uid_t uid); +extern int find_mapped_name(struct pwbuf *pb, uid_t uid, uint32_t session); +extern int make_mapuser(struct pwbuf *pb, const char *name); +extern int map_init_common(int *errnop, const char *plugname); +extern char **fixup_gr_mem(const char *name, const char **gr_in, char *buf, + size_t * lp, int *err, unsigned privbits); +extern void cleanup_gr_mem(void); + +#define PRIV_MATCH 2 +#define UNPRIV_MATCH 1 diff --git a/nss_mapname.c b/nss_mapname.c index e974b75..46a64bc 100644 --- a/nss_mapname.c +++ b/nss_mapname.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 Cumulus Networks, Inc. + * Copyright (C) 2017, 2018 Cumulus Networks, Inc. * All rights reserved. * Author: Dave Olson <olson@cumulusnetworks.com> * @@ -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,17 +43,12 @@ #include "map_common.h" #include <stdbool.h> +#include <fcntl.h> +#include <grp.h> 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 * We only fail if we can't read the configuration file, or the username @@ -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 <sys/types.h> -#include <fcntl.h> -#include <dirent.h> -#include <ctype.h> 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 - |