2 * Copyright (c) 1996, 1998-2005, 2007-2013
3 * Todd C. Miller <Todd.Miller@courtesan.com>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * Sponsored in part by the Defense Advanced Research Projects
18 * Agency (DARPA) and Air Force Research Laboratory, Air Force
19 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
24 #include <sys/types.h>
33 #endif /* STDC_HEADERS */
35 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
39 #endif /* HAVE_STRING_H */
42 #endif /* HAVE_STRINGS_H */
45 #endif /* HAVE_UNISTD_H */
48 #endif /* HAVE_SETAUTHDB */
57 * The passwd and group caches.
59 static struct rbtree *pwcache_byuid, *pwcache_byname;
60 static struct rbtree *grcache_bygid, *grcache_byname;
61 static struct rbtree *grlist_cache;
63 static int cmp_pwuid(const void *, const void *);
64 static int cmp_pwnam(const void *, const void *);
65 static int cmp_grgid(const void *, const void *);
67 #define cmp_grnam cmp_pwnam
73 cmp_pwuid(const void *v1, const void *v2)
75 const struct cache_item *ci1 = (const struct cache_item *) v1;
76 const struct cache_item *ci2 = (const struct cache_item *) v2;
77 return ci1->k.uid - ci2->k.uid;
81 * Compare by user name.
84 cmp_pwnam(const void *v1, const void *v2)
86 const struct cache_item *ci1 = (const struct cache_item *) v1;
87 const struct cache_item *ci2 = (const struct cache_item *) v2;
88 return strcmp(ci1->k.name, ci2->k.name);
92 sudo_pw_addref(struct passwd *pw)
94 debug_decl(sudo_pw_addref, SUDO_DEBUG_NSS)
95 ptr_to_item(pw)->refcnt++;
100 sudo_pw_delref_item(void *v)
102 struct cache_item *item = v;
103 debug_decl(sudo_pw_delref_item, SUDO_DEBUG_NSS)
105 if (--item->refcnt == 0)
112 sudo_pw_delref(struct passwd *pw)
114 debug_decl(sudo_pw_delref, SUDO_DEBUG_NSS)
115 sudo_pw_delref_item(ptr_to_item(pw));
120 * Get a password entry by uid and allocate space for it.
123 sudo_getpwuid(uid_t uid)
125 struct cache_item key, *item;
127 debug_decl(sudo_getpwuid, SUDO_DEBUG_NSS)
130 if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
131 item = (struct cache_item *) node->data;
135 * Cache passwd db entry if it exists or a negative response if not.
137 #ifdef HAVE_SETAUTHDB
138 aix_setauthdb(IDtouser(uid));
140 item = sudo_make_pwitem(uid, NULL);
142 item = ecalloc(1, sizeof(*item));
145 /* item->d.pw = NULL; */
147 if (rbinsert(pwcache_byuid, item) != NULL)
148 fatalx(_("unable to cache uid %u, already exists"),
150 #ifdef HAVE_SETAUTHDB
155 debug_return_ptr(item->d.pw);
159 * Get a password entry by name and allocate space for it.
162 sudo_getpwnam(const char *name)
164 struct cache_item key, *item;
167 debug_decl(sudo_getpwnam, SUDO_DEBUG_NSS)
169 key.k.name = (char *) name;
170 if ((node = rbfind(pwcache_byname, &key)) != NULL) {
171 item = (struct cache_item *) node->data;
175 * Cache passwd db entry if it exists or a negative response if not.
177 #ifdef HAVE_SETAUTHDB
178 aix_setauthdb((char *) name);
180 item = sudo_make_pwitem((uid_t)-1, name);
182 len = strlen(name) + 1;
183 item = ecalloc(1, sizeof(*item) + len);
185 item->k.name = (char *) item + sizeof(*item);
186 memcpy(item->k.name, name, len);
187 /* item->d.pw = NULL; */
189 if (rbinsert(pwcache_byname, item) != NULL)
190 fatalx(_("unable to cache user %s, already exists"), name);
191 #ifdef HAVE_SETAUTHDB
196 debug_return_ptr(item->d.pw);
200 * Take a user, uid, gid, home and shell and return a faked up passwd struct.
201 * If home or shell are NULL default values will be used.
204 sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home,
207 struct cache_item_pw *pwitem;
210 size_t len, name_len, home_len, shell_len;
212 debug_decl(sudo_mkpwent, SUDO_DEBUG_NSS)
214 /* Optional arguments. */
218 shell = _PATH_BSHELL;
220 name_len = strlen(user);
221 home_len = strlen(home);
222 shell_len = strlen(shell);
223 len = sizeof(*pwitem) + name_len + 1 /* pw_name */ +
224 sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
225 home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */;
227 for (i = 0; i < 2; i++) {
228 pwitem = ecalloc(1, len);
232 pw->pw_name = (char *)(pwitem + 1);
233 memcpy(pw->pw_name, user, name_len + 1);
234 pw->pw_passwd = pw->pw_name + name_len + 1;
235 memcpy(pw->pw_passwd, "*", 2);
236 pw->pw_gecos = pw->pw_passwd + 2;
237 pw->pw_gecos[0] = '\0';
238 pw->pw_dir = pw->pw_gecos + 1;
239 memcpy(pw->pw_dir, home, home_len + 1);
240 pw->pw_shell = pw->pw_dir + home_len + 1;
241 memcpy(pw->pw_shell, shell, shell_len + 1);
243 pwitem->cache.refcnt = 1;
244 pwitem->cache.d.pw = pw;
246 /* Store by uid if it doesn't already exist. */
247 pwitem->cache.k.uid = pw->pw_uid;
248 if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {
249 /* Already exists, free the item we created. */
251 pwitem = (struct cache_item_pw *) node->data;
254 /* Store by name if it doesn't already exist. */
255 pwitem->cache.k.name = pw->pw_name;
256 if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {
257 /* Already exists, free the item we created. */
259 pwitem = (struct cache_item_pw *) node->data;
263 pwitem->cache.refcnt++;
264 debug_return_ptr(&pwitem->pw);
268 * Take a uid in string form "#123" and return a faked up passwd struct.
271 sudo_fakepwnam(const char *user, gid_t gid)
275 uid = (uid_t) atoi(user + 1);
276 return sudo_mkpwent(user, uid, gid, NULL, NULL);
282 debug_decl(sudo_setpwent, SUDO_DEBUG_NSS)
285 if (pwcache_byuid == NULL)
286 pwcache_byuid = rbcreate(cmp_pwuid);
287 if (pwcache_byname == NULL)
288 pwcache_byname = rbcreate(cmp_pwnam);
294 sudo_freepwcache(void)
296 debug_decl(sudo_freepwcache, SUDO_DEBUG_NSS)
298 if (pwcache_byuid != NULL) {
299 rbdestroy(pwcache_byuid, sudo_pw_delref_item);
300 pwcache_byuid = NULL;
302 if (pwcache_byname != NULL) {
303 rbdestroy(pwcache_byname, sudo_pw_delref_item);
304 pwcache_byname = NULL;
313 debug_decl(sudo_endpwent, SUDO_DEBUG_NSS)
325 cmp_grgid(const void *v1, const void *v2)
327 const struct cache_item *ci1 = (const struct cache_item *) v1;
328 const struct cache_item *ci2 = (const struct cache_item *) v2;
329 return ci1->k.gid - ci2->k.gid;
333 sudo_gr_addref(struct group *gr)
335 debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS)
336 ptr_to_item(gr)->refcnt++;
341 sudo_gr_delref_item(void *v)
343 struct cache_item *item = v;
344 debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS)
346 if (--item->refcnt == 0)
353 sudo_gr_delref(struct group *gr)
355 debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
356 sudo_gr_delref_item(ptr_to_item(gr));
361 * Get a group entry by gid and allocate space for it.
364 sudo_getgrgid(gid_t gid)
366 struct cache_item key, *item;
368 debug_decl(sudo_getgrgid, SUDO_DEBUG_NSS)
371 if ((node = rbfind(grcache_bygid, &key)) != NULL) {
372 item = (struct cache_item *) node->data;
376 * Cache group db entry if it exists or a negative response if not.
378 item = sudo_make_gritem(gid, NULL);
380 item = ecalloc(1, sizeof(*item));
383 /* item->d.gr = NULL; */
385 if (rbinsert(grcache_bygid, item) != NULL)
386 fatalx(_("unable to cache gid %u, already exists"),
390 debug_return_ptr(item->d.gr);
394 * Get a group entry by name and allocate space for it.
397 sudo_getgrnam(const char *name)
399 struct cache_item key, *item;
402 debug_decl(sudo_getgrnam, SUDO_DEBUG_NSS)
404 key.k.name = (char *) name;
405 if ((node = rbfind(grcache_byname, &key)) != NULL) {
406 item = (struct cache_item *) node->data;
410 * Cache group db entry if it exists or a negative response if not.
412 item = sudo_make_gritem((gid_t)-1, name);
414 len = strlen(name) + 1;
415 item = ecalloc(1, sizeof(*item) + len);
417 item->k.name = (char *) item + sizeof(*item);
418 memcpy(item->k.name, name, len);
419 /* item->d.gr = NULL; */
421 if (rbinsert(grcache_byname, item) != NULL)
422 fatalx(_("unable to cache group %s, already exists"), name);
425 debug_return_ptr(item->d.gr);
429 * Take a gid in string form "#123" and return a faked up group struct.
432 sudo_fakegrnam(const char *group)
434 struct cache_item_gr *gritem;
437 size_t len, name_len;
439 debug_decl(sudo_fakegrnam, SUDO_DEBUG_NSS)
441 name_len = strlen(group);
442 len = sizeof(*gritem) + name_len + 1;
444 for (i = 0; i < 2; i++) {
445 gritem = ecalloc(1, len);
447 gr->gr_gid = (gid_t) atoi(group + 1);
448 gr->gr_name = (char *)(gritem + 1);
449 memcpy(gr->gr_name, group, name_len + 1);
451 gritem->cache.refcnt = 1;
452 gritem->cache.d.gr = gr;
454 /* Store by gid if it doesn't already exist. */
455 gritem->cache.k.gid = gr->gr_gid;
456 if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) {
457 /* Already exists, free the item we created. */
459 gritem = (struct cache_item_gr *) node->data;
462 /* Store by name, overwriting cached version. */
463 gritem->cache.k.name = gr->gr_name;
464 if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) {
465 /* Already exists, free the item we created. */
467 gritem = (struct cache_item_gr *) node->data;
471 gritem->cache.refcnt++;
472 debug_return_ptr(&gritem->gr);
476 sudo_grlist_addref(struct group_list *grlist)
478 debug_decl(sudo_gr_addref, SUDO_DEBUG_NSS)
479 ptr_to_item(grlist)->refcnt++;
484 sudo_grlist_delref_item(void *v)
486 struct cache_item *item = v;
487 debug_decl(sudo_gr_delref_item, SUDO_DEBUG_NSS)
489 if (--item->refcnt == 0)
496 sudo_grlist_delref(struct group_list *grlist)
498 debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
499 sudo_grlist_delref_item(ptr_to_item(grlist));
506 debug_decl(sudo_setgrent, SUDO_DEBUG_NSS)
509 if (grcache_bygid == NULL)
510 grcache_bygid = rbcreate(cmp_grgid);
511 if (grcache_byname == NULL)
512 grcache_byname = rbcreate(cmp_grnam);
513 if (grlist_cache == NULL)
514 grlist_cache = rbcreate(cmp_grnam);
520 sudo_freegrcache(void)
522 debug_decl(sudo_freegrcache, SUDO_DEBUG_NSS)
524 if (grcache_bygid != NULL) {
525 rbdestroy(grcache_bygid, sudo_gr_delref_item);
526 grcache_bygid = NULL;
528 if (grcache_byname != NULL) {
529 rbdestroy(grcache_byname, sudo_gr_delref_item);
530 grcache_byname = NULL;
532 if (grlist_cache != NULL) {
533 rbdestroy(grlist_cache, sudo_grlist_delref_item);
543 debug_decl(sudo_endgrent, SUDO_DEBUG_NSS)
552 sudo_get_grlist(struct passwd *pw)
554 struct cache_item key, *item;
557 debug_decl(sudo_get_grlist, SUDO_DEBUG_NSS)
559 key.k.name = pw->pw_name;
560 if ((node = rbfind(grlist_cache, &key)) != NULL) {
561 item = (struct cache_item *) node->data;
565 * Cache group db entry if it exists or a negative response if not.
567 item = sudo_make_grlist_item(pw, NULL, NULL);
569 /* Should not happen. */
570 len = strlen(pw->pw_name) + 1;
571 item = ecalloc(1, sizeof(*item) + len);
573 item->k.name = (char *) item + sizeof(*item);
574 memcpy(item->k.name, pw->pw_name, len);
575 /* item->d.grlist = NULL; */
577 if (rbinsert(grlist_cache, item) != NULL)
578 fatalx(_("unable to cache group list for %s, already exists"),
582 debug_return_ptr(item->d.grlist);
586 sudo_set_grlist(struct passwd *pw, char * const *groups, char * const *gids)
588 struct cache_item key, *item;
590 debug_decl(sudo_set_grlist, SUDO_DEBUG_NSS)
593 * Cache group db entry if it doesn't already exist
595 key.k.name = pw->pw_name;
596 if ((node = rbfind(grlist_cache, &key)) == NULL) {
597 if ((item = sudo_make_grlist_item(pw, groups, gids)) == NULL)
598 fatalx(_("unable to parse groups for %s"), pw->pw_name);
599 if (rbinsert(grlist_cache, item) != NULL)
600 fatalx(_("unable to cache group list for %s, already exists"),
607 user_in_group(struct passwd *pw, const char *group)
609 struct group_list *grlist;
610 struct group *grp = NULL;
612 bool matched = false;
613 debug_decl(user_in_group, SUDO_DEBUG_NSS)
615 if ((grlist = sudo_get_grlist(pw)) != NULL) {
617 * If it could be a sudo-style group ID check gids first.
619 if (group[0] == '#') {
620 gid_t gid = atoi(group + 1);
621 if (gid == pw->pw_gid) {
625 for (i = 0; i < grlist->ngids; i++) {
626 if (gid == grlist->gids[i]) {
634 * Next check the supplementary group vector.
635 * It usually includes the password db group too.
637 for (i = 0; i < grlist->ngroups; i++) {
638 if (strcasecmp(group, grlist->groups[i]) == 0) {
644 /* Finally check against user's primary (passwd file) group. */
645 if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
646 if (strcasecmp(group, grp->gr_name) == 0) {
654 sudo_grlist_delref(grlist);
656 debug_return_bool(matched);