version 1.1, 2012/02/21 16:23:02
|
version 1.1.1.5, 2014/06/15 16:12:54
|
Line 1
|
Line 1
|
/* |
/* |
* Copyright (c) 1996, 1998-2005, 2007-2011 | * Copyright (c) 1996, 1998-2005, 2007-2013 |
* Todd C. Miller <Todd.Miller@courtesan.com> |
* Todd C. Miller <Todd.Miller@courtesan.com> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
Line 22
|
Line 22
|
#include <config.h> |
#include <config.h> |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/stat.h> |
|
#include <sys/param.h> |
|
#include <stdio.h> |
#include <stdio.h> |
#ifdef STDC_HEADERS |
#ifdef STDC_HEADERS |
# include <stdlib.h> |
# include <stdlib.h> |
Line 48
|
Line 46
|
#ifdef HAVE_SETAUTHDB |
#ifdef HAVE_SETAUTHDB |
# include <usersec.h> |
# include <usersec.h> |
#endif /* HAVE_SETAUTHDB */ |
#endif /* HAVE_SETAUTHDB */ |
#ifdef HAVE_UTMPX_H |
|
# include <utmpx.h> |
|
#else |
|
# include <utmp.h> |
|
#endif /* HAVE_UTMPX_H */ |
|
#include <limits.h> |
|
#include <pwd.h> |
#include <pwd.h> |
#include <grp.h> |
#include <grp.h> |
|
|
#include "sudoers.h" |
#include "sudoers.h" |
#include "redblack.h" |
#include "redblack.h" |
|
#include "pwutil.h" |
|
|
/* |
/* |
* The passwd and group caches. |
* The passwd and group caches. |
Line 73 static int cmp_grgid(const void *, const void *);
|
Line 66 static int cmp_grgid(const void *, const void *);
|
|
|
#define cmp_grnam cmp_pwnam |
#define cmp_grnam cmp_pwnam |
|
|
#define ptr_to_item(p) ((struct cache_item *)((char *)(p) - sizeof(struct cache_item))) |
|
|
|
struct cache_item { |
|
unsigned int refcnt; |
|
/* key */ |
|
union { |
|
uid_t uid; |
|
gid_t gid; |
|
char *name; |
|
} k; |
|
/* datum */ |
|
union { |
|
struct passwd *pw; |
|
struct group *gr; |
|
struct group_list *grlist; |
|
} d; |
|
}; |
|
|
|
/* |
/* |
* Compare by uid. |
* Compare by uid. |
*/ |
*/ |
Line 113 cmp_pwnam(const void *v1, const void *v2)
|
Line 88 cmp_pwnam(const void *v1, const void *v2)
|
return strcmp(ci1->k.name, ci2->k.name); |
return strcmp(ci1->k.name, ci2->k.name); |
} |
} |
|
|
#define FIELD_SIZE(src, name, size) \ |
|
do { \ |
|
if (src->name) { \ |
|
size = strlen(src->name) + 1; \ |
|
total += size; \ |
|
} \ |
|
} while (0) |
|
|
|
#define FIELD_COPY(src, dst, name, size) \ |
|
do { \ |
|
if (src->name) { \ |
|
memcpy(cp, src->name, size); \ |
|
dst->name = cp; \ |
|
cp += size; \ |
|
} \ |
|
} while (0) |
|
|
|
/* |
|
* 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. |
|
* |
|
* We would like to fill in the encrypted password too but the |
|
* call to the shadow function could overwrite the pw buffer (NIS). |
|
*/ |
|
static struct cache_item * |
|
make_pwitem(const struct passwd *pw, const char *name) |
|
{ |
|
char *cp; |
|
const char *pw_shell; |
|
size_t nsize, psize, csize, gsize, dsize, ssize, total; |
|
struct cache_item *item; |
|
struct passwd *newpw; |
|
|
|
/* If shell field is empty, expand to _PATH_BSHELL. */ |
|
pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0') |
|
? _PATH_BSHELL : pw->pw_shell; |
|
|
|
/* Allocate in one big chunk for easy freeing. */ |
|
nsize = psize = csize = gsize = dsize = ssize = 0; |
|
total = sizeof(struct cache_item) + sizeof(struct passwd); |
|
FIELD_SIZE(pw, pw_name, nsize); |
|
FIELD_SIZE(pw, pw_passwd, psize); |
|
#ifdef HAVE_LOGIN_CAP_H |
|
FIELD_SIZE(pw, pw_class, csize); |
|
#endif |
|
FIELD_SIZE(pw, pw_gecos, gsize); |
|
FIELD_SIZE(pw, pw_dir, dsize); |
|
/* Treat shell specially since we expand "" -> _PATH_BSHELL */ |
|
ssize = strlen(pw_shell) + 1; |
|
total += ssize; |
|
if (name != NULL) |
|
total += strlen(name) + 1; |
|
|
|
/* Allocate space for struct item, struct passwd and the strings. */ |
|
item = emalloc(total); |
|
cp = (char *) item + sizeof(struct cache_item); |
|
|
|
/* |
|
* Copy in passwd contents and make strings relative to space |
|
* at the end of the buffer. |
|
*/ |
|
newpw = (struct passwd *) cp; |
|
memcpy(newpw, pw, sizeof(struct passwd)); |
|
cp += sizeof(struct passwd); |
|
FIELD_COPY(pw, newpw, pw_name, nsize); |
|
FIELD_COPY(pw, newpw, pw_passwd, psize); |
|
#ifdef HAVE_LOGIN_CAP_H |
|
FIELD_COPY(pw, newpw, pw_class, csize); |
|
#endif |
|
FIELD_COPY(pw, newpw, pw_gecos, gsize); |
|
FIELD_COPY(pw, newpw, pw_dir, dsize); |
|
/* Treat shell specially since we expand "" -> _PATH_BSHELL */ |
|
memcpy(cp, pw_shell, ssize); |
|
newpw->pw_shell = cp; |
|
cp += ssize; |
|
|
|
/* Set key and datum. */ |
|
if (name != NULL) { |
|
memcpy(cp, name, strlen(name) + 1); |
|
item->k.name = cp; |
|
} else { |
|
item->k.uid = pw->pw_uid; |
|
} |
|
item->d.pw = newpw; |
|
item->refcnt = 1; |
|
|
|
return item; |
|
} |
|
|
|
void |
void |
pw_addref(struct passwd *pw) | sudo_pw_addref(struct passwd *pw) |
{ |
{ |
|
debug_decl(sudo_pw_addref, SUDO_DEBUG_NSS) |
ptr_to_item(pw)->refcnt++; |
ptr_to_item(pw)->refcnt++; |
|
debug_return; |
} |
} |
|
|
static void |
static void |
pw_delref_item(void *v) | sudo_pw_delref_item(void *v) |
{ |
{ |
struct cache_item *item = v; |
struct cache_item *item = v; |
|
debug_decl(sudo_pw_delref_item, SUDO_DEBUG_NSS) |
|
|
if (--item->refcnt == 0) |
if (--item->refcnt == 0) |
efree(item); |
efree(item); |
|
|
|
debug_return; |
} |
} |
|
|
void |
void |
pw_delref(struct passwd *pw) | sudo_pw_delref(struct passwd *pw) |
{ |
{ |
pw_delref_item(ptr_to_item(pw)); | debug_decl(sudo_pw_delref, SUDO_DEBUG_NSS) |
| sudo_pw_delref_item(ptr_to_item(pw)); |
| debug_return; |
} |
} |
|
|
/* |
/* |
* Get a password entry by uid and allocate space for it. |
* Get a password entry by uid and allocate space for it. |
* Fills in pw_passwd from shadow file if necessary. |
|
*/ |
*/ |
struct passwd * |
struct passwd * |
sudo_getpwuid(uid_t uid) |
sudo_getpwuid(uid_t uid) |
{ |
{ |
struct cache_item key, *item; |
struct cache_item key, *item; |
struct rbnode *node; |
struct rbnode *node; |
|
debug_decl(sudo_getpwuid, SUDO_DEBUG_NSS) |
|
|
key.k.uid = uid; |
key.k.uid = uid; |
if ((node = rbfind(pwcache_byuid, &key)) != NULL) { |
if ((node = rbfind(pwcache_byuid, &key)) != NULL) { |
Line 245 sudo_getpwuid(uid_t uid)
|
Line 137 sudo_getpwuid(uid_t uid)
|
#ifdef HAVE_SETAUTHDB |
#ifdef HAVE_SETAUTHDB |
aix_setauthdb(IDtouser(uid)); |
aix_setauthdb(IDtouser(uid)); |
#endif |
#endif |
if ((key.d.pw = getpwuid(uid)) != NULL) { | item = sudo_make_pwitem(uid, NULL); |
item = make_pwitem(key.d.pw, NULL); | if (item == NULL) { |
if (rbinsert(pwcache_byuid, item) != NULL) | item = ecalloc(1, sizeof(*item)); |
errorx(1, _("unable to cache uid %u (%s), already exists"), | |
(unsigned int) uid, item->d.pw->pw_name); | |
} else { | |
item = emalloc(sizeof(*item)); | |
item->refcnt = 1; |
item->refcnt = 1; |
item->k.uid = uid; |
item->k.uid = uid; |
item->d.pw = NULL; | /* item->d.pw = NULL; */ |
if (rbinsert(pwcache_byuid, item) != NULL) | |
errorx(1, _("unable to cache uid %u, already exists"), | |
(unsigned int) uid); | |
} |
} |
|
if (rbinsert(pwcache_byuid, item) != NULL) |
|
fatalx(U_("unable to cache uid %u, already exists"), |
|
(unsigned int) uid); |
#ifdef HAVE_SETAUTHDB |
#ifdef HAVE_SETAUTHDB |
aix_restoreauthdb(); |
aix_restoreauthdb(); |
#endif |
#endif |
done: |
done: |
item->refcnt++; |
item->refcnt++; |
return item->d.pw; | debug_return_ptr(item->d.pw); |
} |
} |
|
|
/* |
/* |
* Get a password entry by name and allocate space for it. |
* Get a password entry by name and allocate space for it. |
* Fills in pw_passwd from shadow file if necessary. |
|
*/ |
*/ |
struct passwd * |
struct passwd * |
sudo_getpwnam(const char *name) |
sudo_getpwnam(const char *name) |
Line 277 sudo_getpwnam(const char *name)
|
Line 164 sudo_getpwnam(const char *name)
|
struct cache_item key, *item; |
struct cache_item key, *item; |
struct rbnode *node; |
struct rbnode *node; |
size_t len; |
size_t len; |
|
debug_decl(sudo_getpwnam, SUDO_DEBUG_NSS) |
|
|
key.k.name = (char *) name; |
key.k.name = (char *) name; |
if ((node = rbfind(pwcache_byname, &key)) != NULL) { |
if ((node = rbfind(pwcache_byname, &key)) != NULL) { |
Line 289 sudo_getpwnam(const char *name)
|
Line 177 sudo_getpwnam(const char *name)
|
#ifdef HAVE_SETAUTHDB |
#ifdef HAVE_SETAUTHDB |
aix_setauthdb((char *) name); |
aix_setauthdb((char *) name); |
#endif |
#endif |
if ((key.d.pw = getpwnam(name)) != NULL) { | item = sudo_make_pwitem((uid_t)-1, name); |
item = make_pwitem(key.d.pw, name); | if (item == NULL) { |
if (rbinsert(pwcache_byname, item) != NULL) | |
errorx(1, _("unable to cache user %s, already exists"), name); | |
} else { | |
len = strlen(name) + 1; |
len = strlen(name) + 1; |
item = emalloc(sizeof(*item) + len); | item = ecalloc(1, sizeof(*item) + len); |
item->refcnt = 1; |
item->refcnt = 1; |
item->k.name = (char *) item + sizeof(*item); |
item->k.name = (char *) item + sizeof(*item); |
memcpy(item->k.name, name, len); |
memcpy(item->k.name, name, len); |
item->d.pw = NULL; | /* item->d.pw = NULL; */ |
if (rbinsert(pwcache_byname, item) != NULL) | |
errorx(1, _("unable to cache user %s, already exists"), name); | |
} |
} |
|
if (rbinsert(pwcache_byname, item) != NULL) |
|
fatalx(U_("unable to cache user %s, already exists"), name); |
#ifdef HAVE_SETAUTHDB |
#ifdef HAVE_SETAUTHDB |
aix_restoreauthdb(); |
aix_restoreauthdb(); |
#endif |
#endif |
done: |
done: |
item->refcnt++; |
item->refcnt++; |
return item->d.pw; | debug_return_ptr(item->d.pw); |
} |
} |
|
|
/* |
/* |
* Take a user, uid and gid and return a faked up passwd struct. | * Take a user, uid, gid, home and shell and return a faked up passwd struct. |
| * If home or shell are NULL default values will be used. |
*/ |
*/ |
struct passwd * |
struct passwd * |
sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid) | sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home, |
| const char *shell) |
{ |
{ |
struct cache_item *item; | struct cache_item_pw *pwitem; |
struct passwd *pw; |
struct passwd *pw; |
struct rbnode *node; |
struct rbnode *node; |
size_t len, namelen; | size_t len, name_len, home_len, shell_len; |
int i; |
int i; |
|
debug_decl(sudo_mkpwent, SUDO_DEBUG_NSS) |
|
|
namelen = strlen(user); | /* Optional arguments. */ |
len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ + | if (home == NULL) |
| home = "/"; |
| if (shell == NULL) |
| shell = _PATH_BSHELL; |
| |
| name_len = strlen(user); |
| home_len = strlen(home); |
| shell_len = strlen(shell); |
| len = sizeof(*pwitem) + name_len + 1 /* pw_name */ + |
sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ + |
sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ + |
sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL); | home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */; |
|
|
for (i = 0; i < 2; i++) { |
for (i = 0; i < 2; i++) { |
item = emalloc(len); | pwitem = ecalloc(1, len); |
zero_bytes(item, sizeof(*item) + sizeof(*pw)); | pw = &pwitem->pw; |
pw = (struct passwd *) ((char *)item + sizeof(*item)); | |
pw->pw_uid = uid; |
pw->pw_uid = uid; |
pw->pw_gid = gid; |
pw->pw_gid = gid; |
pw->pw_name = (char *)pw + sizeof(struct passwd); | pw->pw_name = (char *)(pwitem + 1); |
memcpy(pw->pw_name, user, namelen + 1); | memcpy(pw->pw_name, user, name_len + 1); |
pw->pw_passwd = pw->pw_name + namelen + 1; | pw->pw_passwd = pw->pw_name + name_len + 1; |
memcpy(pw->pw_passwd, "*", 2); |
memcpy(pw->pw_passwd, "*", 2); |
pw->pw_gecos = pw->pw_passwd + 2; |
pw->pw_gecos = pw->pw_passwd + 2; |
pw->pw_gecos[0] = '\0'; |
pw->pw_gecos[0] = '\0'; |
pw->pw_dir = pw->pw_gecos + 1; |
pw->pw_dir = pw->pw_gecos + 1; |
memcpy(pw->pw_dir, "/", 2); | memcpy(pw->pw_dir, home, home_len + 1); |
pw->pw_shell = pw->pw_dir + 2; | pw->pw_shell = pw->pw_dir + home_len + 1; |
memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL)); | memcpy(pw->pw_shell, shell, shell_len + 1); |
|
|
item->refcnt = 1; | pwitem->cache.refcnt = 1; |
item->d.pw = pw; | pwitem->cache.d.pw = pw; |
if (i == 0) { |
if (i == 0) { |
/* Store by uid, overwriting cached version. */ | /* Store by uid if it doesn't already exist. */ |
item->k.uid = pw->pw_uid; | pwitem->cache.k.uid = pw->pw_uid; |
if ((node = rbinsert(pwcache_byuid, item)) != NULL) { | if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) { |
pw_delref_item(node->data); | /* Already exists, free the item we created. */ |
node->data = item; | efree(pwitem); |
| pwitem = (struct cache_item_pw *) node->data; |
} |
} |
} else { |
} else { |
/* Store by name, overwriting cached version. */ | /* Store by name if it doesn't already exist. */ |
item->k.name = pw->pw_name; | pwitem->cache.k.name = pw->pw_name; |
if ((node = rbinsert(pwcache_byname, item)) != NULL) { | if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) { |
pw_delref_item(node->data); | /* Already exists, free the item we created. */ |
node->data = item; | efree(pwitem); |
| pwitem = (struct cache_item_pw *) node->data; |
} |
} |
} |
} |
} |
} |
item->refcnt++; | pwitem->cache.refcnt++; |
return pw; | debug_return_ptr(&pwitem->pw); |
} |
} |
|
|
/* |
/* |
Line 373 sudo_fakepwnamid(const char *user, uid_t uid, gid_t gi
|
Line 270 sudo_fakepwnamid(const char *user, uid_t uid, gid_t gi
|
struct passwd * |
struct passwd * |
sudo_fakepwnam(const char *user, gid_t gid) |
sudo_fakepwnam(const char *user, gid_t gid) |
{ |
{ |
|
const char *errstr; |
uid_t uid; |
uid_t uid; |
|
debug_decl(sudo_fakepwnam, SUDO_DEBUG_NSS) |
|
|
uid = (uid_t) atoi(user + 1); | uid = (uid_t) atoid(user + 1, NULL, NULL, &errstr); |
return sudo_fakepwnamid(user, uid, gid); | if (errstr != NULL) { |
| sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG, |
| "uid %s %s", user, errstr); |
| debug_return_ptr(NULL); |
| } |
| debug_return_ptr(sudo_mkpwent(user, uid, gid, NULL, NULL)); |
} |
} |
|
|
void |
void |
sudo_setpwent(void) |
sudo_setpwent(void) |
{ |
{ |
|
debug_decl(sudo_setpwent, SUDO_DEBUG_NSS) |
|
|
setpwent(); |
setpwent(); |
if (pwcache_byuid == NULL) |
if (pwcache_byuid == NULL) |
pwcache_byuid = rbcreate(cmp_pwuid); |
pwcache_byuid = rbcreate(cmp_pwuid); |
if (pwcache_byname == NULL) |
if (pwcache_byname == NULL) |
pwcache_byname = rbcreate(cmp_pwnam); |
pwcache_byname = rbcreate(cmp_pwnam); |
|
|
|
debug_return; |
} |
} |
|
|
void |
void |
sudo_freepwcache(void) |
sudo_freepwcache(void) |
{ |
{ |
|
debug_decl(sudo_freepwcache, SUDO_DEBUG_NSS) |
|
|
if (pwcache_byuid != NULL) { |
if (pwcache_byuid != NULL) { |
rbdestroy(pwcache_byuid, pw_delref_item); | rbdestroy(pwcache_byuid, sudo_pw_delref_item); |
pwcache_byuid = NULL; |
pwcache_byuid = NULL; |
} |
} |
if (pwcache_byname != NULL) { |
if (pwcache_byname != NULL) { |
rbdestroy(pwcache_byname, pw_delref_item); | rbdestroy(pwcache_byname, sudo_pw_delref_item); |
pwcache_byname = NULL; |
pwcache_byname = NULL; |
} |
} |
|
|
|
debug_return; |
} |
} |
|
|
void |
void |
sudo_endpwent(void) |
sudo_endpwent(void) |
{ |
{ |
|
debug_decl(sudo_endpwent, SUDO_DEBUG_NSS) |
|
|
endpwent(); |
endpwent(); |
sudo_freepwcache(); |
sudo_freepwcache(); |
|
|
|
debug_return; |
} |
} |
|
|
/* |
/* |
Line 420 cmp_grgid(const void *v1, const void *v2)
|
Line 336 cmp_grgid(const void *v1, const void *v2)
|
return ci1->k.gid - ci2->k.gid; |
return ci1->k.gid - ci2->k.gid; |
} |
} |
|
|
/* |
|
* 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. |
|
*/ |
|
static struct cache_item * |
|
make_gritem(const struct group *gr, const char *name) |
|
{ |
|
char *cp; |
|
size_t nsize, psize, nmem, total, len; |
|
struct cache_item *item; |
|
struct group *newgr; |
|
|
|
/* Allocate in one big chunk for easy freeing. */ |
|
nsize = psize = nmem = 0; |
|
total = sizeof(struct cache_item) + sizeof(struct group); |
|
FIELD_SIZE(gr, gr_name, nsize); |
|
FIELD_SIZE(gr, gr_passwd, psize); |
|
if (gr->gr_mem) { |
|
for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) |
|
total += strlen(gr->gr_mem[nmem]) + 1; |
|
nmem++; |
|
total += sizeof(char *) * nmem; |
|
} |
|
if (name != NULL) |
|
total += strlen(name) + 1; |
|
|
|
item = emalloc(total); |
|
cp = (char *) item + sizeof(struct cache_item); |
|
|
|
/* |
|
* Copy in group contents and make strings relative to space |
|
* at the end of the buffer. Note that gr_mem must come |
|
* immediately after struct group to guarantee proper alignment. |
|
*/ |
|
newgr = (struct group *)cp; |
|
memcpy(newgr, gr, sizeof(struct group)); |
|
cp += sizeof(struct group); |
|
if (gr->gr_mem) { |
|
newgr->gr_mem = (char **)cp; |
|
cp += sizeof(char *) * nmem; |
|
for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) { |
|
len = strlen(gr->gr_mem[nmem]) + 1; |
|
memcpy(cp, gr->gr_mem[nmem], len); |
|
newgr->gr_mem[nmem] = cp; |
|
cp += len; |
|
} |
|
newgr->gr_mem[nmem] = NULL; |
|
} |
|
FIELD_COPY(gr, newgr, gr_passwd, psize); |
|
FIELD_COPY(gr, newgr, gr_name, nsize); |
|
|
|
/* Set key and datum. */ |
|
if (name != NULL) { |
|
memcpy(cp, name, strlen(name) + 1); |
|
item->k.name = cp; |
|
} else { |
|
item->k.gid = gr->gr_gid; |
|
} |
|
item->d.gr = newgr; |
|
item->refcnt = 1; |
|
|
|
return item; |
|
} |
|
|
|
#ifdef HAVE_UTMPX_H |
|
# define GROUPNAME_LEN (sizeof((struct utmpx *)0)->ut_user + 1) |
|
#else |
|
# ifdef HAVE_STRUCT_UTMP_UT_USER |
|
# define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_user + 1) |
|
# else |
|
# define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_name + 1) |
|
# endif |
|
#endif /* HAVE_UTMPX_H */ |
|
|
|
/* |
|
* Dynamically allocate space for a struct item plus the key and data |
|
* elements. Fills in datum from the groups and gids arrays. |
|
*/ |
|
static struct cache_item * |
|
make_grlist_item(const char *user, GETGROUPS_T *gids, int ngids) |
|
{ |
|
char *cp; |
|
size_t i, nsize, ngroups, total, len; |
|
struct cache_item *item; |
|
struct group_list *grlist; |
|
struct group *grp; |
|
|
|
#ifdef HAVE_SETAUTHDB |
|
aix_setauthdb((char *) user); |
|
#endif |
|
|
|
/* Allocate in one big chunk for easy freeing. */ |
|
nsize = strlen(user) + 1; |
|
total = sizeof(struct cache_item) + sizeof(struct group_list) + nsize; |
|
total += sizeof(char *) * ngids; |
|
total += sizeof(gid_t *) * ngids; |
|
total += GROUPNAME_LEN * ngids; |
|
|
|
again: |
|
item = emalloc(total); |
|
cp = (char *) item + sizeof(struct cache_item); |
|
|
|
/* |
|
* Copy in group list and make pointers relative to space |
|
* at the end of the buffer. Note that the groups array must come |
|
* immediately after struct group to guarantee proper alignment. |
|
*/ |
|
grlist = (struct group_list *)cp; |
|
zero_bytes(grlist, sizeof(struct group_list)); |
|
cp += sizeof(struct group_list); |
|
grlist->groups = (char **)cp; |
|
cp += sizeof(char *) * ngids; |
|
grlist->gids = (gid_t *)cp; |
|
cp += sizeof(gid_t) * ngids; |
|
|
|
/* Set key and datum. */ |
|
memcpy(cp, user, nsize); |
|
item->k.name = cp; |
|
item->d.grlist = grlist; |
|
item->refcnt = 1; |
|
cp += nsize; |
|
|
|
/* |
|
* Store group IDs. |
|
*/ |
|
for (i = 0; i < ngids; i++) |
|
grlist->gids[i] = gids[i]; |
|
grlist->ngids = ngids; |
|
|
|
/* |
|
* Resolve and store group names by ID. |
|
*/ |
|
ngroups = 0; |
|
for (i = 0; i < ngids; i++) { |
|
if ((grp = sudo_getgrgid(gids[i])) != NULL) { |
|
len = strlen(grp->gr_name) + 1; |
|
if (cp - (char *)item + len > total) { |
|
total += len + GROUPNAME_LEN; |
|
efree(item); |
|
gr_delref(grp); |
|
goto again; |
|
} |
|
memcpy(cp, grp->gr_name, len); |
|
grlist->groups[ngroups++] = cp; |
|
cp += len; |
|
gr_delref(grp); |
|
} |
|
} |
|
grlist->ngroups = ngroups; |
|
|
|
#ifdef HAVE_SETAUTHDB |
|
aix_restoreauthdb(); |
|
#endif |
|
|
|
return item; |
|
} |
|
|
|
void |
void |
gr_addref(struct group *gr) | sudo_gr_addref(struct group *gr) |
{ |
{ |
|
debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS) |
ptr_to_item(gr)->refcnt++; |
ptr_to_item(gr)->refcnt++; |
|
debug_return; |
} |
} |
|
|
static void |
static void |
gr_delref_item(void *v) | sudo_gr_delref_item(void *v) |
{ |
{ |
struct cache_item *item = v; |
struct cache_item *item = v; |
|
debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS) |
|
|
if (--item->refcnt == 0) |
if (--item->refcnt == 0) |
efree(item); |
efree(item); |
|
|
|
debug_return; |
} |
} |
|
|
void |
void |
gr_delref(struct group *gr) | sudo_gr_delref(struct group *gr) |
{ |
{ |
gr_delref_item(ptr_to_item(gr)); | debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS) |
| sudo_gr_delref_item(ptr_to_item(gr)); |
| debug_return; |
} |
} |
|
|
/* |
/* |
Line 607 sudo_getgrgid(gid_t gid)
|
Line 372 sudo_getgrgid(gid_t gid)
|
{ |
{ |
struct cache_item key, *item; |
struct cache_item key, *item; |
struct rbnode *node; |
struct rbnode *node; |
|
debug_decl(sudo_getgrgid, SUDO_DEBUG_NSS) |
|
|
key.k.gid = gid; |
key.k.gid = gid; |
if ((node = rbfind(grcache_bygid, &key)) != NULL) { |
if ((node = rbfind(grcache_bygid, &key)) != NULL) { |
Line 616 sudo_getgrgid(gid_t gid)
|
Line 382 sudo_getgrgid(gid_t gid)
|
/* |
/* |
* Cache group db entry if it exists or a negative response if not. |
* Cache group db entry if it exists or a negative response if not. |
*/ |
*/ |
if ((key.d.gr = getgrgid(gid)) != NULL) { | item = sudo_make_gritem(gid, NULL); |
item = make_gritem(key.d.gr, NULL); | if (item == NULL) { |
if (rbinsert(grcache_bygid, item) != NULL) | item = ecalloc(1, sizeof(*item)); |
errorx(1, _("unable to cache gid %u (%s), already exists"), | |
(unsigned int) gid, key.d.gr->gr_name); | |
} else { | |
item = emalloc(sizeof(*item)); | |
item->refcnt = 1; |
item->refcnt = 1; |
item->k.gid = gid; |
item->k.gid = gid; |
item->d.gr = NULL; | /* item->d.gr = NULL; */ |
if (rbinsert(grcache_bygid, item) != NULL) | |
errorx(1, _("unable to cache gid %u, already exists"), | |
(unsigned int) gid); | |
} |
} |
|
if (rbinsert(grcache_bygid, item) != NULL) |
|
fatalx(U_("unable to cache gid %u, already exists"), |
|
(unsigned int) gid); |
done: |
done: |
item->refcnt++; |
item->refcnt++; |
return item->d.gr; | debug_return_ptr(item->d.gr); |
} |
} |
|
|
/* |
/* |
Line 644 sudo_getgrnam(const char *name)
|
Line 406 sudo_getgrnam(const char *name)
|
struct cache_item key, *item; |
struct cache_item key, *item; |
struct rbnode *node; |
struct rbnode *node; |
size_t len; |
size_t len; |
|
debug_decl(sudo_getgrnam, SUDO_DEBUG_NSS) |
|
|
key.k.name = (char *) name; |
key.k.name = (char *) name; |
if ((node = rbfind(grcache_byname, &key)) != NULL) { |
if ((node = rbfind(grcache_byname, &key)) != NULL) { |
Line 653 sudo_getgrnam(const char *name)
|
Line 416 sudo_getgrnam(const char *name)
|
/* |
/* |
* Cache group db entry if it exists or a negative response if not. |
* Cache group db entry if it exists or a negative response if not. |
*/ |
*/ |
if ((key.d.gr = getgrnam(name)) != NULL) { | item = sudo_make_gritem((gid_t)-1, name); |
item = make_gritem(key.d.gr, name); | if (item == NULL) { |
if (rbinsert(grcache_byname, item) != NULL) | |
errorx(1, _("unable to cache group %s, already exists"), name); | |
} else { | |
len = strlen(name) + 1; |
len = strlen(name) + 1; |
item = emalloc(sizeof(*item) + len); | item = ecalloc(1, sizeof(*item) + len); |
item->refcnt = 1; |
item->refcnt = 1; |
item->k.name = (char *) item + sizeof(*item); |
item->k.name = (char *) item + sizeof(*item); |
memcpy(item->k.name, name, len); |
memcpy(item->k.name, name, len); |
item->d.gr = NULL; | /* item->d.gr = NULL; */ |
if (rbinsert(grcache_byname, item) != NULL) | |
errorx(1, _("unable to cache group %s, already exists"), name); | |
} |
} |
|
if (rbinsert(grcache_byname, item) != NULL) |
|
fatalx(U_("unable to cache group %s, already exists"), name); |
done: |
done: |
item->refcnt++; |
item->refcnt++; |
return item->d.gr; | debug_return_ptr(item->d.gr); |
} |
} |
|
|
/* |
/* |
Line 678 done:
|
Line 438 done:
|
struct group * |
struct group * |
sudo_fakegrnam(const char *group) |
sudo_fakegrnam(const char *group) |
{ |
{ |
struct cache_item *item; | struct cache_item_gr *gritem; |
| const char *errstr; |
struct group *gr; |
struct group *gr; |
struct rbnode *node; |
struct rbnode *node; |
size_t len, namelen; | size_t len, name_len; |
int i; |
int i; |
|
debug_decl(sudo_fakegrnam, SUDO_DEBUG_NSS) |
|
|
namelen = strlen(group); | name_len = strlen(group); |
len = sizeof(*item) + sizeof(*gr) + namelen + 1; | len = sizeof(*gritem) + name_len + 1; |
|
|
for (i = 0; i < 2; i++) { |
for (i = 0; i < 2; i++) { |
item = emalloc(len); | gritem = ecalloc(1, len); |
zero_bytes(item, sizeof(*item) + sizeof(*gr)); | gr = &gritem->gr; |
gr = (struct group *) ((char *)item + sizeof(*item)); | gr->gr_gid = (gid_t) atoid(group + 1, NULL, NULL, &errstr); |
gr->gr_gid = (gid_t) atoi(group + 1); | gr->gr_name = (char *)(gritem + 1); |
gr->gr_name = (char *)gr + sizeof(struct group); | memcpy(gr->gr_name, group, name_len + 1); |
memcpy(gr->gr_name, group, namelen + 1); | if (errstr != NULL) { |
| sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG, |
| "gid %s %s", group, errstr); |
| efree(gritem); |
| debug_return_ptr(NULL); |
| } |
|
|
item->refcnt = 1; | gritem->cache.refcnt = 1; |
item->d.gr = gr; | gritem->cache.d.gr = gr; |
if (i == 0) { |
if (i == 0) { |
/* Store by gid, overwriting cached version. */ | /* Store by gid if it doesn't already exist. */ |
item->k.gid = gr->gr_gid; | gritem->cache.k.gid = gr->gr_gid; |
if ((node = rbinsert(grcache_bygid, item)) != NULL) { | if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) { |
gr_delref_item(node->data); | /* Already exists, free the item we created. */ |
node->data = item; | efree(gritem); |
| gritem = (struct cache_item_gr *) node->data; |
} |
} |
} else { |
} else { |
/* Store by name, overwriting cached version. */ |
/* Store by name, overwriting cached version. */ |
item->k.name = gr->gr_name; | gritem->cache.k.name = gr->gr_name; |
if ((node = rbinsert(grcache_byname, item)) != NULL) { | if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) { |
gr_delref_item(node->data); | /* Already exists, free the item we created. */ |
node->data = item; | efree(gritem); |
| gritem = (struct cache_item_gr *) node->data; |
} |
} |
} |
} |
} |
} |
item->refcnt++; | gritem->cache.refcnt++; |
return gr; | debug_return_ptr(&gritem->gr); |
} |
} |
|
|
void |
void |
grlist_addref(struct group_list *grlist) | sudo_grlist_addref(struct group_list *grlist) |
{ |
{ |
|
debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS) |
ptr_to_item(grlist)->refcnt++; |
ptr_to_item(grlist)->refcnt++; |
|
debug_return; |
} |
} |
|
|
static void |
static void |
grlist_delref_item(void *v) | sudo_grlist_delref_item(void *v) |
{ |
{ |
struct cache_item *item = v; |
struct cache_item *item = v; |
|
debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS) |
|
|
if (--item->refcnt == 0) |
if (--item->refcnt == 0) |
efree(item); |
efree(item); |
|
|
|
debug_return; |
} |
} |
|
|
void |
void |
grlist_delref(struct group_list *grlist) | sudo_grlist_delref(struct group_list *grlist) |
{ |
{ |
grlist_delref_item(ptr_to_item(grlist)); | debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS) |
| sudo_grlist_delref_item(ptr_to_item(grlist)); |
| debug_return; |
} |
} |
|
|
void |
void |
sudo_setgrent(void) |
sudo_setgrent(void) |
{ |
{ |
|
debug_decl(sudo_setgrent, SUDO_DEBUG_NSS) |
|
|
setgrent(); |
setgrent(); |
if (grcache_bygid == NULL) |
if (grcache_bygid == NULL) |
grcache_bygid = rbcreate(cmp_grgid); |
grcache_bygid = rbcreate(cmp_grgid); |
Line 748 sudo_setgrent(void)
|
Line 526 sudo_setgrent(void)
|
grcache_byname = rbcreate(cmp_grnam); |
grcache_byname = rbcreate(cmp_grnam); |
if (grlist_cache == NULL) |
if (grlist_cache == NULL) |
grlist_cache = rbcreate(cmp_grnam); |
grlist_cache = rbcreate(cmp_grnam); |
|
|
|
debug_return; |
} |
} |
|
|
void |
void |
sudo_freegrcache(void) |
sudo_freegrcache(void) |
{ |
{ |
|
debug_decl(sudo_freegrcache, SUDO_DEBUG_NSS) |
|
|
if (grcache_bygid != NULL) { |
if (grcache_bygid != NULL) { |
rbdestroy(grcache_bygid, gr_delref_item); | rbdestroy(grcache_bygid, sudo_gr_delref_item); |
grcache_bygid = NULL; |
grcache_bygid = NULL; |
} |
} |
if (grcache_byname != NULL) { |
if (grcache_byname != NULL) { |
rbdestroy(grcache_byname, gr_delref_item); | rbdestroy(grcache_byname, sudo_gr_delref_item); |
grcache_byname = NULL; |
grcache_byname = NULL; |
} |
} |
if (grlist_cache != NULL) { |
if (grlist_cache != NULL) { |
rbdestroy(grlist_cache, grlist_delref_item); | rbdestroy(grlist_cache, sudo_grlist_delref_item); |
grlist_cache = NULL; |
grlist_cache = NULL; |
} |
} |
|
|
|
debug_return; |
} |
} |
|
|
void |
void |
sudo_endgrent(void) |
sudo_endgrent(void) |
{ |
{ |
|
debug_decl(sudo_endgrent, SUDO_DEBUG_NSS) |
|
|
endgrent(); |
endgrent(); |
sudo_freegrcache(); |
sudo_freegrcache(); |
|
|
|
debug_return; |
} |
} |
|
|
struct group_list * |
struct group_list * |
get_group_list(struct passwd *pw) | sudo_get_grlist(const struct passwd *pw) |
{ |
{ |
struct cache_item key, *item; |
struct cache_item key, *item; |
struct rbnode *node; |
struct rbnode *node; |
size_t len; |
size_t len; |
GETGROUPS_T *gids; | debug_decl(sudo_get_grlist, SUDO_DEBUG_NSS) |
int ngids; | |
|
|
key.k.name = pw->pw_name; |
key.k.name = pw->pw_name; |
if ((node = rbfind(grlist_cache, &key)) != NULL) { |
if ((node = rbfind(grlist_cache, &key)) != NULL) { |
Line 791 get_group_list(struct passwd *pw)
|
Line 578 get_group_list(struct passwd *pw)
|
/* |
/* |
* Cache group db entry if it exists or a negative response if not. |
* Cache group db entry if it exists or a negative response if not. |
*/ |
*/ |
#if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) | item = sudo_make_grlist_item(pw, NULL, NULL); |
ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2; | if (item == NULL) { |
if (ngids < 0) | |
#endif | |
ngids = NGROUPS_MAX * 2; | |
gids = emalloc2(ngids, sizeof(GETGROUPS_T)); | |
if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) { | |
efree(gids); | |
gids = emalloc2(ngids, sizeof(GETGROUPS_T)); | |
if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) { | |
efree(gids); | |
return NULL; | |
} | |
} | |
if (ngids > 0) { | |
if ((item = make_grlist_item(pw->pw_name, gids, ngids)) == NULL) | |
errorx(1, "unable to parse group list for %s", pw->pw_name); | |
efree(gids); | |
if (rbinsert(grlist_cache, item) != NULL) | |
errorx(1, "unable to cache group list for %s, already exists", | |
pw->pw_name); | |
} else { | |
/* Should not happen. */ |
/* Should not happen. */ |
len = strlen(pw->pw_name) + 1; |
len = strlen(pw->pw_name) + 1; |
item = emalloc(sizeof(*item) + len); | item = ecalloc(1, sizeof(*item) + len); |
item->refcnt = 1; |
item->refcnt = 1; |
item->k.name = (char *) item + sizeof(*item); |
item->k.name = (char *) item + sizeof(*item); |
memcpy(item->k.name, pw->pw_name, len); |
memcpy(item->k.name, pw->pw_name, len); |
item->d.grlist = NULL; | /* item->d.grlist = NULL; */ |
if (rbinsert(grlist_cache, item) != NULL) | |
errorx(1, "unable to cache group list for %s, already exists", | |
pw->pw_name); | |
} |
} |
|
if (rbinsert(grlist_cache, item) != NULL) |
|
fatalx(U_("unable to cache group list for %s, already exists"), |
|
pw->pw_name); |
done: |
done: |
item->refcnt++; |
item->refcnt++; |
return item->d.grlist; | debug_return_ptr(item->d.grlist); |
} |
} |
|
|
void |
void |
set_group_list(const char *user, GETGROUPS_T *gids, int ngids) | sudo_set_grlist(struct passwd *pw, char * const *groups, char * const *gids) |
{ |
{ |
struct cache_item key, *item; |
struct cache_item key, *item; |
struct rbnode *node; |
struct rbnode *node; |
|
debug_decl(sudo_set_grlist, SUDO_DEBUG_NSS) |
|
|
/* |
/* |
* Cache group db entry if it doesn't already exist |
* Cache group db entry if it doesn't already exist |
*/ |
*/ |
key.k.name = (char *) user; | key.k.name = pw->pw_name; |
if ((node = rbfind(grlist_cache, &key)) == NULL) { |
if ((node = rbfind(grlist_cache, &key)) == NULL) { |
if ((item = make_grlist_item(user, gids, ngids)) == NULL) | if ((item = sudo_make_grlist_item(pw, groups, gids)) == NULL) |
errorx(1, "unable to parse group list for %s", user); | fatalx(U_("unable to parse groups for %s"), pw->pw_name); |
if (rbinsert(grlist_cache, item) != NULL) |
if (rbinsert(grlist_cache, item) != NULL) |
errorx(1, "unable to cache group list for %s, already exists", | fatalx(U_("unable to cache group list for %s, already exists"), |
user); | pw->pw_name); |
} |
} |
|
debug_return; |
} |
} |
|
|
int | bool |
user_in_group(struct passwd *pw, const char *group) | user_in_group(const struct passwd *pw, const char *group) |
{ |
{ |
struct group_list *grlist; |
struct group_list *grlist; |
struct group *grp = NULL; |
struct group *grp = NULL; |
int i, matched = FALSE; | const char *errstr; |
| int i; |
| bool matched = false; |
| debug_decl(user_in_group, SUDO_DEBUG_NSS) |
|
|
if ((grlist = get_group_list(pw)) != NULL) { | if ((grlist = sudo_get_grlist(pw)) != NULL) { |
/* |
/* |
* If it could be a sudo-style group ID check gids first. |
* If it could be a sudo-style group ID check gids first. |
*/ |
*/ |
if (group[0] == '#') { |
if (group[0] == '#') { |
gid_t gid = atoi(group + 1); | gid_t gid = (gid_t) atoid(group + 1, NULL, NULL, &errstr); |
if (gid == pw->pw_gid) { | if (errstr != NULL) { |
matched = TRUE; | sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_DIAG, |
goto done; | "gid %s %s", group, errstr); |
} | } else { |
for (i = 0; i < grlist->ngids; i++) { | if (gid == pw->pw_gid) { |
if (gid == grlist->gids[i]) { | matched = true; |
matched = TRUE; | |
goto done; |
goto done; |
} |
} |
|
for (i = 0; i < grlist->ngids; i++) { |
|
if (gid == grlist->gids[i]) { |
|
matched = true; |
|
goto done; |
|
} |
|
} |
} |
} |
} |
} |
|
|
Line 879 user_in_group(struct passwd *pw, const char *group)
|
Line 656 user_in_group(struct passwd *pw, const char *group)
|
*/ |
*/ |
for (i = 0; i < grlist->ngroups; i++) { |
for (i = 0; i < grlist->ngroups; i++) { |
if (strcasecmp(group, grlist->groups[i]) == 0) { |
if (strcasecmp(group, grlist->groups[i]) == 0) { |
matched = TRUE; | matched = true; |
goto done; |
goto done; |
} |
} |
} |
} |
Line 887 user_in_group(struct passwd *pw, const char *group)
|
Line 664 user_in_group(struct passwd *pw, const char *group)
|
/* Finally check against user's primary (passwd file) group. */ |
/* Finally check against user's primary (passwd file) group. */ |
if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) { |
if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) { |
if (strcasecmp(group, grp->gr_name) == 0) { |
if (strcasecmp(group, grp->gr_name) == 0) { |
matched = TRUE; | matched = true; |
goto done; |
goto done; |
} |
} |
} |
} |
done: |
done: |
if (grp != NULL) |
if (grp != NULL) |
gr_delref(grp); | sudo_gr_delref(grp); |
grlist_delref(grlist); | sudo_grlist_delref(grlist); |
} |
} |
return matched; | debug_return_bool(matched); |
} |
} |