From: Todd C. Miller Date: Tue, 22 Jan 2013 20:41:15 +0000 (-0500) Subject: Use nss_search() to implement getgrouplist() where available. Tested X-Git-Tag: SUDO_1_8_7~1^2~274 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6bc3d4aed57147b35bd6b8ee5af9df4c09fc1344;p=sudo Use nss_search() to implement getgrouplist() where available. Tested on Solaris and HP-UX. We need to include a compatibility header for HP-UX which uses the Solaris nsswitch implementation but doesn't ship nss_dbdefs.h. --- diff --git a/MANIFEST b/MANIFEST index aeb9661d6..0253da6b5 100644 --- a/MANIFEST +++ b/MANIFEST @@ -47,6 +47,7 @@ compat/mksigname.c compat/mksigname.h compat/mktemp.c compat/nanosleep.c +compat/nss_dbdefs.h compat/pw_dup.c compat/regress/fnmatch/fnm_test.c compat/regress/fnmatch/fnm_test.in diff --git a/compat/getgrouplist.c b/compat/getgrouplist.c index cea72f511..d1327e092 100644 --- a/compat/getgrouplist.c +++ b/compat/getgrouplist.c @@ -33,6 +33,15 @@ # include #endif /* HAVE_STRINGS_H */ #include +#ifdef HAVE_NSS_SEARCH +# include +# include +# ifdef HAVE_NSS_DBDEFS_H +# include +# else +# include "compat/nss_dbdefs.h" +# endif +#endif #include "missing.h" @@ -79,31 +88,169 @@ done: return rval; } -#elif defined(HAVE__GETGROUPSBYMEMBER) +#elif defined(HAVE_NSS_SEARCH) + +#ifndef GID_MAX +# define GID_MAX UID_MAX +#endif + +#ifndef ALIGNBYTES +# define ALIGNBYTES (sizeof(long) - 1L) +#endif +#ifndef ALIGN +# define ALIGN(p) (((unsigned long)(p) + ALIGNBYTES) & ~ALIGNBYTES) +#endif + +extern void _nss_initf_group(nss_db_params_t *); /* - * BSD-compatible getgrouplist(3) using _getgroupsbymember(3) + * Convert a groups file string (instr) to a struct group (ent) using + * buf for storage. */ -int -getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp) +static int +str2grp(const char *instr, int inlen, void *ent, char *buf, int buflen) { - int ngroups, grpsize = *ngroupsp; - int rval = -1; + struct group *grp = ent; + char *cp, *ep, *fieldsep = buf; + char **gr_mem, **gr_end; + int yp = 0; + unsigned long gid; - if (grpsize > 0) { - /* We support BSD semantics where the first element is the base gid */ - groups[0] = basegid; + /* Must at least have space to copy instr -> buf. */ + if (inlen >= buflen) + return NSS_STR_PARSE_ERANGE; - /* The last arg is 1 because we already filled in the base gid. */ - ngroups = _getgroupsbymember(name, groups, grpsize, 1); - if (ngroups != -1) { - rval = 0; - *ngroupsp = ngroups; + /* Paranoia: buf and instr should be distinct. */ + if (buf != instr) { + memmove(buf, instr, inlen); + buf[inlen] = '\0'; + } + + if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL) + return NSS_STR_PARSE_PARSE; + *fieldsep++ = '\0'; + grp->gr_name = cp; + + /* Check for YP inclusion/exclusion entries. */ + if (*cp == '+' || *cp == '-') { + /* Only the name is required for YP inclusion/exclusion entries. */ + grp->gr_passwd = ""; + grp->gr_gid = 0; + grp->gr_mem = NULL; + yp = 1; + } + + if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL) + return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE; + *fieldsep++ = '\0'; + grp->gr_passwd = cp; + + if ((fieldsep = strchr(cp = fieldsep, ':')) == NULL) + return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE; + *fieldsep++ = '\0'; + gid = strtoul(cp, &ep, 10); + if (*cp == '\0' || *ep != '\0') + return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE; + if (gid > GID_MAX || (gid == ULONG_MAX && errno == ERANGE)) + return NSS_STR_PARSE_ERANGE; + grp->gr_gid = (gid_t)gid; + + /* Store group members, taking care to use proper alignment. */ + grp->gr_mem = NULL; + if (*fieldsep != '\0') { + grp->gr_mem = gr_mem = (char **)ALIGN(buf + inlen + 1); + gr_end = (char **)((unsigned long)(buf + buflen) & ~ALIGNBYTES); + for (;;) { + if (gr_mem == gr_end) + return NSS_STR_PARSE_ERANGE; /* out of space! */ + *gr_mem++ = cp; + if (fieldsep == NULL) + break; + if ((fieldsep = strchr(cp = fieldsep, ',')) != NULL) + *fieldsep++ = '\0'; } + *gr_mem = NULL; } + return NSS_STR_PARSE_SUCCESS; +} + +static nss_status_t +process_cstr(const char *instr, int inlen, struct nss_groupsbymem *gbm) +{ + const char *user = gbm->username; + nss_status_t rval = NSS_NOTFOUND; + nss_XbyY_buf_t *buf; + struct group *grp; + char **gr_mem; + int error, i; + + buf = _nss_XbyY_buf_alloc(sizeof(struct group), NSS_BUFLEN_GROUP); + if (buf == NULL) + return NSS_UNAVAIL; + + /* Parse groups file string -> struct group. */ + grp = buf->result; + error = (*gbm->str2ent)(instr, inlen, grp, buf->buffer, buf->buflen); + if (error || grp->gr_mem == NULL) + goto done; + + for (gr_mem = grp->gr_mem; *gr_mem != NULL; gr_mem++) { + if (strcmp(*gr_mem, user) == 0) { + /* Append to gid_array unless gr_gid is a dupe. */ + for (i = 0; i < gbm->numgids; i++) { + if (gbm->gid_array[i] == grp->gr_gid) + goto done; /* already present */ + } + /* Store gid if there is space. */ + if (i < gbm->maxgids) + gbm->gid_array[i] = grp->gr_gid; + /* Always increment numgids so we can detect when out of space. */ + gbm->numgids++; + goto done; + } + } +done: + _nss_XbyY_buf_free(buf); return rval; } +/* + * BSD-compatible getgrouplist(3) using nss_search(3) + */ +int +getgrouplist(const char *name, gid_t basegid, gid_t *groups, int *ngroupsp) +{ + struct nss_groupsbymem gbm; + static DEFINE_NSS_DB_ROOT(db_root); + + /* We support BSD semantics where the first element is the base gid */ + if (*ngroupsp <= 0) + return -1; + groups[0] = basegid; + + memset(&gbm, 0, sizeof(gbm)); + gbm.username = name; + gbm.gid_array = groups; + gbm.maxgids = *ngroupsp; + gbm.numgids = 1; /* for basegid */ + gbm.force_slow_way = 1; + gbm.str2ent = str2grp; + gbm.process_cstr = process_cstr; + + /* + * Can't use nss_search return value since it may return NSS_UNAVAIL + * when no nsswitch.conf entry (e.g. compat mode). + */ + (void)nss_search(&db_root, _nss_initf_group, NSS_DBOP_GROUP_BYMEMBER, &gbm); + + if (gbm.numgids <= gbm.maxgids) { + *ngroupsp = gbm.numgids; + return 0; + } + *ngroupsp = gbm.maxgids; + return -1; +} + #else /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */ /* diff --git a/compat/nss_dbdefs.h b/compat/nss_dbdefs.h new file mode 100644 index 000000000..7ac91133f --- /dev/null +++ b/compat/nss_dbdefs.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _COMPAT_NSS_DBDEFS_H +#define _COMPAT_NSS_DBDEFS_H + +/* + * Bits of nss_dbdefs.h and nss_common.h needed to implement + * getgrouplist(3) using nss_search(3). + * + * HP-UX does not ship those headers so we need this compatibility header. + * It may also work on other systems that use a Solaris-derived nsswitch + * API. + */ + +#ifdef NEED_HPUX_MUTEX +# include +#endif + +typedef enum { + NSS_SUCCESS, + NSS_NOTFOUND, + NSS_UNAVAIL +} nss_status_t; + +typedef struct nss_db_params { + const char *name; + const char *config_name; + const char *default_config; + unsigned int max_active_per_src; + unsigned int max_dormant_per_src; + int flags; + void *finders; + void *private; + void (*cleanup)(struct nss_db_params *); +} nss_db_params_t; + +struct nss_groupsbymem { + const char *username; + gid_t *gid_array; + int maxgids; + int force_slow_way; + int (*str2ent)(const char *, int, void *, char *, int); + nss_status_t (*process_cstr)(const char *, int, struct nss_groupsbymem *); + int numgids; +}; + +typedef struct { + void *result; /* group struct to fill in. */ + char *buffer; /* string buffer for above */ + size_t buflen; /* string buffer size */ +} nss_XbyY_buf_t; + +typedef struct { + void *state; /* really struct nss_db_state * */ +#ifdef NEED_HPUX_MUTEX + lwp_mutex_t lock; +#endif +} nss_db_root_t; + +#ifdef NEED_HPUX_MUTEX +# define NSS_DB_ROOT_INIT { 0, LWP_MUTEX_INITIALIZER } +#else +# define NSS_DB_ROOT_INIT { 0 } +#endif +# define DEFINE_NSS_DB_ROOT(name) nss_db_root_t name = NSS_DB_ROOT_INIT + +/* Backend function to find all groups a user belongs to for initgroups(). */ +#define NSS_DBOP_GROUP_BYMEMBER 6 + +/* str2ent function return values */ +#define NSS_STR_PARSE_SUCCESS 0 +#define NSS_STR_PARSE_PARSE 1 +#define NSS_STR_PARSE_ERANGE 2 + +/* Max length for an /etc/group file line. */ +#define NSS_BUFLEN_GROUP 8192 + +/* HP-UX uses an extra underscore for these functions. */ +#ifdef HAVE___NSS_INITF_GROUP +# define _nss_initf_group __nss_initf_group +#endif +#ifdef HAVE__NSS_XBYY_BUF_ALLOC +# define _nss_XbyY_buf_alloc __nss_XbyY_buf_alloc +# define _nss_XbyY_buf_free __nss_XbyY_buf_free +#endif + +typedef void (*nss_db_initf_t)(nss_db_params_t *); +extern nss_status_t nss_search(nss_db_root_t *, nss_db_initf_t, int, void *); +extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int, int); +extern void _nss_XbyY_buf_free(nss_XbyY_buf_t *); + +#endif /* _COMPAT_NSS_DBDEFS_H */ diff --git a/config.h.in b/config.h.in index 3483ac225..45e8088ce 100644 --- a/config.h.in +++ b/config.h.in @@ -391,6 +391,12 @@ /* Define to 1 if you have the `nl_langinfo' function. */ #undef HAVE_NL_LANGINFO +/* Define to 1 if you have the header file. */ +#undef HAVE_NSS_DBDEFS_H + +/* Define to 1 if you have the `nss_search' function. */ +#undef HAVE_NSS_SEARCH + /* Define to 1 if you have the `openpty' function. */ #undef HAVE_OPENPTY @@ -697,21 +703,30 @@ /* Define to 1 if the system has the type `_Bool'. */ #undef HAVE__BOOL -/* Define to 1 if you have the `_getgroupsbymember' function. */ -#undef HAVE__GETGROUPSBYMEMBER - /* Define to 1 if you have the `_getpty' function. */ #undef HAVE__GETPTY /* Define to 1 if you have the `_innetgr' function. */ #undef HAVE__INNETGR +/* Define to 1 if you have the `_nss_initf_group' function. */ +#undef HAVE__NSS_INITF_GROUP + +/* Define to 1 if you have the `_nss_XbyY_buf_alloc' function. */ +#undef HAVE__NSS_XBYY_BUF_ALLOC + /* Define to 1 if you have the `_ttyname_dev' function. */ #undef HAVE__TTYNAME_DEV /* Define to 1 if the compiler supports the C99 __func__ variable. */ #undef HAVE___FUNC__ +/* Define to 1 if you have the `__nss_initf_group' function. */ +#undef HAVE___NSS_INITF_GROUP + +/* Define to 1 if you have the `__nss_XbyY_buf_alloc' function. */ +#undef HAVE___NSS_XBYY_BUF_ALLOC + /* Define to 1 if your crt0.o defines the __progname symbol for you. */ #undef HAVE___PROGNAME diff --git a/configure b/configure index 5018c023e..e69ae7cbc 100755 --- a/configure +++ b/configure @@ -13927,19 +13927,6 @@ case "$host" in OS_INIT=os_init_solaris SUDO_OBJS="${SUDO_OBJS} solaris.o" - # For implementing getgrouplist() - for ac_func in _getgroupsbymember -do : - ac_fn_c_check_func "$LINENO" "_getgroupsbymember" "ac_cv_func__getgroupsbymember" -if test "x$ac_cv_func__getgroupsbymember" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE__GETGROUPSBYMEMBER 1 -_ACEOF - -fi -done - - # To get the crypt(3) prototype (so we pass -Wall) OSDEFS="${OSDEFS} -D__EXTENSIONS__" # AFS support needs -lucb @@ -14040,19 +14027,6 @@ fi with_netsvc="/etc/netsvc.conf" fi - # For implementing getgrouplist() - for ac_func in getgrset -do : - ac_fn_c_check_func "$LINENO" "getgrset" "ac_cv_func_getgrset" -if test "x$ac_cv_func_getgrset" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETGRSET 1 -_ACEOF - -fi -done - - # LDR_PRELOAD is only supported in AIX 5.3 and later if test $OSMAJOR -lt 5; then with_noexec=no @@ -16665,19 +16639,124 @@ _ACEOF fi done -ac_fn_c_check_func "$LINENO" "getgrouplist" "ac_cv_func_getgrouplist" +for ac_func in getgrouplist +do : + ac_fn_c_check_func "$LINENO" "getgrouplist" "ac_cv_func_getgrouplist" if test "x$ac_cv_func_getgrouplist" = xyes; then : - $as_echo "#define HAVE_GETGROUPLIST 1" >>confdefs.h + cat >>confdefs.h <<_ACEOF +#define HAVE_GETGROUPLIST 1 +_ACEOF else - case " $LIBOBJS " in + + case "$host" in + *-*-aix*) + for ac_func in getgrset +do : + ac_fn_c_check_func "$LINENO" "getgrset" "ac_cv_func_getgrset" +if test "x$ac_cv_func_getgrset" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GETGRSET 1 +_ACEOF + case " $LIBOBJS " in *" getgrouplist.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS getgrouplist.$ac_objext" ;; esac +fi +done + + ;; + *) + ac_fn_c_check_func "$LINENO" "nss_search" "ac_cv_func_nss_search" +if test "x$ac_cv_func_nss_search" = xyes; then : + + ac_fn_c_check_func "$LINENO" "_nss_XbyY_buf_alloc" "ac_cv_func__nss_XbyY_buf_alloc" +if test "x$ac_cv_func__nss_XbyY_buf_alloc" = xyes; then : + + # Solaris + ac_fn_c_check_func "$LINENO" "_nss_initf_group" "ac_cv_func__nss_initf_group" +if test "x$ac_cv_func__nss_initf_group" = xyes; then : + + for ac_header in nss_dbdefs.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "nss_dbdefs.h" "ac_cv_header_nss_dbdefs_h" "$ac_includes_default" +if test "x$ac_cv_header_nss_dbdefs_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NSS_DBDEFS_H 1 +_ACEOF + fi +done + + $as_echo "#define HAVE_NSS_SEARCH 1" >>confdefs.h + + $as_echo "#define HAVE__NSS_XBYY_BUF_ALLOC 1" >>confdefs.h + + $as_echo "#define HAVE__NSS_INITF_GROUP 1" >>confdefs.h + + case " $LIBOBJS " in + *" getgrouplist.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getgrouplist.$ac_objext" + ;; +esac + + +fi + + +else + + # HP-UX + ac_fn_c_check_func "$LINENO" "__nss_XbyY_buf_alloc" "ac_cv_func___nss_XbyY_buf_alloc" +if test "x$ac_cv_func___nss_XbyY_buf_alloc" = xyes; then : + + ac_fn_c_check_func "$LINENO" "__nss_initf_group" "ac_cv_func___nss_initf_group" +if test "x$ac_cv_func___nss_initf_group" = xyes; then : + + for ac_header in nss_dbdefs.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "nss_dbdefs.h" "ac_cv_header_nss_dbdefs_h" "$ac_includes_default" +if test "x$ac_cv_header_nss_dbdefs_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_NSS_DBDEFS_H 1 +_ACEOF + +fi + +done + + $as_echo "#define HAVE_NSS_SEARCH 1" >>confdefs.h + + $as_echo "#define HAVE___NSS_XBYY_BUF_ALLOC 1" >>confdefs.h + + $as_echo "#define HAVE___NSS_INITF_GROUP 1" >>confdefs.h + + case " $LIBOBJS " in + *" getgrouplist.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS getgrouplist.$ac_objext" + ;; +esac + + +fi + + +fi + + +fi + + +fi + + ;; + esac + +fi +done for ac_func in getline do : @@ -17117,13 +17196,12 @@ if test "x$ac_cv_func_setreuid" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SETREUID 1 _ACEOF - SKIP_SETEUID=yes + fi done fi -if test -z "$SKIP_SETEUID"; then - for ac_func in seteuid +for ac_func in seteuid do : ac_fn_c_check_func "$LINENO" "seteuid" "ac_cv_func_seteuid" if test "x$ac_cv_func_seteuid" = xyes; then : @@ -17134,7 +17212,6 @@ _ACEOF fi done -fi if test X"$with_interfaces" != X"no"; then for ac_func in getifaddrs do : @@ -23448,6 +23525,11 @@ fi + + + + + diff --git a/configure.in b/configure.in index c9b589d70..246d65406 100644 --- a/configure.in +++ b/configure.in @@ -1573,9 +1573,6 @@ case "$host" in OS_INIT=os_init_solaris SUDO_OBJS="${SUDO_OBJS} solaris.o" - # For implementing getgrouplist() - AC_CHECK_FUNCS(_getgroupsbymember) - # To get the crypt(3) prototype (so we pass -Wall) OSDEFS="${OSDEFS} -D__EXTENSIONS__" # AFS support needs -lucb @@ -1632,9 +1629,6 @@ case "$host" in with_netsvc="/etc/netsvc.conf" fi - # For implementing getgrouplist() - AC_CHECK_FUNCS(getgrset) - # LDR_PRELOAD is only supported in AIX 5.3 and later if test $OSMAJOR -lt 5; then with_noexec=no @@ -2228,7 +2222,39 @@ AC_FUNC_GETGROUPS AC_CHECK_FUNCS(glob strrchr sysconf tzset strftime setenv \ regcomp nl_langinfo mbr_check_membership \ setrlimit64) -AC_REPLACE_FUNCS(getgrouplist) +dnl AC_REPLACE_FUNCS(getgrouplist) +AC_CHECK_FUNCS(getgrouplist, [], [ + case "$host" in + *-*-aix*) + AC_CHECK_FUNCS(getgrset, [AC_LIBOBJ(getgrouplist)]) + ;; + *) + AC_CHECK_FUNC(nss_search, [ + AC_CHECK_FUNC(_nss_XbyY_buf_alloc, [ + # Solaris + AC_CHECK_FUNC(_nss_initf_group, [ + AC_CHECK_HEADERS(nss_dbdefs.h) + AC_DEFINE([HAVE_NSS_SEARCH]) + AC_DEFINE([HAVE__NSS_XBYY_BUF_ALLOC]) + AC_DEFINE([HAVE__NSS_INITF_GROUP]) + AC_LIBOBJ(getgrouplist) + ]) + ], [ + # HP-UX + AC_CHECK_FUNC(__nss_XbyY_buf_alloc, [ + AC_CHECK_FUNC(__nss_initf_group, [ + AC_CHECK_HEADERS(nss_dbdefs.h) + AC_DEFINE([HAVE_NSS_SEARCH]) + AC_DEFINE([HAVE___NSS_XBYY_BUF_ALLOC]) + AC_DEFINE([HAVE___NSS_INITF_GROUP]) + AC_LIBOBJ(getgrouplist) + ]) + ]) + ]) + ]) + ;; + esac +]) AC_CHECK_FUNCS(getline, [], [ AC_LIBOBJ(getline) AC_CHECK_FUNCS(fgetln) @@ -2304,11 +2330,9 @@ if test -z "$SKIP_SETRESUID"; then ]) fi if test -z "$SKIP_SETREUID"; then - AC_CHECK_FUNCS(setreuid, [SKIP_SETEUID=yes]) -fi -if test -z "$SKIP_SETEUID"; then - AC_CHECK_FUNCS(seteuid) + AC_CHECK_FUNCS(setreuid) fi +AC_CHECK_FUNCS(seteuid) if test X"$with_interfaces" != X"no"; then AC_CHECK_FUNCS(getifaddrs, [AC_CHECK_FUNCS(freeifaddrs)]) fi @@ -3697,6 +3721,11 @@ AH_TEMPLATE(RTLD_PRELOAD_DELIM, [The delimiter to use when defining multiple pre AH_TEMPLATE(RTLD_PRELOAD_DEFAULT, [The default value of preloaded objects (if any).]) AH_TEMPLATE(HAVE_DSO_VISIBILITY, [Define to 1 if the compiler supports the __visibility__ attribute.]) AH_TEMPLATE(HAVE_SYS_SIGABBREV, [Define to 1 if your libc has the `sys_sigabbrev' symbol.]) +AH_TEMPLATE(HAVE_NSS_SEARCH, [Define to 1 if you have the `nss_search' function.]) +AH_TEMPLATE(HAVE__NSS_INITF_GROUP, [Define to 1 if you have the `_nss_initf_group' function.]) +AH_TEMPLATE(HAVE___NSS_INITF_GROUP, [Define to 1 if you have the `__nss_initf_group' function.]) +AH_TEMPLATE(HAVE__NSS_XBYY_BUF_ALLOC, [Define to 1 if you have the `_nss_XbyY_buf_alloc' function.]) +AH_TEMPLATE(HAVE___NSS_XBYY_BUF_ALLOC, [Define to 1 if you have the `__nss_XbyY_buf_alloc' function.]) dnl dnl Bits to copy verbatim into config.h.in