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