Annotation of embedaddon/sudo/plugins/sudoers/pwutil.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (c) 1996, 1998-2005, 2007-2011
3: * Todd C. Miller <Todd.Miller@courtesan.com>
4: *
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.
8: *
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.
16: *
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.
20: */
21:
22: #include <config.h>
23:
24: #include <sys/types.h>
25: #include <sys/stat.h>
26: #include <sys/param.h>
27: #include <stdio.h>
28: #ifdef STDC_HEADERS
29: # include <stdlib.h>
30: # include <stddef.h>
31: #else
32: # ifdef HAVE_STDLIB_H
33: # include <stdlib.h>
34: # endif
35: #endif /* STDC_HEADERS */
36: #ifdef HAVE_STRING_H
37: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
38: # include <memory.h>
39: # endif
40: # include <string.h>
41: #endif /* HAVE_STRING_H */
42: #ifdef HAVE_STRINGS_H
43: # include <strings.h>
44: #endif /* HAVE_STRINGS_H */
45: #ifdef HAVE_UNISTD_H
46: # include <unistd.h>
47: #endif /* HAVE_UNISTD_H */
48: #ifdef HAVE_SETAUTHDB
49: # include <usersec.h>
50: #endif /* HAVE_SETAUTHDB */
51: #ifdef HAVE_UTMPX_H
52: # include <utmpx.h>
53: #else
54: # include <utmp.h>
55: #endif /* HAVE_UTMPX_H */
56: #include <limits.h>
57: #include <pwd.h>
58: #include <grp.h>
59:
60: #include "sudoers.h"
61: #include "redblack.h"
62:
63: /*
64: * The passwd and group caches.
65: */
66: static struct rbtree *pwcache_byuid, *pwcache_byname;
67: static struct rbtree *grcache_bygid, *grcache_byname;
68: static struct rbtree *grlist_cache;
69:
70: static int cmp_pwuid(const void *, const void *);
71: static int cmp_pwnam(const void *, const void *);
72: static int cmp_grgid(const void *, const void *);
73:
74: #define cmp_grnam cmp_pwnam
75:
76: #define ptr_to_item(p) ((struct cache_item *)((char *)(p) - sizeof(struct cache_item)))
77:
78: struct cache_item {
79: unsigned int refcnt;
80: /* key */
81: union {
82: uid_t uid;
83: gid_t gid;
84: char *name;
85: } k;
86: /* datum */
87: union {
88: struct passwd *pw;
89: struct group *gr;
90: struct group_list *grlist;
91: } d;
92: };
93:
94: /*
95: * Compare by uid.
96: */
97: static int
98: cmp_pwuid(const void *v1, const void *v2)
99: {
100: const struct cache_item *ci1 = (const struct cache_item *) v1;
101: const struct cache_item *ci2 = (const struct cache_item *) v2;
102: return ci1->k.uid - ci2->k.uid;
103: }
104:
105: /*
106: * Compare by user name.
107: */
108: static int
109: cmp_pwnam(const void *v1, const void *v2)
110: {
111: const struct cache_item *ci1 = (const struct cache_item *) v1;
112: const struct cache_item *ci2 = (const struct cache_item *) v2;
113: return strcmp(ci1->k.name, ci2->k.name);
114: }
115:
116: #define FIELD_SIZE(src, name, size) \
117: do { \
118: if (src->name) { \
119: size = strlen(src->name) + 1; \
120: total += size; \
121: } \
122: } while (0)
123:
124: #define FIELD_COPY(src, dst, name, size) \
125: do { \
126: if (src->name) { \
127: memcpy(cp, src->name, size); \
128: dst->name = cp; \
129: cp += size; \
130: } \
131: } while (0)
132:
133: /*
134: * Dynamically allocate space for a struct item plus the key and data
135: * elements. If name is non-NULL it is used as the key, else the
136: * uid is the key. Fills in datum from struct password.
137: *
138: * We would like to fill in the encrypted password too but the
139: * call to the shadow function could overwrite the pw buffer (NIS).
140: */
141: static struct cache_item *
142: make_pwitem(const struct passwd *pw, const char *name)
143: {
144: char *cp;
145: const char *pw_shell;
146: size_t nsize, psize, csize, gsize, dsize, ssize, total;
147: struct cache_item *item;
148: struct passwd *newpw;
149:
150: /* If shell field is empty, expand to _PATH_BSHELL. */
151: pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
152: ? _PATH_BSHELL : pw->pw_shell;
153:
154: /* Allocate in one big chunk for easy freeing. */
155: nsize = psize = csize = gsize = dsize = ssize = 0;
156: total = sizeof(struct cache_item) + sizeof(struct passwd);
157: FIELD_SIZE(pw, pw_name, nsize);
158: FIELD_SIZE(pw, pw_passwd, psize);
159: #ifdef HAVE_LOGIN_CAP_H
160: FIELD_SIZE(pw, pw_class, csize);
161: #endif
162: FIELD_SIZE(pw, pw_gecos, gsize);
163: FIELD_SIZE(pw, pw_dir, dsize);
164: /* Treat shell specially since we expand "" -> _PATH_BSHELL */
165: ssize = strlen(pw_shell) + 1;
166: total += ssize;
167: if (name != NULL)
168: total += strlen(name) + 1;
169:
170: /* Allocate space for struct item, struct passwd and the strings. */
171: item = emalloc(total);
172: cp = (char *) item + sizeof(struct cache_item);
173:
174: /*
175: * Copy in passwd contents and make strings relative to space
176: * at the end of the buffer.
177: */
178: newpw = (struct passwd *) cp;
179: memcpy(newpw, pw, sizeof(struct passwd));
180: cp += sizeof(struct passwd);
181: FIELD_COPY(pw, newpw, pw_name, nsize);
182: FIELD_COPY(pw, newpw, pw_passwd, psize);
183: #ifdef HAVE_LOGIN_CAP_H
184: FIELD_COPY(pw, newpw, pw_class, csize);
185: #endif
186: FIELD_COPY(pw, newpw, pw_gecos, gsize);
187: FIELD_COPY(pw, newpw, pw_dir, dsize);
188: /* Treat shell specially since we expand "" -> _PATH_BSHELL */
189: memcpy(cp, pw_shell, ssize);
190: newpw->pw_shell = cp;
191: cp += ssize;
192:
193: /* Set key and datum. */
194: if (name != NULL) {
195: memcpy(cp, name, strlen(name) + 1);
196: item->k.name = cp;
197: } else {
198: item->k.uid = pw->pw_uid;
199: }
200: item->d.pw = newpw;
201: item->refcnt = 1;
202:
203: return item;
204: }
205:
206: void
207: pw_addref(struct passwd *pw)
208: {
209: ptr_to_item(pw)->refcnt++;
210: }
211:
212: static void
213: pw_delref_item(void *v)
214: {
215: struct cache_item *item = v;
216:
217: if (--item->refcnt == 0)
218: efree(item);
219: }
220:
221: void
222: pw_delref(struct passwd *pw)
223: {
224: pw_delref_item(ptr_to_item(pw));
225: }
226:
227: /*
228: * Get a password entry by uid and allocate space for it.
229: * Fills in pw_passwd from shadow file if necessary.
230: */
231: struct passwd *
232: sudo_getpwuid(uid_t uid)
233: {
234: struct cache_item key, *item;
235: struct rbnode *node;
236:
237: key.k.uid = uid;
238: if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
239: item = (struct cache_item *) node->data;
240: goto done;
241: }
242: /*
243: * Cache passwd db entry if it exists or a negative response if not.
244: */
245: #ifdef HAVE_SETAUTHDB
246: aix_setauthdb(IDtouser(uid));
247: #endif
248: if ((key.d.pw = getpwuid(uid)) != NULL) {
249: item = make_pwitem(key.d.pw, NULL);
250: if (rbinsert(pwcache_byuid, item) != NULL)
251: errorx(1, _("unable to cache uid %u (%s), already exists"),
252: (unsigned int) uid, item->d.pw->pw_name);
253: } else {
254: item = emalloc(sizeof(*item));
255: item->refcnt = 1;
256: item->k.uid = uid;
257: item->d.pw = NULL;
258: if (rbinsert(pwcache_byuid, item) != NULL)
259: errorx(1, _("unable to cache uid %u, already exists"),
260: (unsigned int) uid);
261: }
262: #ifdef HAVE_SETAUTHDB
263: aix_restoreauthdb();
264: #endif
265: done:
266: item->refcnt++;
267: return item->d.pw;
268: }
269:
270: /*
271: * Get a password entry by name and allocate space for it.
272: * Fills in pw_passwd from shadow file if necessary.
273: */
274: struct passwd *
275: sudo_getpwnam(const char *name)
276: {
277: struct cache_item key, *item;
278: struct rbnode *node;
279: size_t len;
280:
281: key.k.name = (char *) name;
282: if ((node = rbfind(pwcache_byname, &key)) != NULL) {
283: item = (struct cache_item *) node->data;
284: goto done;
285: }
286: /*
287: * Cache passwd db entry if it exists or a negative response if not.
288: */
289: #ifdef HAVE_SETAUTHDB
290: aix_setauthdb((char *) name);
291: #endif
292: if ((key.d.pw = getpwnam(name)) != NULL) {
293: item = make_pwitem(key.d.pw, name);
294: if (rbinsert(pwcache_byname, item) != NULL)
295: errorx(1, _("unable to cache user %s, already exists"), name);
296: } else {
297: len = strlen(name) + 1;
298: item = emalloc(sizeof(*item) + len);
299: item->refcnt = 1;
300: item->k.name = (char *) item + sizeof(*item);
301: memcpy(item->k.name, name, len);
302: item->d.pw = NULL;
303: if (rbinsert(pwcache_byname, item) != NULL)
304: errorx(1, _("unable to cache user %s, already exists"), name);
305: }
306: #ifdef HAVE_SETAUTHDB
307: aix_restoreauthdb();
308: #endif
309: done:
310: item->refcnt++;
311: return item->d.pw;
312: }
313:
314: /*
315: * Take a user, uid and gid and return a faked up passwd struct.
316: */
317: struct passwd *
318: sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid)
319: {
320: struct cache_item *item;
321: struct passwd *pw;
322: struct rbnode *node;
323: size_t len, namelen;
324: int i;
325:
326: namelen = strlen(user);
327: len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ +
328: sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
329: sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL);
330:
331: for (i = 0; i < 2; i++) {
332: item = emalloc(len);
333: zero_bytes(item, sizeof(*item) + sizeof(*pw));
334: pw = (struct passwd *) ((char *)item + sizeof(*item));
335: pw->pw_uid = uid;
336: pw->pw_gid = gid;
337: pw->pw_name = (char *)pw + sizeof(struct passwd);
338: memcpy(pw->pw_name, user, namelen + 1);
339: pw->pw_passwd = pw->pw_name + namelen + 1;
340: memcpy(pw->pw_passwd, "*", 2);
341: pw->pw_gecos = pw->pw_passwd + 2;
342: pw->pw_gecos[0] = '\0';
343: pw->pw_dir = pw->pw_gecos + 1;
344: memcpy(pw->pw_dir, "/", 2);
345: pw->pw_shell = pw->pw_dir + 2;
346: memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
347:
348: item->refcnt = 1;
349: item->d.pw = pw;
350: if (i == 0) {
351: /* Store by uid, overwriting cached version. */
352: item->k.uid = pw->pw_uid;
353: if ((node = rbinsert(pwcache_byuid, item)) != NULL) {
354: pw_delref_item(node->data);
355: node->data = item;
356: }
357: } else {
358: /* Store by name, overwriting cached version. */
359: item->k.name = pw->pw_name;
360: if ((node = rbinsert(pwcache_byname, item)) != NULL) {
361: pw_delref_item(node->data);
362: node->data = item;
363: }
364: }
365: }
366: item->refcnt++;
367: return pw;
368: }
369:
370: /*
371: * Take a uid in string form "#123" and return a faked up passwd struct.
372: */
373: struct passwd *
374: sudo_fakepwnam(const char *user, gid_t gid)
375: {
376: uid_t uid;
377:
378: uid = (uid_t) atoi(user + 1);
379: return sudo_fakepwnamid(user, uid, gid);
380: }
381:
382: void
383: sudo_setpwent(void)
384: {
385: setpwent();
386: if (pwcache_byuid == NULL)
387: pwcache_byuid = rbcreate(cmp_pwuid);
388: if (pwcache_byname == NULL)
389: pwcache_byname = rbcreate(cmp_pwnam);
390: }
391:
392: void
393: sudo_freepwcache(void)
394: {
395: if (pwcache_byuid != NULL) {
396: rbdestroy(pwcache_byuid, pw_delref_item);
397: pwcache_byuid = NULL;
398: }
399: if (pwcache_byname != NULL) {
400: rbdestroy(pwcache_byname, pw_delref_item);
401: pwcache_byname = NULL;
402: }
403: }
404:
405: void
406: sudo_endpwent(void)
407: {
408: endpwent();
409: sudo_freepwcache();
410: }
411:
412: /*
413: * Compare by gid.
414: */
415: static int
416: cmp_grgid(const void *v1, const void *v2)
417: {
418: const struct cache_item *ci1 = (const struct cache_item *) v1;
419: const struct cache_item *ci2 = (const struct cache_item *) v2;
420: return ci1->k.gid - ci2->k.gid;
421: }
422:
423: /*
424: * Dynamically allocate space for a struct item plus the key and data
425: * elements. If name is non-NULL it is used as the key, else the
426: * gid is the key. Fills in datum from struct group.
427: */
428: static struct cache_item *
429: make_gritem(const struct group *gr, const char *name)
430: {
431: char *cp;
432: size_t nsize, psize, nmem, total, len;
433: struct cache_item *item;
434: struct group *newgr;
435:
436: /* Allocate in one big chunk for easy freeing. */
437: nsize = psize = nmem = 0;
438: total = sizeof(struct cache_item) + sizeof(struct group);
439: FIELD_SIZE(gr, gr_name, nsize);
440: FIELD_SIZE(gr, gr_passwd, psize);
441: if (gr->gr_mem) {
442: for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
443: total += strlen(gr->gr_mem[nmem]) + 1;
444: nmem++;
445: total += sizeof(char *) * nmem;
446: }
447: if (name != NULL)
448: total += strlen(name) + 1;
449:
450: item = emalloc(total);
451: cp = (char *) item + sizeof(struct cache_item);
452:
453: /*
454: * Copy in group contents and make strings relative to space
455: * at the end of the buffer. Note that gr_mem must come
456: * immediately after struct group to guarantee proper alignment.
457: */
458: newgr = (struct group *)cp;
459: memcpy(newgr, gr, sizeof(struct group));
460: cp += sizeof(struct group);
461: if (gr->gr_mem) {
462: newgr->gr_mem = (char **)cp;
463: cp += sizeof(char *) * nmem;
464: for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
465: len = strlen(gr->gr_mem[nmem]) + 1;
466: memcpy(cp, gr->gr_mem[nmem], len);
467: newgr->gr_mem[nmem] = cp;
468: cp += len;
469: }
470: newgr->gr_mem[nmem] = NULL;
471: }
472: FIELD_COPY(gr, newgr, gr_passwd, psize);
473: FIELD_COPY(gr, newgr, gr_name, nsize);
474:
475: /* Set key and datum. */
476: if (name != NULL) {
477: memcpy(cp, name, strlen(name) + 1);
478: item->k.name = cp;
479: } else {
480: item->k.gid = gr->gr_gid;
481: }
482: item->d.gr = newgr;
483: item->refcnt = 1;
484:
485: return item;
486: }
487:
488: #ifdef HAVE_UTMPX_H
489: # define GROUPNAME_LEN (sizeof((struct utmpx *)0)->ut_user + 1)
490: #else
491: # ifdef HAVE_STRUCT_UTMP_UT_USER
492: # define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_user + 1)
493: # else
494: # define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_name + 1)
495: # endif
496: #endif /* HAVE_UTMPX_H */
497:
498: /*
499: * Dynamically allocate space for a struct item plus the key and data
500: * elements. Fills in datum from the groups and gids arrays.
501: */
502: static struct cache_item *
503: make_grlist_item(const char *user, GETGROUPS_T *gids, int ngids)
504: {
505: char *cp;
506: size_t i, nsize, ngroups, total, len;
507: struct cache_item *item;
508: struct group_list *grlist;
509: struct group *grp;
510:
511: #ifdef HAVE_SETAUTHDB
512: aix_setauthdb((char *) user);
513: #endif
514:
515: /* Allocate in one big chunk for easy freeing. */
516: nsize = strlen(user) + 1;
517: total = sizeof(struct cache_item) + sizeof(struct group_list) + nsize;
518: total += sizeof(char *) * ngids;
519: total += sizeof(gid_t *) * ngids;
520: total += GROUPNAME_LEN * ngids;
521:
522: again:
523: item = emalloc(total);
524: cp = (char *) item + sizeof(struct cache_item);
525:
526: /*
527: * Copy in group list and make pointers relative to space
528: * at the end of the buffer. Note that the groups array must come
529: * immediately after struct group to guarantee proper alignment.
530: */
531: grlist = (struct group_list *)cp;
532: zero_bytes(grlist, sizeof(struct group_list));
533: cp += sizeof(struct group_list);
534: grlist->groups = (char **)cp;
535: cp += sizeof(char *) * ngids;
536: grlist->gids = (gid_t *)cp;
537: cp += sizeof(gid_t) * ngids;
538:
539: /* Set key and datum. */
540: memcpy(cp, user, nsize);
541: item->k.name = cp;
542: item->d.grlist = grlist;
543: item->refcnt = 1;
544: cp += nsize;
545:
546: /*
547: * Store group IDs.
548: */
549: for (i = 0; i < ngids; i++)
550: grlist->gids[i] = gids[i];
551: grlist->ngids = ngids;
552:
553: /*
554: * Resolve and store group names by ID.
555: */
556: ngroups = 0;
557: for (i = 0; i < ngids; i++) {
558: if ((grp = sudo_getgrgid(gids[i])) != NULL) {
559: len = strlen(grp->gr_name) + 1;
560: if (cp - (char *)item + len > total) {
561: total += len + GROUPNAME_LEN;
562: efree(item);
563: gr_delref(grp);
564: goto again;
565: }
566: memcpy(cp, grp->gr_name, len);
567: grlist->groups[ngroups++] = cp;
568: cp += len;
569: gr_delref(grp);
570: }
571: }
572: grlist->ngroups = ngroups;
573:
574: #ifdef HAVE_SETAUTHDB
575: aix_restoreauthdb();
576: #endif
577:
578: return item;
579: }
580:
581: void
582: gr_addref(struct group *gr)
583: {
584: ptr_to_item(gr)->refcnt++;
585: }
586:
587: static void
588: gr_delref_item(void *v)
589: {
590: struct cache_item *item = v;
591:
592: if (--item->refcnt == 0)
593: efree(item);
594: }
595:
596: void
597: gr_delref(struct group *gr)
598: {
599: gr_delref_item(ptr_to_item(gr));
600: }
601:
602: /*
603: * Get a group entry by gid and allocate space for it.
604: */
605: struct group *
606: sudo_getgrgid(gid_t gid)
607: {
608: struct cache_item key, *item;
609: struct rbnode *node;
610:
611: key.k.gid = gid;
612: if ((node = rbfind(grcache_bygid, &key)) != NULL) {
613: item = (struct cache_item *) node->data;
614: goto done;
615: }
616: /*
617: * Cache group db entry if it exists or a negative response if not.
618: */
619: if ((key.d.gr = getgrgid(gid)) != NULL) {
620: item = make_gritem(key.d.gr, NULL);
621: if (rbinsert(grcache_bygid, item) != NULL)
622: errorx(1, _("unable to cache gid %u (%s), already exists"),
623: (unsigned int) gid, key.d.gr->gr_name);
624: } else {
625: item = emalloc(sizeof(*item));
626: item->refcnt = 1;
627: item->k.gid = gid;
628: item->d.gr = NULL;
629: if (rbinsert(grcache_bygid, item) != NULL)
630: errorx(1, _("unable to cache gid %u, already exists"),
631: (unsigned int) gid);
632: }
633: done:
634: item->refcnt++;
635: return item->d.gr;
636: }
637:
638: /*
639: * Get a group entry by name and allocate space for it.
640: */
641: struct group *
642: sudo_getgrnam(const char *name)
643: {
644: struct cache_item key, *item;
645: struct rbnode *node;
646: size_t len;
647:
648: key.k.name = (char *) name;
649: if ((node = rbfind(grcache_byname, &key)) != NULL) {
650: item = (struct cache_item *) node->data;
651: goto done;
652: }
653: /*
654: * Cache group db entry if it exists or a negative response if not.
655: */
656: if ((key.d.gr = getgrnam(name)) != NULL) {
657: item = make_gritem(key.d.gr, name);
658: if (rbinsert(grcache_byname, item) != NULL)
659: errorx(1, _("unable to cache group %s, already exists"), name);
660: } else {
661: len = strlen(name) + 1;
662: item = emalloc(sizeof(*item) + len);
663: item->refcnt = 1;
664: item->k.name = (char *) item + sizeof(*item);
665: memcpy(item->k.name, name, len);
666: item->d.gr = NULL;
667: if (rbinsert(grcache_byname, item) != NULL)
668: errorx(1, _("unable to cache group %s, already exists"), name);
669: }
670: done:
671: item->refcnt++;
672: return item->d.gr;
673: }
674:
675: /*
676: * Take a gid in string form "#123" and return a faked up group struct.
677: */
678: struct group *
679: sudo_fakegrnam(const char *group)
680: {
681: struct cache_item *item;
682: struct group *gr;
683: struct rbnode *node;
684: size_t len, namelen;
685: int i;
686:
687: namelen = strlen(group);
688: len = sizeof(*item) + sizeof(*gr) + namelen + 1;
689:
690: for (i = 0; i < 2; i++) {
691: item = emalloc(len);
692: zero_bytes(item, sizeof(*item) + sizeof(*gr));
693: gr = (struct group *) ((char *)item + sizeof(*item));
694: gr->gr_gid = (gid_t) atoi(group + 1);
695: gr->gr_name = (char *)gr + sizeof(struct group);
696: memcpy(gr->gr_name, group, namelen + 1);
697:
698: item->refcnt = 1;
699: item->d.gr = gr;
700: if (i == 0) {
701: /* Store by gid, overwriting cached version. */
702: item->k.gid = gr->gr_gid;
703: if ((node = rbinsert(grcache_bygid, item)) != NULL) {
704: gr_delref_item(node->data);
705: node->data = item;
706: }
707: } else {
708: /* Store by name, overwriting cached version. */
709: item->k.name = gr->gr_name;
710: if ((node = rbinsert(grcache_byname, item)) != NULL) {
711: gr_delref_item(node->data);
712: node->data = item;
713: }
714: }
715: }
716: item->refcnt++;
717: return gr;
718: }
719:
720: void
721: grlist_addref(struct group_list *grlist)
722: {
723: ptr_to_item(grlist)->refcnt++;
724: }
725:
726: static void
727: grlist_delref_item(void *v)
728: {
729: struct cache_item *item = v;
730:
731: if (--item->refcnt == 0)
732: efree(item);
733: }
734:
735: void
736: grlist_delref(struct group_list *grlist)
737: {
738: grlist_delref_item(ptr_to_item(grlist));
739: }
740:
741: void
742: sudo_setgrent(void)
743: {
744: setgrent();
745: if (grcache_bygid == NULL)
746: grcache_bygid = rbcreate(cmp_grgid);
747: if (grcache_byname == NULL)
748: grcache_byname = rbcreate(cmp_grnam);
749: if (grlist_cache == NULL)
750: grlist_cache = rbcreate(cmp_grnam);
751: }
752:
753: void
754: sudo_freegrcache(void)
755: {
756: if (grcache_bygid != NULL) {
757: rbdestroy(grcache_bygid, gr_delref_item);
758: grcache_bygid = NULL;
759: }
760: if (grcache_byname != NULL) {
761: rbdestroy(grcache_byname, gr_delref_item);
762: grcache_byname = NULL;
763: }
764: if (grlist_cache != NULL) {
765: rbdestroy(grlist_cache, grlist_delref_item);
766: grlist_cache = NULL;
767: }
768: }
769:
770: void
771: sudo_endgrent(void)
772: {
773: endgrent();
774: sudo_freegrcache();
775: }
776:
777: struct group_list *
778: get_group_list(struct passwd *pw)
779: {
780: struct cache_item key, *item;
781: struct rbnode *node;
782: size_t len;
783: GETGROUPS_T *gids;
784: int ngids;
785:
786: key.k.name = pw->pw_name;
787: if ((node = rbfind(grlist_cache, &key)) != NULL) {
788: item = (struct cache_item *) node->data;
789: goto done;
790: }
791: /*
792: * Cache group db entry if it exists or a negative response if not.
793: */
794: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
795: ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2;
796: if (ngids < 0)
797: #endif
798: ngids = NGROUPS_MAX * 2;
799: gids = emalloc2(ngids, sizeof(GETGROUPS_T));
800: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
801: efree(gids);
802: gids = emalloc2(ngids, sizeof(GETGROUPS_T));
803: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
804: efree(gids);
805: return NULL;
806: }
807: }
808: if (ngids > 0) {
809: if ((item = make_grlist_item(pw->pw_name, gids, ngids)) == NULL)
810: errorx(1, "unable to parse group list for %s", pw->pw_name);
811: efree(gids);
812: if (rbinsert(grlist_cache, item) != NULL)
813: errorx(1, "unable to cache group list for %s, already exists",
814: pw->pw_name);
815: } else {
816: /* Should not happen. */
817: len = strlen(pw->pw_name) + 1;
818: item = emalloc(sizeof(*item) + len);
819: item->refcnt = 1;
820: item->k.name = (char *) item + sizeof(*item);
821: memcpy(item->k.name, pw->pw_name, len);
822: item->d.grlist = NULL;
823: if (rbinsert(grlist_cache, item) != NULL)
824: errorx(1, "unable to cache group list for %s, already exists",
825: pw->pw_name);
826: }
827: done:
828: item->refcnt++;
829: return item->d.grlist;
830: }
831:
832: void
833: set_group_list(const char *user, GETGROUPS_T *gids, int ngids)
834: {
835: struct cache_item key, *item;
836: struct rbnode *node;
837:
838: /*
839: * Cache group db entry if it doesn't already exist
840: */
841: key.k.name = (char *) user;
842: if ((node = rbfind(grlist_cache, &key)) == NULL) {
843: if ((item = make_grlist_item(user, gids, ngids)) == NULL)
844: errorx(1, "unable to parse group list for %s", user);
845: if (rbinsert(grlist_cache, item) != NULL)
846: errorx(1, "unable to cache group list for %s, already exists",
847: user);
848: }
849: }
850:
851: int
852: user_in_group(struct passwd *pw, const char *group)
853: {
854: struct group_list *grlist;
855: struct group *grp = NULL;
856: int i, matched = FALSE;
857:
858: if ((grlist = get_group_list(pw)) != NULL) {
859: /*
860: * If it could be a sudo-style group ID check gids first.
861: */
862: if (group[0] == '#') {
863: gid_t gid = atoi(group + 1);
864: if (gid == pw->pw_gid) {
865: matched = TRUE;
866: goto done;
867: }
868: for (i = 0; i < grlist->ngids; i++) {
869: if (gid == grlist->gids[i]) {
870: matched = TRUE;
871: goto done;
872: }
873: }
874: }
875:
876: /*
877: * Next check the supplementary group vector.
878: * It usually includes the password db group too.
879: */
880: for (i = 0; i < grlist->ngroups; i++) {
881: if (strcasecmp(group, grlist->groups[i]) == 0) {
882: matched = TRUE;
883: goto done;
884: }
885: }
886:
887: /* Finally check against user's primary (passwd file) group. */
888: if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
889: if (strcasecmp(group, grp->gr_name) == 0) {
890: matched = TRUE;
891: goto done;
892: }
893: }
894: done:
895: if (grp != NULL)
896: gr_delref(grp);
897: grlist_delref(grlist);
898: }
899: return matched;
900: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>