fix up changelog
[debian/sudo] / pwutil.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2010
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 #include <pwd.h>
52 #include <grp.h>
53
54 #include "sudo.h"
55 #include "redblack.h"
56
57 /*
58  * The passwd and group caches.
59  */
60 static struct rbtree *pwcache_byuid, *pwcache_byname;
61 static struct rbtree *grcache_bygid, *grcache_byname;
62
63 static int  cmp_pwuid   __P((const void *, const void *));
64 static int  cmp_pwnam   __P((const void *, const void *));
65 static int  cmp_grgid   __P((const void *, const void *));
66 static int  cmp_grnam   __P((const void *, const void *));
67
68 /*
69  * Compare by uid.
70  */
71 static int
72 cmp_pwuid(v1, v2)
73     const void *v1;
74     const void *v2;
75 {
76     const struct passwd *pw1 = (const struct passwd *) v1;
77     const struct passwd *pw2 = (const struct passwd *) v2;
78     return(pw1->pw_uid - pw2->pw_uid);
79 }
80
81 /*
82  * Compare by user name.
83  */
84 static int
85 cmp_pwnam(v1, v2)
86     const void *v1;
87     const void *v2;
88 {
89     const struct passwd *pw1 = (const struct passwd *) v1;
90     const struct passwd *pw2 = (const struct passwd *) v2;
91     return(strcasecmp(pw1->pw_name, pw2->pw_name));
92 }
93
94 #define FIELD_SIZE(src, name, size)                     \
95 do {                                                    \
96         if (src->name) {                                \
97                 size = strlen(src->name) + 1;           \
98                 total += size;                          \
99         }                                               \
100 } while (0)
101
102 #define FIELD_COPY(src, dst, name, size)                \
103 do {                                                    \
104         if (src->name) {                                \
105                 memcpy(cp, src->name, size);            \
106                 dst->name = cp;                         \
107                 cp += size;                             \
108         }                                               \
109 } while (0)
110
111 /*
112  * Dynamically allocate space for a struct password and the constituent parts
113  * that we care about.  Fills in pw_passwd from shadow file.
114  */
115 static struct passwd *
116 sudo_pwdup(pw)
117     const struct passwd *pw;
118 {
119     char *cp;
120     const char *pw_shell;
121     size_t nsize, psize, csize, gsize, dsize, ssize, total;
122     struct passwd *newpw;
123
124     /* If shell field is empty, expand to _PATH_BSHELL. */
125     pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
126         ? _PATH_BSHELL : pw->pw_shell;
127
128     /* Allocate in one big chunk for easy freeing. */
129     nsize = psize = csize = gsize = dsize = ssize = 0;
130     total = sizeof(struct passwd);
131     FIELD_SIZE(pw, pw_name, nsize);
132     FIELD_SIZE(pw, pw_passwd, psize);
133 #ifdef HAVE_LOGIN_CAP_H
134     FIELD_SIZE(pw, pw_class, csize);
135 #endif
136     FIELD_SIZE(pw, pw_gecos, gsize);
137     FIELD_SIZE(pw, pw_dir, dsize);
138     /* Treat shell specially since we expand "" -> _PATH_BSHELL */
139     ssize = strlen(pw_shell) + 1;
140     total += ssize;
141
142     if ((cp = malloc(total)) == NULL)
143             return(NULL);
144     newpw = (struct passwd *) cp;
145
146     /*
147      * Copy in passwd contents and make strings relative to space
148      * at the end of the buffer.
149      */
150     memcpy(newpw, pw, sizeof(struct passwd));
151     cp += sizeof(struct passwd);
152     FIELD_COPY(pw, newpw, pw_name, nsize);
153     FIELD_COPY(pw, newpw, pw_passwd, psize);
154 #ifdef HAVE_LOGIN_CAP_H
155     FIELD_COPY(pw, newpw, pw_class, csize);
156 #endif
157     FIELD_COPY(pw, newpw, pw_gecos, gsize);
158     FIELD_COPY(pw, newpw, pw_dir, dsize);
159     /* Treat shell specially since we expand "" -> _PATH_BSHELL */
160     memcpy(cp, pw_shell, ssize);
161     newpw->pw_shell = cp;
162
163     return(newpw);
164 }
165
166 /*
167  * Get a password entry by uid and allocate space for it.
168  * Fills in pw_passwd from shadow file if necessary.
169  */
170 struct passwd *
171 sudo_getpwuid(uid)
172     uid_t uid;
173 {
174     struct passwd key, *pw;
175     struct rbnode *node;
176     char *cp;
177
178     key.pw_uid = uid;
179     if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
180         pw = (struct passwd *) node->data;
181         goto done;
182     }
183     /*
184      * Cache passwd db entry if it exists or a negative response if not.
185      */
186 #ifdef HAVE_SETAUTHDB
187     aix_setauthdb(IDtouser(uid));
188 #endif
189     if ((pw = getpwuid(uid)) != NULL) {
190         pw = sudo_pwdup(pw);
191         cp = sudo_getepw(pw);           /* get shadow password */
192         if (pw->pw_passwd != NULL)
193             zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
194         pw->pw_passwd = cp;
195         if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
196             errorx(1, "unable to cache uid %lu (%s), already exists",
197                 uid, pw->pw_name);
198     } else {
199         pw = emalloc(sizeof(*pw));
200         zero_bytes(pw, sizeof(*pw));
201         pw->pw_uid = uid;
202         if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
203             errorx(1, "unable to cache uid %lu, already exists", uid);
204     }
205 #ifdef HAVE_SETAUTHDB
206     aix_restoreauthdb();
207 #endif
208 done:
209     return(pw->pw_name != NULL ? pw : NULL);
210 }
211
212 /*
213  * Get a password entry by name and allocate space for it.
214  * Fills in pw_passwd from shadow file if necessary.
215  */
216 struct passwd *
217 sudo_getpwnam(name)
218     const char *name;
219 {
220     struct passwd key, *pw;
221     struct rbnode *node;
222     size_t len;
223     char *cp;
224
225     key.pw_name = (char *) name;
226     if ((node = rbfind(pwcache_byname, &key)) != NULL) {
227         pw = (struct passwd *) node->data;
228         goto done;
229     }
230     /*
231      * Cache passwd db entry if it exists or a negative response if not.
232      */
233 #ifdef HAVE_SETAUTHDB
234     aix_setauthdb((char *) name);
235 #endif
236     if ((pw = getpwnam(name)) != NULL) {
237         pw = sudo_pwdup(pw);
238         cp = sudo_getepw(pw);           /* get shadow password */
239         if (pw->pw_passwd != NULL)
240             zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
241         pw->pw_passwd = cp;
242         if (rbinsert(pwcache_byname, (void *) pw) != NULL)
243             errorx(1, "unable to cache user %s, already exists", name);
244     } else {
245         len = strlen(name) + 1;
246         cp = emalloc(sizeof(*pw) + len);
247         zero_bytes(cp, sizeof(*pw));
248         pw = (struct passwd *) cp;
249         cp += sizeof(*pw);
250         memcpy(cp, name, len);
251         pw->pw_name = cp;
252         pw->pw_uid = (uid_t) -1;
253         if (rbinsert(pwcache_byname, (void *) pw) != NULL)
254             errorx(1, "unable to cache user %s, already exists", name);
255     }
256 #ifdef HAVE_SETAUTHDB
257     aix_restoreauthdb();
258 #endif
259 done:
260     return(pw->pw_uid != (uid_t) -1 ? pw : NULL);
261 }
262
263 /*
264  * Take a uid in string form "#123" and return a faked up passwd struct.
265  */
266 struct passwd *
267 sudo_fakepwnam(user, gid)
268     const char *user;
269     gid_t gid;
270 {
271     struct passwd *pw;
272     struct rbnode *node;
273     size_t len;
274
275     len = strlen(user);
276     pw = emalloc(sizeof(struct passwd) + len + 1 /* pw_name */ +
277         sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
278         sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL));
279     zero_bytes(pw, sizeof(struct passwd));
280     pw->pw_uid = (uid_t) atoi(user + 1);
281     pw->pw_gid = gid;
282     pw->pw_name = (char *)pw + sizeof(struct passwd);
283     memcpy(pw->pw_name, user, len + 1);
284     pw->pw_passwd = pw->pw_name + len + 1;
285     memcpy(pw->pw_passwd, "*", 2);
286     pw->pw_gecos = pw->pw_passwd + 2;
287     pw->pw_gecos[0] = '\0';
288     pw->pw_dir = pw->pw_gecos + 1;
289     memcpy(pw->pw_dir, "/", 2);
290     pw->pw_shell = pw->pw_dir + 2;
291     memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
292
293     /* Store by uid and by name, overwriting cached version. */
294     if ((node = rbinsert(pwcache_byuid, pw)) != NULL) {
295         efree(node->data);
296         node->data = (void *) pw;
297     }
298     if ((node = rbinsert(pwcache_byname, pw)) != NULL) {
299         efree(node->data);
300         node->data = (void *) pw;
301     }
302     return(pw);
303 }
304
305 /*
306  * Take a gid in string form "#123" and return a faked up group struct.
307  */
308 struct group *
309 sudo_fakegrnam(group)
310     const char *group;
311 {
312     struct group *gr;
313     struct rbnode *node;
314     size_t len;
315
316     len = strlen(group);
317     gr = emalloc(sizeof(struct group) + len + 1);
318     zero_bytes(gr, sizeof(struct group));
319     gr->gr_gid = (gid_t) atoi(group + 1);
320     gr->gr_name = (char *)gr + sizeof(struct group);
321     strlcpy(gr->gr_name, group, len + 1);
322
323     /* Store by gid and by name, overwriting cached version. */
324     if ((node = rbinsert(grcache_bygid, gr)) != NULL) {
325         efree(node->data);
326         node->data = (void *) gr;
327     }
328     if ((node = rbinsert(grcache_byname, gr)) != NULL) {
329         efree(node->data);
330         node->data = (void *) gr;
331     }
332     return(gr);
333 }
334
335 void
336 sudo_setpwent()
337 {
338     setpwent();
339     sudo_setspent();
340     if (pwcache_byuid == NULL)
341         pwcache_byuid = rbcreate(cmp_pwuid);
342     if (pwcache_byname == NULL)
343         pwcache_byname = rbcreate(cmp_pwnam);
344 }
345
346 #ifdef PURIFY
347 static void pw_free     __P((void *));
348
349 void
350 sudo_freepwcache()
351 {
352     if (pwcache_byuid != NULL) {
353         rbdestroy(pwcache_byuid, pw_free);
354         pwcache_byuid = NULL;
355     }
356     if (pwcache_byname != NULL) {
357         rbdestroy(pwcache_byname, NULL);
358         pwcache_byname = NULL;
359     }
360 }
361
362 static void
363 pw_free(v)
364     void *v;
365 {
366     struct passwd *pw = (struct passwd *) v;
367
368     if (pw->pw_passwd != NULL) {
369         zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
370         efree(pw->pw_passwd);
371     }
372     efree(pw);
373 }
374 #endif /* PURIFY */
375
376 void
377 sudo_endpwent()
378 {
379     endpwent();
380     sudo_endspent();
381 #ifdef PURIFY
382     sudo_freepwcache();
383 #endif
384 }
385
386 /*
387  * Compare by gid.
388  */
389 static int
390 cmp_grgid(v1, v2)
391     const void *v1;
392     const void *v2;
393 {
394     const struct group *grp1 = (const struct group *) v1;
395     const struct group *grp2 = (const struct group *) v2;
396     return(grp1->gr_gid - grp2->gr_gid);
397 }
398
399 /*
400  * Compare by group name.
401  */
402 static int
403 cmp_grnam(v1, v2)
404     const void *v1;
405     const void *v2;
406 {
407     const struct group *grp1 = (const struct group *) v1;
408     const struct group *grp2 = (const struct group *) v2;
409     return(strcasecmp(grp1->gr_name, grp2->gr_name));
410 }
411
412 struct group *
413 sudo_grdup(gr)
414     const struct group *gr;
415 {
416     char *cp;
417     size_t nsize, psize, nmem, total, len;
418     struct group *newgr;
419
420     /* Allocate in one big chunk for easy freeing. */
421     nsize = psize = nmem = 0;
422     total = sizeof(struct group);
423     FIELD_SIZE(gr, gr_name, nsize);
424     FIELD_SIZE(gr, gr_passwd, psize);
425     if (gr->gr_mem) {
426         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
427             total += strlen(gr->gr_mem[nmem]) + 1;
428         nmem++;
429         total += sizeof(char *) * nmem;
430     }
431     if ((cp = malloc(total)) == NULL)
432             return(NULL);
433     newgr = (struct group *)cp;
434
435     /*
436      * Copy in group contents and make strings relative to space
437      * at the end of the buffer.  Note that gr_mem must come
438      * immediately after struct group to guarantee proper alignment.
439      */
440     (void)memcpy(newgr, gr, sizeof(struct group));
441     cp += sizeof(struct group);
442     if (gr->gr_mem) {
443         newgr->gr_mem = (char **)cp;
444         cp += sizeof(char *) * nmem;
445         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
446             len = strlen(gr->gr_mem[nmem]) + 1;
447             memcpy(cp, gr->gr_mem[nmem], len);
448             newgr->gr_mem[nmem] = cp;
449             cp += len;
450         }
451         newgr->gr_mem[nmem] = NULL;
452     }
453     FIELD_COPY(gr, newgr, gr_passwd, psize);
454     FIELD_COPY(gr, newgr, gr_name, nsize);
455
456     return(newgr);
457 }
458
459 /*
460  * Get a group entry by gid and allocate space for it.
461  */
462 struct group *
463 sudo_getgrgid(gid)
464     gid_t gid;
465 {
466     struct group key, *gr;
467     struct rbnode *node;
468
469     key.gr_gid = gid;
470     if ((node = rbfind(grcache_bygid, &key)) != NULL) {
471         gr = (struct group *) node->data;
472         goto done;
473     }
474     /*
475      * Cache group db entry if it exists or a negative response if not.
476      */
477     if ((gr = getgrgid(gid)) != NULL) {
478         gr = sudo_grdup(gr);
479         if (rbinsert(grcache_bygid, (void *) gr) != NULL)
480             errorx(1, "unable to cache gid %lu (%s), already exists",
481                 gid, gr->gr_name);
482     } else {
483         gr = emalloc(sizeof(*gr));
484         zero_bytes(gr, sizeof(*gr));
485         gr->gr_gid = gid;
486         if (rbinsert(grcache_bygid, (void *) gr) != NULL)
487             errorx(1, "unable to cache gid %lu, already exists, gid");
488     }
489 done:
490     return(gr->gr_name != NULL ? gr : NULL);
491 }
492
493 /*
494  * Get a group entry by name and allocate space for it.
495  */
496 struct group *
497 sudo_getgrnam(name)
498     const char *name;
499 {
500     struct group key, *gr;
501     struct rbnode *node;
502     size_t len;
503     char *cp;
504
505     key.gr_name = (char *) name;
506     if ((node = rbfind(grcache_byname, &key)) != NULL) {
507         gr = (struct group *) node->data;
508         goto done;
509     }
510     /*
511      * Cache group db entry if it exists or a negative response if not.
512      */
513     if ((gr = getgrnam(name)) != NULL) {
514         gr = sudo_grdup(gr);
515         if (rbinsert(grcache_byname, (void *) gr) != NULL)
516             errorx(1, "unable to cache group %s, already exists", name);
517     } else {
518         len = strlen(name) + 1;
519         cp = emalloc(sizeof(*gr) + len);
520         zero_bytes(cp, sizeof(*gr));
521         gr = (struct group *) cp;
522         cp += sizeof(*gr);
523         memcpy(cp, name, len);
524         gr->gr_name = cp;
525         gr->gr_gid = (gid_t) -1;
526         if (rbinsert(grcache_byname, (void *) gr) != NULL)
527             errorx(1, "unable to cache group %s, already exists", name);
528     }
529 done:
530     return(gr->gr_gid != (gid_t) -1 ? gr : NULL);
531 }
532
533 void
534 sudo_setgrent()
535 {
536     setgrent();
537     if (grcache_bygid == NULL)
538         grcache_bygid = rbcreate(cmp_grgid);
539     if (grcache_byname == NULL)
540         grcache_byname = rbcreate(cmp_grnam);
541 }
542
543 #ifdef PURIFY
544 void
545 sudo_freegrcache()
546 {
547     if (grcache_bygid != NULL) {
548         rbdestroy(grcache_bygid, free);
549         grcache_bygid = NULL;
550     }
551     if (grcache_byname != NULL) {
552         rbdestroy(grcache_byname, NULL);
553         grcache_byname = NULL;
554     }
555 }
556 #endif /* PURIFY */
557
558 void
559 sudo_endgrent()
560 {
561     endgrent();
562 #ifdef PURIFY
563     sudo_freegrcache();
564 #endif
565 }
566
567 int
568 user_in_group(pw, group)
569     struct passwd *pw;
570     const char *group;
571 {
572 #ifdef HAVE_MBR_CHECK_MEMBERSHIP
573     uuid_t gu, uu;
574     int ismember;
575 #else
576     char **gr_mem;
577     int i;
578 #endif
579     struct group *grp;
580
581 #ifdef HAVE_SETAUTHDB
582     aix_setauthdb(pw->pw_name);
583 #endif
584     grp = sudo_getgrnam(group);
585 #ifdef HAVE_SETAUTHDB
586     aix_restoreauthdb();
587 #endif
588     if (grp == NULL)
589         return(FALSE);
590
591     /* check against user's primary (passwd file) gid */
592     if (grp->gr_gid == pw->pw_gid)
593         return(TRUE);
594
595 #ifdef HAVE_MBR_CHECK_MEMBERSHIP
596     /* If we are matching the invoking user use the stashed uuid. */
597     if (strcmp(pw->pw_name, user_name) == 0) {
598         if (mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
599             mbr_check_membership(user_uuid, gu, &ismember) == 0 && ismember)
600             return(TRUE);
601     } else {
602         if (mbr_uid_to_uuid(pw->pw_uid, uu) == 0 &&
603             mbr_gid_to_uuid(grp->gr_gid, gu) == 0 &&
604             mbr_check_membership(uu, gu, &ismember) == 0 && ismember)
605             return(TRUE);
606     }
607 #else /* HAVE_MBR_CHECK_MEMBERSHIP */
608 # ifdef HAVE_GETGROUPS
609     /*
610      * If we are matching the invoking or list user and that user has a
611      * supplementary group vector, check it.
612      */
613     if (user_ngroups > 0 &&
614         strcmp(pw->pw_name, list_pw ? list_pw->pw_name : user_name) == 0) {
615         for (i = 0; i < user_ngroups; i++) {
616             if (grp->gr_gid == user_groups[i])
617                 return(TRUE);
618         }
619     } else
620 # endif /* HAVE_GETGROUPS */
621     {
622         if (grp != NULL && grp->gr_mem != NULL) {
623             for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) {
624                 if (strcmp(*gr_mem, pw->pw_name) == 0)
625                     return(TRUE);
626             }
627         }
628     }
629 #endif /* HAVE_MBR_CHECK_MEMBERSHIP */
630
631     return(FALSE);
632 }