~ubuntu-branches/ubuntu/quantal/sudo/quantal-security

« back to all changes in this revision

Viewing changes to plugins/sudoers/pwutil.c

  • Committer: Package Import Robot
  • Author(s): Marc Deslauriers
  • Date: 2011-11-20 12:07:45 UTC
  • mfrom: (1.3.17 sid)
  • Revision ID: package-import@ubuntu.com-20111120120745-o3qpklobmygytndc
Tags: 1.8.3p1-1ubuntu1
* Merge from debian/testing, remaining changes:
  - debian/patches/keep_home_by_default.patch:
    + Set HOME in initial_keepenv_table. (rebased for 1.8.3p1)
  - debian/patches/enable_badpass.patch: turn on "mail_badpass" by default:
    + attempting sudo without knowing a login password is as bad as not
      being listed in the sudoers file, especially if getting the password
      wrong means doing the access-check-email-notification never happens
      (rebased for 1.8.3p1)
  - debian/rules:
    + compile with --without-lecture --with-tty-tickets (Ubuntu specific)
    + install man/man8/sudo_root.8 (Ubuntu specific)
    + install apport hooks
    + The ubuntu-sudo-as-admin-successful.patch was taken upstream by
      Debian however it requires a --enable-admin-flag configure flag to
      actually enable it.
  - debian/sudoers: 
    + grant admin group sudo access
  - debian/sudo-ldap.dirs, debian/sudo.dirs: 
    + add usr/share/apport/package-hooks
  - debian/sudo.preinst:
    + avoid conffile prompt by checking for known default /etc/sudoers
      and if found installing the correct default /etc/sudoers file

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}