switch from rcS.d to rc[0-6].d
[debian/sudo] / pwutil.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2009
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.23 2009/05/25 12:02:41 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         if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
211             errorx(1, "unable to cache uid %lu (%s), already exists",
212                 uid, pw->pw_name);
213         return(pw);
214     } else {
215         pw = emalloc(sizeof(*pw));
216         zero_bytes(pw, sizeof(*pw));
217         pw->pw_uid = uid;
218         if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
219             errorx(1, "unable to cache uid %lu, already exists", uid);
220         return(NULL);
221     }
222 }
223
224 /*
225  * Get a password entry by name and allocate space for it.
226  * Fills in pw_passwd from shadow file if necessary.
227  */
228 struct passwd *
229 sudo_getpwnam(name)
230     const char *name;
231 {
232     struct passwd key, *pw;
233     struct rbnode *node;
234     size_t len;
235     char *cp;
236
237     key.pw_name = (char *) name;
238     if ((node = rbfind(pwcache_byname, &key)) != NULL) {
239         pw = (struct passwd *) node->data;
240         return(pw->pw_uid != (uid_t) -1 ? pw : NULL);
241     }
242     /*
243      * Cache passwd db entry if it exists or a negative response if not.
244      */
245     if ((pw = getpwnam(name)) != NULL) {
246         pw = sudo_pwdup(pw);
247         cp = sudo_getepw(pw);           /* get shadow password */
248         if (pw->pw_passwd != NULL)
249             zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
250         pw->pw_passwd = cp;
251         if (rbinsert(pwcache_byname, (void *) pw) != NULL)
252             errorx(1, "unable to cache user %s, already exists", name);
253         return(pw);
254     } else {
255         len = strlen(name) + 1;
256         cp = emalloc(sizeof(*pw) + len);
257         zero_bytes(cp, sizeof(*pw));
258         pw = (struct passwd *) cp;
259         cp += sizeof(*pw);
260         memcpy(cp, name, len);
261         pw->pw_name = cp;
262         pw->pw_uid = (uid_t) -1;
263         if (rbinsert(pwcache_byname, (void *) pw) != NULL)
264             errorx(1, "unable to cache user %s, already exists", name);
265         return(NULL);
266     }
267 }
268
269 /*
270  * Take a uid in string form "#123" and return a faked up passwd struct.
271  */
272 struct passwd *
273 sudo_fakepwnam(user, gid)
274     const char *user;
275     gid_t gid;
276 {
277     struct passwd *pw;
278     struct rbnode *node;
279     size_t len;
280
281     len = strlen(user);
282     pw = emalloc(sizeof(struct passwd) + len + 1 /* pw_name */ +
283         sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
284         sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL));
285     zero_bytes(pw, sizeof(struct passwd));
286     pw->pw_uid = (uid_t) atoi(user + 1);
287     pw->pw_gid = gid;
288     pw->pw_name = (char *)pw + sizeof(struct passwd);
289     memcpy(pw->pw_name, user, len + 1);
290     pw->pw_passwd = pw->pw_name + len + 1;
291     memcpy(pw->pw_passwd, "*", 2);
292     pw->pw_gecos = pw->pw_passwd + 2;
293     pw->pw_gecos[0] = '\0';
294     pw->pw_dir = pw->pw_gecos + 1;
295     memcpy(pw->pw_dir, "/", 2);
296     pw->pw_shell = pw->pw_dir + 2;
297     memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
298
299     /* Store by uid and by name, overwriting cached version. */
300     if ((node = rbinsert(pwcache_byuid, pw)) != NULL) {
301         efree(node->data);
302         node->data = (void *) pw;
303     }
304     if ((node = rbinsert(pwcache_byname, pw)) != NULL) {
305         efree(node->data);
306         node->data = (void *) pw;
307     }
308     return(pw);
309 }
310
311 /*
312  * Take a gid in string form "#123" and return a faked up group struct.
313  */
314 struct group *
315 sudo_fakegrnam(group)
316     const char *group;
317 {
318     struct group *gr;
319     struct rbnode *node;
320     size_t len;
321
322     len = strlen(group);
323     gr = emalloc(sizeof(struct group) + len + 1);
324     zero_bytes(gr, sizeof(struct group));
325     gr->gr_gid = (gid_t) atoi(group + 1);
326     gr->gr_name = (char *)gr + sizeof(struct group);
327     strlcpy(gr->gr_name, group, len + 1);
328
329     /* Store by gid and by name, overwriting cached version. */
330     if ((node = rbinsert(grcache_bygid, gr)) != NULL) {
331         efree(node->data);
332         node->data = (void *) gr;
333     }
334     if ((node = rbinsert(grcache_byname, gr)) != NULL) {
335         efree(node->data);
336         node->data = (void *) gr;
337     }
338     return(gr);
339 }
340
341 void
342 sudo_setpwent()
343 {
344     setpwent();
345     sudo_setspent();
346     if (pwcache_byuid == NULL)
347         pwcache_byuid = rbcreate(cmp_pwuid);
348     if (pwcache_byname == NULL)
349         pwcache_byname = rbcreate(cmp_pwnam);
350 }
351
352 #ifdef PURIFY
353 static void pw_free     __P((void *));
354
355 void
356 sudo_freepwcache()
357 {
358     if (pwcache_byuid != NULL) {
359         rbdestroy(pwcache_byuid, pw_free);
360         pwcache_byuid = NULL;
361     }
362     if (pwcache_byname != NULL) {
363         rbdestroy(pwcache_byname, NULL);
364         pwcache_byname = NULL;
365     }
366 }
367
368 static void
369 pw_free(v)
370     void *v;
371 {
372     struct passwd *pw = (struct passwd *) v;
373
374     if (pw->pw_passwd != NULL) {
375         zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
376         efree(pw->pw_passwd);
377     }
378     efree(pw);
379 }
380 #endif /* PURIFY */
381
382 void
383 sudo_endpwent()
384 {
385     endpwent();
386     sudo_endspent();
387 #ifdef PURIFY
388     sudo_freepwcache();
389 #endif
390 }
391
392 /*
393  * Compare by gid.
394  */
395 static int
396 cmp_grgid(v1, v2)
397     const void *v1;
398     const void *v2;
399 {
400     const struct group *grp1 = (const struct group *) v1;
401     const struct group *grp2 = (const struct group *) v2;
402     return(grp1->gr_gid - grp2->gr_gid);
403 }
404
405 /*
406  * Compare by group name.
407  */
408 static int
409 cmp_grnam(v1, v2)
410     const void *v1;
411     const void *v2;
412 {
413     const struct group *grp1 = (const struct group *) v1;
414     const struct group *grp2 = (const struct group *) v2;
415     return(strcmp(grp1->gr_name, grp2->gr_name));
416 }
417
418 struct group *
419 sudo_grdup(gr)
420     const struct group *gr;
421 {
422     char *cp;
423     size_t nsize, psize, nmem, total, len;
424     struct group *newgr;
425
426     /* Allocate in one big chunk for easy freeing. */
427     nsize = psize = nmem = 0;
428     total = sizeof(struct group);
429     FIELD_SIZE(gr, gr_name, nsize);
430     FIELD_SIZE(gr, gr_passwd, psize);
431     if (gr->gr_mem) {
432         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
433             total += strlen(gr->gr_mem[nmem]) + 1;
434         nmem++;
435         total += sizeof(char *) * nmem;
436     }
437     if ((cp = malloc(total)) == NULL)
438             return(NULL);
439     newgr = (struct group *)cp;
440
441     /*
442      * Copy in group contents and make strings relative to space
443      * at the end of the buffer.  Note that gr_mem must come
444      * immediately after struct group to guarantee proper alignment.
445      */
446     (void)memcpy(newgr, gr, sizeof(struct group));
447     cp += sizeof(struct group);
448     if (gr->gr_mem) {
449         newgr->gr_mem = (char **)cp;
450         cp += sizeof(char *) * nmem;
451         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
452             len = strlen(gr->gr_mem[nmem]) + 1;
453             memcpy(cp, gr->gr_mem[nmem], len);
454             newgr->gr_mem[nmem] = cp;
455             cp += len;
456         }
457         newgr->gr_mem[nmem] = NULL;
458     }
459     FIELD_COPY(gr, newgr, gr_passwd, psize);
460     FIELD_COPY(gr, newgr, gr_name, nsize);
461
462     return(newgr);
463 }
464
465 /*
466  * Get a group entry by gid and allocate space for it.
467  */
468 struct group *
469 sudo_getgrgid(gid)
470     gid_t gid;
471 {
472     struct group key, *gr;
473     struct rbnode *node;
474
475     key.gr_gid = gid;
476     if ((node = rbfind(grcache_bygid, &key)) != NULL) {
477         gr = (struct group *) node->data;
478         return(gr->gr_name != NULL ? gr : NULL);
479     }
480     /*
481      * Cache group db entry if it exists or a negative response if not.
482      */
483     if ((gr = getgrgid(gid)) != NULL) {
484         gr = sudo_grdup(gr);
485         if (rbinsert(grcache_bygid, (void *) gr) != NULL)
486             errorx(1, "unable to cache gid %lu (%s), already exists",
487                 gid, gr->gr_name);
488         return(gr);
489     } else {
490         gr = emalloc(sizeof(*gr));
491         zero_bytes(gr, sizeof(*gr));
492         gr->gr_gid = gid;
493         if (rbinsert(grcache_bygid, (void *) gr) != NULL)
494             errorx(1, "unable to cache gid %lu, already exists, gid");
495         return(NULL);
496     }
497 }
498
499 /*
500  * Get a group entry by name and allocate space for it.
501  */
502 struct group *
503 sudo_getgrnam(name)
504     const char *name;
505 {
506     struct group key, *gr;
507     struct rbnode *node;
508     size_t len;
509     char *cp;
510
511     key.gr_name = (char *) name;
512     if ((node = rbfind(grcache_byname, &key)) != NULL) {
513         gr = (struct group *) node->data;
514         return(gr->gr_gid != (gid_t) -1 ? gr : NULL);
515     }
516     /*
517      * Cache group db entry if it exists or a negative response if not.
518      */
519     if ((gr = getgrnam(name)) != NULL) {
520         gr = sudo_grdup(gr);
521         if (rbinsert(grcache_byname, (void *) gr) != NULL)
522             errorx(1, "unable to cache group %s, already exists", name);
523         return(gr);
524     } else {
525         len = strlen(name) + 1;
526         cp = emalloc(sizeof(*gr) + len);
527         zero_bytes(cp, sizeof(*gr));
528         gr = (struct group *) cp;
529         cp += sizeof(*gr);
530         memcpy(cp, name, len);
531         gr->gr_name = cp;
532         gr->gr_gid = (gid_t) -1;
533         if (rbinsert(grcache_byname, (void *) gr) != NULL)
534             errorx(1, "unable to cache group %s, already exists", name);
535         return(NULL);
536     }
537 }
538
539 void
540 sudo_setgrent()
541 {
542     setgrent();
543     if (grcache_bygid == NULL)
544         grcache_bygid = rbcreate(cmp_grgid);
545     if (grcache_byname == NULL)
546         grcache_byname = rbcreate(cmp_grnam);
547 }
548
549 #ifdef PURIFY
550 void
551 sudo_freegrcache()
552 {
553     if (grcache_bygid != NULL) {
554         rbdestroy(grcache_bygid, free);
555         grcache_bygid = NULL;
556     }
557     if (grcache_byname != NULL) {
558         rbdestroy(grcache_byname, NULL);
559         grcache_byname = NULL;
560     }
561 }
562 #endif /* PURIFY */
563
564 void
565 sudo_endgrent()
566 {
567     endgrent();
568 #ifdef PURIFY
569     sudo_freegrcache();
570 #endif
571 }