--- embedaddon/sudo/compat/getgrouplist.c 2012/02/21 16:23:02 1.1.1.1 +++ embedaddon/sudo/compat/getgrouplist.c 2013/10/14 07:56:33 1.1.1.3 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Todd C. Miller + * Copyright (c) 2010, 2011, 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 @@ -16,6 +16,8 @@ #include +#ifndef HAVE_GETGROUPLIST + #include #include #ifdef STDC_HEADERS @@ -33,10 +35,19 @@ # 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" -#ifdef HAVE_GETGRSET +#if defined(HAVE_GETGRSET) /* * BSD-compatible getgrouplist(3) using getgrset(3) */ @@ -70,7 +81,7 @@ getgrouplist(const char *name, gid_t basegid, gid_t *g rval = 0; done: - efree(grset); + free(grset); #ifdef HAVE_SETAUTHDB aix_restoreauthdb(); #endif @@ -79,9 +90,216 @@ done: return rval; } -#else /* HAVE_GETGRSET */ +#elif defined(HAVE_NSS_SEARCH) +#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 *); + +static id_t +strtoid(const char *p, int *errval) +{ + char *ep; + id_t rval = 0; + + errno = 0; + if (*p == '-') { + long lval = strtol(p, &ep, 10); + if (ep == p || *ep != '\0') { + *errval = EINVAL; + goto done; + } + if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) || + (lval > INT_MAX || lval < INT_MIN)) { + *errval = ERANGE; + goto done; + } + rval = (id_t)lval; + *errval = 0; + } else { + unsigned long ulval = strtoul(p, &ep, 10); + if (ep == p || *ep != '\0') { + *errval = EINVAL; + goto done; + } + if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > UINT_MAX) { + *errval = ERANGE; + goto done; + } + rval = (id_t)ulval; + *errval = 0; + } +done: + return rval; +} + /* + * Convert a groups file string (instr) to a struct group (ent) using + * buf for storage. + */ +static int +str2grp(const char *instr, int inlen, void *ent, char *buf, int buflen) +{ + struct group *grp = ent; + char *cp, *fieldsep = buf; + char **gr_mem, **gr_end; + int errval, yp = 0; + id_t id; + + /* Must at least have space to copy instr -> buf. */ + if (inlen >= buflen) + return NSS_STR_PARSE_ERANGE; + + /* 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'; + id = strtoid(cp, &errval); + if (errval != 0) { + /* + * A range error is always a fatal error, but ignore garbage + * at the end of YP entries since it has no meaning. + */ + if (errval == ERANGE) + return NSS_STR_PARSE_ERANGE; + return yp ? NSS_STR_PARSE_SUCCESS : NSS_STR_PARSE_PARSE; + } +#ifdef GID_NOBODY + /* Negative gids get mapped to nobody on Solaris. */ + if (*cp == '-' && id != 0) + grp->gr_gid = GID_NOBODY; + else +#endif + grp->gr_gid = (gid_t)id; + + /* 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 */ + +/* * BSD-compatible getgrouplist(3) using getgrent(3) */ int @@ -128,4 +346,5 @@ done: return rval; } -#endif /* HAVE_GETGRSET */ +#endif /* !HAVE_GETGRSET && !HAVE__GETGROUPSBYMEMBER */ +#endif /* HAVE_GETGROUPLIST */