From 6cbba7d66556dc67d71c6dfae91bcf35ff9bef8c Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Mon, 1 Feb 2016 11:08:58 -0700 Subject: [PATCH] Add an administrative domain to the passwd/group cache key for AIX which can have different name <-> ID mappings depending on whether the database is local, LDAP, etc. --- configure | 2 +- configure.ac | 2 +- include/sudo_util.h | 5 +- lib/util/aix.c | 86 +++++++++++++++++++++++++++++------ lib/util/getgrouplist.c | 2 +- plugins/sudoers/pwutil.c | 85 +++++++++++++++++++++++++--------- plugins/sudoers/pwutil.h | 1 + plugins/sudoers/pwutil_impl.c | 6 +-- plugins/sudoers/set_perms.c | 6 --- src/sudo.c | 2 +- 10 files changed, 147 insertions(+), 50 deletions(-) diff --git a/configure b/configure index f0128d173..66560b7f9 100755 --- a/configure +++ b/configure @@ -14846,7 +14846,7 @@ done COMMON_OBJS="${COMMON_OBJS} aix.lo" - for _sym in aix_prep_user_v1 aix_restoreauthdb_v1 aix_setauthdb_v1; do + for _sym in aix_prep_user_v1 aix_restoreauthdb_v1 aix_setauthdb_v1 aix_setauthdb_v2 aix_getauthregistry_v1; do COMPAT_EXP="${COMPAT_EXP}${_sym} " done diff --git a/configure.ac b/configure.ac index 40d61824c..01214f9ae 100644 --- a/configure.ac +++ b/configure.ac @@ -1749,7 +1749,7 @@ case "$host" in [AC_CHECK_TYPES([authdb_t], [], [], [#include ])]) COMMON_OBJS="${COMMON_OBJS} aix.lo" - SUDO_APPEND_COMPAT_EXP(aix_prep_user_v1 aix_restoreauthdb_v1 aix_setauthdb_v1) + SUDO_APPEND_COMPAT_EXP(aix_prep_user_v1 aix_restoreauthdb_v1 aix_setauthdb_v1 aix_setauthdb_v2 aix_getauthregistry_v1) # These prototypes may be missing AC_CHECK_DECLS([usrinfo], [], [], [ diff --git a/include/sudo_util.h b/include/sudo_util.h index d20bcb1d4..78d4a2d5b 100644 --- a/include/sudo_util.h +++ b/include/sudo_util.h @@ -151,12 +151,15 @@ #endif /* aix.c */ +__dso_public int aix_getauthregistry_v1(char *user, char *saved_registry); +#define aix_getauthregistry(_a, _b) aix_getauthregistry_v1((_a), (_b)) __dso_public int aix_prep_user_v1(char *user, const char *tty); #define aix_prep_user(_a, _b) aix_prep_user_v1((_a), (_b)) __dso_public int aix_restoreauthdb_v1(void); #define aix_restoreauthdb() aix_restoreauthdb_v1() __dso_public int aix_setauthdb_v1(char *user); -#define aix_setauthdb(_a) aix_setauthdb_v1((_a)) +__dso_public int aix_setauthdb_v2(char *user, char *registry); +#define aix_setauthdb(_a, _b) aix_setauthdb_v2((_a), (_b)) /* gethostname.c */ __dso_public char *sudo_gethostname_v1(void); diff --git a/lib/util/aix.c b/lib/util/aix.c index c7c476b93..dce39def5 100644 --- a/lib/util/aix.c +++ b/lib/util/aix.c @@ -133,7 +133,7 @@ aix_setlimits(char *user) typedef char authdb_t[16]; # endif -/* The empty string means to access all defined administrative domains. */ +/* The empty string means to access all defined authentication registries. */ static authdb_t old_registry; # if defined(HAVE_DECL_SETAUTHDB) && !HAVE_DECL_SETAUTHDB @@ -144,41 +144,93 @@ int usrinfo(int cmd, char *buf, int count); # endif /* - * Look up administrative domain for user (SYSTEM in /etc/security/user) and + * Look up authentication registry for user (SYSTEM in /etc/security/user) and * set it as the default for the process. This ensures that password and * group lookups are made against the correct source (files, NIS, LDAP, etc). * Does not modify errno even on error since callers do not check rval. */ int -aix_setauthdb_v1(char *user) +aix_getauthregistry_v1(char *user, char *saved_registry) { - char *registry; int serrno = errno; int rval = -1; - debug_decl(aix_setauthdb, SUDO_DEBUG_UTIL) + debug_decl(aix_getauthregistry, SUDO_DEBUG_UTIL) + saved_registry[0] = '\0'; if (user != NULL) { + char *registry; + if (setuserdb(S_READ) != 0) { sudo_warn(U_("unable to open userdb")); goto done; } - if (getuserattr(user, S_REGISTRY, ®istry, SEC_CHAR) == 0) { - if (setauthdb(registry, old_registry) != 0) { - sudo_warn(U_("unable to switch to registry \"%s\" for %s"), - registry, user); - goto done; + rval = getuserattr(user, S_REGISTRY, ®istry, SEC_CHAR); + if (rval == 0) { + /* sizeof(authdb_t) is guaranteed to be 16 */ + if (strlcpy(saved_registry, registry, 16) >= 16) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "registry for user %s too long: %s", user, registry); } + sudo_debug_printf(SUDO_DEBUG_INFO, + "%s: saved authentication registry for user %s is %s", + __func__, user, saved_registry); } enduserdb(); + } else { + /* Get the process-wide registry. */ + rval = getauthdb(saved_registry); } - rval = 0; done: errno = serrno; debug_return_int(rval); } /* - * Restore the saved administrative domain, if any. + * Set the specified authentication registry for user (SYSTEM in + * /etc/security/user) and set it as the default for the process. + * This ensures that password and group lookups are made against + * the correct source (files, NIS, LDAP, etc). + * If registry is NULL, look it up based on the user name. + * Does not modify errno even on error since callers do not check rval. + */ +int +aix_setauthdb_v1(char *user) +{ + return aix_setauthdb_v2(user, NULL); +} + +int +aix_setauthdb_v2(char *user, char *registry) +{ + authdb_t regbuf; + int serrno = errno; + int rval = -1; + debug_decl(aix_setauthdb, SUDO_DEBUG_UTIL) + + if (user != NULL) { + /* Look up authentication registry if one is not provided. */ + if (registry == NULL) { + if (aix_getauthregistry(user, regbuf) != 0) + goto done; + registry = regbuf; + } + rval = setauthdb(registry, old_registry); + if (rval != 0) { + sudo_warn(U_("unable to switch to registry \"%s\" for %s"), + registry, user); + } else { + sudo_debug_printf(SUDO_DEBUG_INFO, + "%s: setting authentication registry to %s", + __func__, registry); + } + } +done: + errno = serrno; + debug_return_int(rval); +} + +/* + * Restore the saved authentication registry, if any. * Does not modify errno even on error since callers do not check rval. */ int @@ -191,7 +243,11 @@ aix_restoreauthdb_v1(void) if (setauthdb(old_registry, NULL) != 0) { sudo_warn(U_("unable to restore registry")); rval = -1; - } + } else { + sudo_debug_printf(SUDO_DEBUG_INFO, + "%s: setting authentication registry to %s", + __func__, old_registry); +} errno = serrno; debug_return_int(rval); } @@ -215,8 +271,8 @@ aix_prep_user_v1(char *user, const char *tty) free(info); #ifdef HAVE_SETAUTHDB - /* set administrative domain */ - if (aix_setauthdb(user) != 0) + /* set authentication registry */ + if (aix_setauthdb(user, NULL) != 0) debug_return_int(-1); #endif diff --git a/lib/util/getgrouplist.c b/lib/util/getgrouplist.c index 29e70d557..3d1069a4b 100644 --- a/lib/util/getgrouplist.c +++ b/lib/util/getgrouplist.c @@ -62,7 +62,7 @@ sudo_getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp) groups[0] = basegid; #ifdef HAVE_SETAUTHDB - aix_setauthdb((char *) name); + aix_setauthdb((char *) name, NULL); #endif if ((grset = getgrset(name)) != NULL) { char *last; diff --git a/plugins/sudoers/pwutil.c b/plugins/sudoers/pwutil.c index 03906f401..dee2b42ca 100644 --- a/plugins/sudoers/pwutil.c +++ b/plugins/sudoers/pwutil.c @@ -56,6 +56,18 @@ static int cmp_grgid(const void *, const void *); #define cmp_grnam cmp_pwnam +/* + * AIX has the concept of authentication registries (files, NIS, LDAP, etc). + * This allows you to have separate ID <-> name mappings based on which + * authentication registries the user was looked up in. + * We store the registry as part of the key and use it when matching. + */ +#ifdef HAVE_SETAUTHDB +# define getauthregistry(u, r) aix_getauthregistry((u), (r)) +#else +# define getauthregistry(u, r) ((r)[0] = '\0') +#endif + /* * Compare by uid. */ @@ -64,6 +76,8 @@ cmp_pwuid(const void *v1, const void *v2) { const struct cache_item *ci1 = (const struct cache_item *) v1; const struct cache_item *ci2 = (const struct cache_item *) v2; + if (ci1->k.uid == ci2->k.uid) + return strcmp(ci1->registry, ci2->registry); return ci1->k.uid - ci2->k.uid; } @@ -75,7 +89,10 @@ cmp_pwnam(const void *v1, const void *v2) { const struct cache_item *ci1 = (const struct cache_item *) v1; const struct cache_item *ci2 = (const struct cache_item *) v2; - return strcmp(ci1->k.name, ci2->k.name); + int rval = strcmp(ci1->k.name, ci2->k.name); + if (rval == 0) + rval = strcmp(ci1->registry, ci2->registry); + return rval; } void @@ -117,17 +134,20 @@ sudo_getpwuid(uid_t uid) debug_decl(sudo_getpwuid, SUDOERS_DEBUG_NSS) key.k.uid = uid; + getauthregistry(IDtouser(uid), key.registry); if ((node = rbfind(pwcache_byuid, &key)) != NULL) { item = node->data; - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: uid %u -> user %s (cache hit)", - __func__, (unsigned int)uid, item->d.pw->pw_name); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: uid %u [%s] -> user %s [%s] (cache hit)", __func__, + (unsigned int)uid, key.registry, item->d.pw->pw_name, + item->registry); goto done; } /* * Cache passwd db entry if it exists or a negative response if not. */ #ifdef HAVE_SETAUTHDB - aix_setauthdb(IDtouser(uid)); + aix_setauthdb(IDtouser(uid), key.registry); #endif item = sudo_make_pwitem(uid, NULL); #ifdef HAVE_SETAUTHDB @@ -143,6 +163,7 @@ sudo_getpwuid(uid_t uid) item->k.uid = uid; /* item->d.pw = NULL; */ } + strlcpy(item->registry, key.registry, sizeof(item->registry)); switch (rbinsert(pwcache_byuid, item, NULL)) { case 1: /* should not happen */ @@ -157,9 +178,10 @@ sudo_getpwuid(uid_t uid) item->refcnt = 0; break; } - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: uid %u -> user %s (cached)", - __func__, (unsigned int)uid, - item->d.pw ? item->d.pw->pw_name : "unknown"); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: uid %u [%s] -> user %s [%s] (cached)", __func__, + (unsigned int)uid, key.registry, + item->d.pw ? item->d.pw->pw_name : "unknown", item->registry); done: item->refcnt++; debug_return_ptr(item->d.pw); @@ -176,17 +198,19 @@ sudo_getpwnam(const char *name) debug_decl(sudo_getpwnam, SUDOERS_DEBUG_NSS) key.k.name = (char *) name; + getauthregistry((char *) name, key.registry); if ((node = rbfind(pwcache_byname, &key)) != NULL) { item = node->data; - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: user %s -> uid %u (cache hit)", - __func__, name, (unsigned int)item->d.pw->pw_uid); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: user %s [%s] -> uid %u [%s] (cache hit)", __func__, name, + key.registry, (unsigned int)item->d.pw->pw_uid, item->registry); goto done; } /* * Cache passwd db entry if it exists or a negative response if not. */ #ifdef HAVE_SETAUTHDB - aix_setauthdb((char *) name); + aix_setauthdb((char *) name, key.registry); #endif item = sudo_make_pwitem((uid_t)-1, name); #ifdef HAVE_SETAUTHDB @@ -203,6 +227,7 @@ sudo_getpwnam(const char *name) memcpy(item->k.name, name, len); /* item->d.pw = NULL; */ } + strlcpy(item->registry, key.registry, sizeof(item->registry)); switch (rbinsert(pwcache_byname, item, NULL)) { case 1: /* should not happen */ @@ -215,8 +240,9 @@ sudo_getpwnam(const char *name) item->refcnt = 0; break; } - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: user %s -> uid %d (cached)", - __func__, name, item->d.pw ? (int)item->d.pw->pw_uid : -1); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: user %s [%s] -> uid %d [%s] (cached)", __func__, name, + key.registry, item->d.pw ? (int)item->d.pw->pw_uid : -1, item->registry); done: item->refcnt++; debug_return_ptr(item->d.pw); @@ -289,6 +315,7 @@ sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home, item->k.name = pw->pw_name; pwcache = pwcache_byname; } + getauthregistry(NULL, item->registry); switch (rbinsert(pwcache, item, &node)) { case 1: /* Already exists. */ @@ -385,6 +412,8 @@ cmp_grgid(const void *v1, const void *v2) { const struct cache_item *ci1 = (const struct cache_item *) v1; const struct cache_item *ci2 = (const struct cache_item *) v2; + if (ci1->k.gid == ci2->k.gid) + return strcmp(ci1->registry, ci2->registry); return ci1->k.gid - ci2->k.gid; } @@ -427,10 +456,13 @@ sudo_getgrgid(gid_t gid) debug_decl(sudo_getgrgid, SUDOERS_DEBUG_NSS) key.k.gid = gid; + getauthregistry(NULL, key.registry); if ((node = rbfind(grcache_bygid, &key)) != NULL) { item = node->data; - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: gid %u -> group %s (cache hit)", - __func__, (unsigned int)gid, item->d.gr->gr_name); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: gid %u [%s] -> group %s [%s] (cache hit)", __func__, + (unsigned int)gid, key.registry, item->d.gr->gr_name, + item->registry); goto done; } /* @@ -447,6 +479,7 @@ sudo_getgrgid(gid_t gid) item->k.gid = gid; /* item->d.gr = NULL; */ } + strlcpy(item->registry, key.registry, sizeof(item->registry)); switch (rbinsert(grcache_bygid, item, NULL)) { case 1: /* should not happen */ @@ -461,9 +494,10 @@ sudo_getgrgid(gid_t gid) item->refcnt = 0; break; } - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: gid %u -> group %s (cached)", - __func__, (unsigned int)gid, - item->d.gr ? item->d.gr->gr_name : "unknown"); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: gid %u [%s] -> group %s [%s] (cached)", __func__, + (unsigned int)gid, key.registry, + item->d.gr ? item->d.gr->gr_name : "unknown", item->registry); done: item->refcnt++; debug_return_ptr(item->d.gr); @@ -480,10 +514,12 @@ sudo_getgrnam(const char *name) debug_decl(sudo_getgrnam, SUDOERS_DEBUG_NSS) key.k.name = (char *) name; + getauthregistry(NULL, key.registry); if ((node = rbfind(grcache_byname, &key)) != NULL) { item = node->data; - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: group %s -> gid %u (cache hit)", - __func__, name, (unsigned int)item->d.gr->gr_gid); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: group %s [%s] -> gid %u [%s] (cache hit)", __func__, name, + key.registry, (unsigned int)item->d.gr->gr_gid, item->registry); goto done; } /* @@ -501,6 +537,7 @@ sudo_getgrnam(const char *name) memcpy(item->k.name, name, len); /* item->d.gr = NULL; */ } + strlcpy(item->registry, key.registry, sizeof(item->registry)); switch (rbinsert(grcache_byname, item, NULL)) { case 1: /* should not happen */ @@ -513,8 +550,9 @@ sudo_getgrnam(const char *name) item->refcnt = 0; break; } - sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: group %s -> gid %d (cache hit)", - __func__, name, item->d.gr ? (int)item->d.gr->gr_gid : -1); + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s: group %s [%s] -> gid %d [%s] (cache hit)", __func__, name, + key.registry, item->d.gr ? (int)item->d.gr->gr_gid : -1, item->registry); done: item->refcnt++; debug_return_ptr(item->d.gr); @@ -569,6 +607,7 @@ sudo_fakegrnam(const char *group) gritem->cache.k.name = gr->gr_name; grcache = grcache_byname; } + getauthregistry(NULL, item->registry); switch (rbinsert(grcache, item, &node)) { case 1: /* Already exists. */ @@ -680,6 +719,7 @@ sudo_get_grlist(const struct passwd *pw) debug_decl(sudo_get_grlist, SUDOERS_DEBUG_NSS) key.k.name = pw->pw_name; + getauthregistry(pw->pw_name, key.registry); if ((node = rbfind(grlist_cache, &key)) != NULL) { item = node->data; goto done; @@ -692,6 +732,7 @@ sudo_get_grlist(const struct passwd *pw) /* Out of memory? */ debug_return_ptr(NULL); } + strlcpy(item->registry, key.registry, sizeof(item->registry)); switch (rbinsert(grlist_cache, item, NULL)) { case 1: /* should not happen */ @@ -730,11 +771,13 @@ sudo_set_grlist(struct passwd *pw, char * const *groups, char * const *gids) * Cache group db entry if it doesn't already exist */ key.k.name = pw->pw_name; + getauthregistry(NULL, key.registry); if ((node = rbfind(grlist_cache, &key)) == NULL) { if ((item = sudo_make_grlist_item(pw, groups, gids)) == NULL) { sudo_warnx(U_("unable to parse groups for %s"), pw->pw_name); debug_return_int(-1); } + strlcpy(item->registry, key.registry, sizeof(item->registry)); switch (rbinsert(grlist_cache, item, NULL)) { case 1: sudo_warnx(U_("unable to cache group list for %s, already exists"), diff --git a/plugins/sudoers/pwutil.h b/plugins/sudoers/pwutil.h index 9b5dabd57..433e15687 100644 --- a/plugins/sudoers/pwutil.h +++ b/plugins/sudoers/pwutil.h @@ -24,6 +24,7 @@ */ struct cache_item { unsigned int refcnt; + char registry[16]; /* key */ union { uid_t uid; diff --git a/plugins/sudoers/pwutil_impl.c b/plugins/sudoers/pwutil_impl.c index f081ef5ab..1a1b4392f 100644 --- a/plugins/sudoers/pwutil_impl.c +++ b/plugins/sudoers/pwutil_impl.c @@ -68,7 +68,7 @@ do { \ * Dynamically allocate space for a struct item plus the key and data * elements. If name is non-NULL it is used as the key, else the * uid is the key. Fills in datum from struct password. - * Returns NULL on malloc error or unknown name/id, setting errno + * Returns NULL on calloc error or unknown name/id, setting errno * to ENOMEM or ENOENT respectively. */ struct cache_item * @@ -151,7 +151,7 @@ sudo_make_pwitem(uid_t uid, const char *name) * Dynamically allocate space for a struct item plus the key and data * elements. If name is non-NULL it is used as the key, else the * gid is the key. Fills in datum from struct group. - * Returns NULL on malloc error or unknown name/id, setting errno + * Returns NULL on calloc error or unknown name/id, setting errno * to ENOMEM or ENOENT respectively. */ struct cache_item * @@ -338,7 +338,7 @@ again: */ #ifdef HAVE_SETAUTHDB if (grp == NULL) - aix_setauthdb((char *) pw->pw_name); + aix_setauthdb((char *) pw->pw_name, NULL); #endif ngroups = 0; for (i = 0; i < ngids; i++) { diff --git a/plugins/sudoers/set_perms.c b/plugins/sudoers/set_perms.c index af6cdc36a..e38d98de2 100644 --- a/plugins/sudoers/set_perms.c +++ b/plugins/sudoers/set_perms.c @@ -1573,13 +1573,7 @@ runas_setgroups(void) } pw = runas_pw ? runas_pw : sudo_user.pw; -#ifdef HAVE_SETAUTHDB - aix_setauthdb(pw->pw_name); -#endif grlist = sudo_get_grlist(pw); -#ifdef HAVE_SETAUTHDB - aix_restoreauthdb(); -#endif if (grlist != NULL) { if (sudo_setgroups(grlist->ngids, grlist->gids) < 0) { sudo_grlist_delref(grlist); diff --git a/src/sudo.c b/src/sudo.c index ed823c6e0..d4be82acd 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -803,7 +803,7 @@ command_info_to_details(char * const info[], struct command_details *details) details->egid = details->gid; #ifdef HAVE_SETAUTHDB - aix_setauthdb(IDtouser(details->euid)); + aix_setauthdb(IDtouser(details->euid), NULL); #endif details->pw = getpwuid(details->euid); if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL) -- 2.40.0