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