Coverage Report

Created: 2025-07-11 06:57

/src/sudo/plugins/sudoers/pwutil.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * SPDX-License-Identifier: ISC
3
 *
4
 * Copyright (c) 1996, 1998-2005, 2007-2018
5
 *  Todd C. Miller <Todd.Miller@sudo.ws>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 *
19
 * Sponsored in part by the Defense Advanced Research Projects
20
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22
 */
23
24
/*
25
 * This is an open source non-commercial project. Dear PVS-Studio, please check it.
26
 * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
27
 */
28
29
#include <config.h>
30
31
#include <stdarg.h>
32
#include <stddef.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
#ifdef HAVE_STRINGS_H
37
# include <strings.h>   /* strcasecmp */
38
#endif
39
#ifdef HAVE_SETAUTHDB
40
# include <usersec.h>
41
#endif /* HAVE_SETAUTHDB */
42
#include <errno.h>
43
#include <pwd.h>
44
#include <grp.h>
45
46
#include <sudoers.h>
47
#include <redblack.h>
48
#include <pwutil.h>
49
50
/*
51
 * The passwd and group caches.
52
 */
53
static struct rbtree *pwcache_byuid, *pwcache_byname;
54
static struct rbtree *grcache_bygid, *grcache_byname;
55
static struct rbtree *gidlist_cache, *grlist_cache;
56
57
static int  cmp_pwuid(const void *, const void *);
58
static int  cmp_pwnam(const void *, const void *);
59
static int  cmp_grgid(const void *, const void *);
60
61
static int max_groups;
62
63
/*
64
 * Default functions for building cache items.
65
 */
66
static sudo_make_pwitem_t make_pwitem = sudo_make_pwitem;
67
static sudo_make_gritem_t make_gritem = sudo_make_gritem;
68
static sudo_make_gidlist_item_t make_gidlist_item = sudo_make_gidlist_item;
69
static sudo_make_grlist_item_t make_grlist_item = sudo_make_grlist_item;
70
static sudo_valid_shell_t valid_shell = sudo_valid_shell;
71
72
551
#define cmp_grnam cmp_pwnam
73
74
/*
75
 * AIX has the concept of authentication registries (files, NIS, LDAP, etc).
76
 * This allows you to have separate ID <-> name mappings based on which
77
 * authentication registries the user was looked up in.
78
 * We store the registry as part of the key and use it when matching.
79
 */
80
#ifdef HAVE_SETAUTHDB
81
# define getauthregistry(u, r)  aix_getauthregistry((u), (r))
82
#else
83
126k
# define getauthregistry(u, r)  ((r)[0] = '\0')
84
#endif
85
86
/*
87
 * Change the default pwutil backend functions.
88
 * The default functions query the password and group databases.
89
 */
90
void
91
sudo_pwutil_set_backend(sudo_make_pwitem_t pwitem, sudo_make_gritem_t gritem,
92
    sudo_make_gidlist_item_t gidlist_item, sudo_make_grlist_item_t grlist_item,
93
    sudo_valid_shell_t check_shell)
94
0
{
95
0
    debug_decl(sudo_pwutil_set_backend, SUDOERS_DEBUG_NSS);
96
97
0
    if (pwitem != NULL)
98
0
  make_pwitem = pwitem;
99
0
    if (gritem != NULL)
100
0
  make_gritem = gritem;
101
0
    if (gidlist_item != NULL)
102
0
  make_gidlist_item = gidlist_item;
103
0
    if (grlist_item != NULL)
104
0
  make_grlist_item = grlist_item;
105
0
    if (check_shell != NULL)
106
0
  valid_shell = check_shell;
107
108
0
    debug_return;
109
0
}
110
111
/* Get the max number of user groups if set, or 0 if not set. */
112
int
113
sudo_pwutil_get_max_groups(void)
114
27.3k
{
115
27.3k
    return max_groups;
116
27.3k
}
117
118
/* Set the max number of user groups (negative values ignored). */
119
void
120
sudo_pwutil_set_max_groups(int n)
121
247
{
122
247
    max_groups = n > 0 ? n : 0;
123
247
}
124
125
/*
126
 * Compare by user-ID.
127
 * v1 is the key to find or data to insert, v2 is in-tree data.
128
 */
129
static int
130
cmp_pwuid(const void *v1, const void *v2)
131
70
{
132
70
    const struct cache_item *ci1 = (const struct cache_item *) v1;
133
70
    const struct cache_item *ci2 = (const struct cache_item *) v2;
134
70
    if (ci1->k.uid == ci2->k.uid)
135
70
  return strcmp(ci1->registry, ci2->registry);
136
0
    if (ci1->k.uid < ci2->k.uid)
137
0
  return -1;
138
0
    return 1;
139
0
}
140
141
/*
142
 * Compare by user/group name.
143
 * v1 is the key to find or data to insert, v2 is in-tree data.
144
 */
145
static int
146
cmp_pwnam(const void *v1, const void *v2)
147
137k
{
148
137k
    const struct cache_item *ci1 = (const struct cache_item *) v1;
149
137k
    const struct cache_item *ci2 = (const struct cache_item *) v2;
150
137k
    int ret = strcmp(ci1->k.name, ci2->k.name);
151
137k
    if (ret == 0)
152
47.8k
  ret = strcmp(ci1->registry, ci2->registry);
153
137k
    return ret;
154
137k
}
155
156
/*
157
 * Compare by user name, taking into account the source type.
158
 * Need to differentiate between group-IDs received from the front-end
159
 * (via getgroups()) and groups IDs queried from the group database.
160
 * v1 is the key to find or data to insert, v2 is in-tree data.
161
 */
162
static int
163
cmp_gidlist(const void *v1, const void *v2)
164
33.5k
{
165
33.5k
    const struct cache_item *ci1 = (const struct cache_item *) v1;
166
33.5k
    const struct cache_item *ci2 = (const struct cache_item *) v2;
167
33.5k
    int ret = strcmp(ci1->k.name, ci2->k.name);
168
33.5k
    if (ret == 0) {
169
8.58k
  if (ci1->type == ENTRY_TYPE_ANY || ci1->type == ci2->type)
170
8.42k
      return strcmp(ci1->registry, ci2->registry);
171
164
  if (ci1->type < ci2->type)
172
164
      return -1;
173
0
  return 1;
174
164
    }
175
24.9k
    return ret;
176
33.5k
}
177
178
void
179
sudo_pw_addref(struct passwd *pw)
180
0
{
181
0
    debug_decl(sudo_pw_addref, SUDOERS_DEBUG_NSS);
182
0
    ptr_to_item(pw)->refcnt++;
183
0
    debug_return;
184
0
}
185
186
static void
187
sudo_pw_delref_item(void *v)
188
127k
{
189
127k
    struct cache_item *item = v;
190
127k
    debug_decl(sudo_pw_delref_item, SUDOERS_DEBUG_NSS);
191
192
127k
    if (--item->refcnt == 0)
193
40.4k
  free(item);
194
195
127k
    debug_return;
196
127k
}
197
198
void
199
sudo_pw_delref(struct passwd *pw)
200
87.2k
{
201
87.2k
    debug_decl(sudo_pw_delref, SUDOERS_DEBUG_NSS);
202
87.2k
    sudo_pw_delref_item(ptr_to_item(pw));
203
87.2k
    debug_return;
204
87.2k
}
205
206
/*
207
 * Get a password entry by uid and allocate space for it.
208
 */
209
struct passwd *
210
sudo_getpwuid(uid_t uid)
211
320
{
212
320
    struct cache_item key, *item;
213
320
    struct rbnode *node;
214
320
    debug_decl(sudo_getpwuid, SUDOERS_DEBUG_NSS);
215
216
320
    if (pwcache_byuid == NULL) {
217
320
  pwcache_byuid = rbcreate(cmp_pwuid);
218
320
  if (pwcache_byuid == NULL) {
219
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
220
0
      debug_return_ptr(NULL);
221
0
  }
222
320
    }
223
224
320
    key.k.uid = uid;
225
320
    getauthregistry(IDtouser(uid), key.registry);
226
320
    if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
227
0
  item = node->data;
228
0
  goto done;
229
0
    }
230
    /*
231
     * Cache passwd db entry if it exists or a negative response if not.
232
     */
233
#ifdef HAVE_SETAUTHDB
234
    aix_setauthdb(IDtouser(uid), key.registry);
235
#endif
236
320
    item = make_pwitem(uid, NULL);
237
#ifdef HAVE_SETAUTHDB
238
    aix_restoreauthdb();
239
#endif
240
320
    if (item == NULL) {
241
70
  if (errno != ENOENT || (item = calloc(1, sizeof(*item))) == NULL) {
242
0
      sudo_warn(U_("unable to cache uid %u"), (unsigned int) uid);
243
      /* cppcheck-suppress memleak */
244
0
      debug_return_ptr(NULL);
245
0
  }
246
70
  item->refcnt = 1;
247
70
  item->k.uid = uid;
248
  /* item->d.pw = NULL; */
249
70
    }
250
320
    strlcpy(item->registry, key.registry, sizeof(item->registry));
251
320
    switch (rbinsert(pwcache_byuid, item, NULL)) {
252
0
    case 1:
253
  /* should not happen */
254
0
  sudo_warnx(U_("unable to cache uid %u, already exists"),
255
0
      (unsigned int) uid);
256
0
  item->refcnt = 0;
257
0
  break;
258
0
    case -1:
259
  /* can't cache item, just return it */
260
0
  sudo_warn(U_("unable to cache uid %u"), (unsigned int) uid);
261
0
  item->refcnt = 0;
262
0
  break;
263
320
    }
264
320
done:
265
320
    if (item->refcnt != 0) {
266
320
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
267
320
      "%s: uid %u [%s] -> user %s [%s] (%s)", __func__,
268
320
      (unsigned int)uid, key.registry,
269
320
      item->d.pw ? item->d.pw->pw_name : "unknown",
270
320
      item->registry, node ? "cache hit" : "cached");
271
320
    }
272
320
    if (item->d.pw != NULL)
273
250
  item->refcnt++;
274
320
    debug_return_ptr(item->d.pw);
275
320
}
276
277
/*
278
 * Get a password entry by name and allocate space for it.
279
 */
280
struct passwd *
281
sudo_getpwnam(const char *name)
282
87.0k
{
283
87.0k
    struct cache_item key, *item;
284
87.0k
    struct rbnode *node;
285
87.0k
    debug_decl(sudo_getpwnam, SUDOERS_DEBUG_NSS);
286
287
87.0k
    if (pwcache_byname == NULL) {
288
21.8k
  pwcache_byname = rbcreate(cmp_pwnam);
289
21.8k
  if (pwcache_byname == NULL) {
290
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
291
0
      debug_return_ptr(NULL);
292
0
  }
293
21.8k
    }
294
295
87.0k
    key.k.name = (char *) name;
296
87.0k
    getauthregistry((char *) name, key.registry);
297
87.0k
    if ((node = rbfind(pwcache_byname, &key)) != NULL) {
298
47.5k
  item = node->data;
299
47.5k
  goto done;
300
47.5k
    }
301
    /*
302
     * Cache passwd db entry if it exists or a negative response if not.
303
     */
304
#ifdef HAVE_SETAUTHDB
305
    aix_setauthdb((char *) name, key.registry);
306
#endif
307
39.4k
    item = make_pwitem((uid_t)-1, name);
308
#ifdef HAVE_SETAUTHDB
309
    aix_restoreauthdb();
310
#endif
311
39.4k
    if (item == NULL) {
312
310
  const size_t len = strlen(name) + 1;
313
310
  if (errno != ENOENT || (item = calloc(1, sizeof(*item) + len)) == NULL) {
314
0
      sudo_warn(U_("unable to cache user %s"), name);
315
      /* cppcheck-suppress memleak */
316
0
      debug_return_ptr(NULL);
317
0
  }
318
310
  item->refcnt = 1;
319
310
  item->k.name = (char *) item + sizeof(*item);
320
310
  memcpy(item->k.name, name, len);
321
  /* item->d.pw = NULL; */
322
310
    }
323
39.4k
    strlcpy(item->registry, key.registry, sizeof(item->registry));
324
39.4k
    switch (rbinsert(pwcache_byname, item, NULL)) {
325
0
    case 1:
326
  /* should not happen */
327
0
  sudo_warnx(U_("unable to cache user %s, already exists"), name);
328
0
  item->refcnt = 0;
329
0
  break;
330
0
    case -1:
331
  /* can't cache item, just return it */
332
0
  sudo_warn(U_("unable to cache user %s"), name);
333
0
  item->refcnt = 0;
334
0
  break;
335
39.4k
    }
336
87.0k
done:
337
87.0k
    if (item->refcnt != 0) {
338
87.0k
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
339
87.0k
      "%s: user %s [%s] -> uid %d [%s] (%s)", __func__, name,
340
87.0k
      key.registry, item->d.pw ? (int)item->d.pw->pw_uid : -1,
341
87.0k
      item->registry, node ? "cache hit" : "cached");
342
87.0k
    }
343
87.0k
    if (item->d.pw != NULL)
344
86.6k
  item->refcnt++;
345
87.0k
    debug_return_ptr(item->d.pw);
346
87.0k
}
347
348
/*
349
 * Take a user, uid, gid, home and shell and return a faked up passwd struct.
350
 * If home or shell are NULL default values will be used.
351
 */
352
struct passwd *
353
sudo_mkpwent(const char *user, uid_t uid, gid_t gid, const char *home,
354
    const char *shell)
355
349
{
356
349
    struct cache_item_pw *pwitem;
357
349
    struct cache_item *item;
358
349
    struct passwd *pw;
359
349
    size_t len, name_len, home_len, shell_len;
360
349
    unsigned int i;
361
349
    debug_decl(sudo_mkpwent, SUDOERS_DEBUG_NSS);
362
363
349
    if (pwcache_byuid == NULL)
364
279
  pwcache_byuid = rbcreate(cmp_pwuid);
365
349
    if (pwcache_byname == NULL)
366
0
  pwcache_byname = rbcreate(cmp_pwnam);
367
349
    if (pwcache_byuid == NULL || pwcache_byname == NULL) {
368
0
  sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
369
0
  debug_return_ptr(NULL);
370
0
    }
371
372
    /* Optional arguments. */
373
349
    if (home == NULL)
374
349
  home = "/";
375
349
    if (shell == NULL)
376
349
  shell = _PATH_BSHELL;
377
378
349
    sudo_debug_printf(SUDO_DEBUG_DEBUG,
379
349
  "%s: creating and caching passwd struct for %s:%u:%u:%s:%s", __func__,
380
349
  user, (unsigned int)uid, (unsigned int)gid, home, shell);
381
382
349
    name_len = strlen(user);
383
349
    home_len = strlen(home);
384
349
    shell_len = strlen(shell);
385
349
    len = sizeof(*pwitem) + name_len + 1 /* pw_name */ +
386
349
  sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
387
349
  home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */;
388
389
1.04k
    for (i = 0; i < 2; i++) {
390
698
  struct rbtree *pwcache;
391
698
  struct rbnode *node;
392
393
698
  pwitem = calloc(1, len);
394
698
  if (pwitem == NULL) {
395
0
      sudo_warn(U_("unable to cache user %s"), user);
396
0
      debug_return_ptr(NULL);
397
0
  }
398
698
  pw = &pwitem->pw;
399
698
  pw->pw_uid = uid;
400
698
  pw->pw_gid = gid;
401
698
  pw->pw_name = (char *)(pwitem + 1);
402
698
  memcpy(pw->pw_name, user, name_len + 1);
403
698
  pw->pw_passwd = pw->pw_name + name_len + 1;
404
698
  memcpy(pw->pw_passwd, "*", 2);
405
698
  pw->pw_gecos = pw->pw_passwd + 2;
406
698
  pw->pw_gecos[0] = '\0';
407
698
  pw->pw_dir = pw->pw_gecos + 1;
408
698
  memcpy(pw->pw_dir, home, home_len + 1);
409
698
  pw->pw_shell = pw->pw_dir + home_len + 1;
410
698
  memcpy(pw->pw_shell, shell, shell_len + 1);
411
412
698
  item = &pwitem->cache;
413
698
  item->refcnt = 1;
414
698
  item->d.pw = pw;
415
698
  if (i == 0) {
416
      /* Store by uid. */
417
349
      item->k.uid = pw->pw_uid;
418
349
      pwcache = pwcache_byuid;
419
349
  } else {
420
      /* Store by name. */
421
349
      item->k.name = pw->pw_name;
422
349
      pwcache = pwcache_byname;
423
349
  }
424
698
  getauthregistry(NULL, item->registry);
425
698
  switch (rbinsert(pwcache, item, &node)) {
426
349
  case 1:
427
      /* Already exists. */
428
349
      item = node->data;
429
349
      if (item->d.pw == NULL) {
430
    /* Negative cache entry, replace with ours. */
431
349
    sudo_pw_delref_item(item);
432
349
    item = node->data = &pwitem->cache;
433
349
      } else {
434
    /* Good entry, discard our fake one. */
435
0
    free(pwitem);
436
0
      }
437
349
      break;
438
0
  case -1:
439
      /* can't cache item, just return it */
440
0
      sudo_warn(U_("unable to cache user %s"), user);
441
0
      item->refcnt = 0;
442
0
      break;
443
698
  }
444
698
    }
445
349
    item->refcnt++;
446
349
    debug_return_ptr(item->d.pw);
447
349
}
448
449
/*
450
 * Take a uid in string form "#123" and return a faked up passwd struct.
451
 */
452
struct passwd *
453
sudo_fakepwnam(const char *user, gid_t gid)
454
70
{
455
70
    const char *errstr;
456
70
    uid_t uid;
457
70
    debug_decl(sudo_fakepwnam, SUDOERS_DEBUG_NSS);
458
459
70
    uid = (uid_t) sudo_strtoid(user + 1, &errstr);
460
70
    if (errstr != NULL) {
461
0
  sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
462
0
      "uid %s %s", user, errstr);
463
0
  debug_return_ptr(NULL);
464
0
    }
465
70
    debug_return_ptr(sudo_mkpwent(user, uid, gid, NULL, NULL));
466
70
}
467
468
void
469
sudo_freepwcache(void)
470
26.8k
{
471
26.8k
    debug_decl(sudo_freepwcache, SUDOERS_DEBUG_NSS);
472
473
26.8k
    if (pwcache_byuid != NULL) {
474
599
  rbdestroy(pwcache_byuid, sudo_pw_delref_item);
475
599
  pwcache_byuid = NULL;
476
599
    }
477
26.8k
    if (pwcache_byname != NULL) {
478
21.8k
  rbdestroy(pwcache_byname, sudo_pw_delref_item);
479
21.8k
  pwcache_byname = NULL;
480
21.8k
    }
481
482
26.8k
    debug_return;
483
26.8k
}
484
485
/*
486
 * Compare by group-ID.
487
 * v1 is the key to find or data to insert, v2 is in-tree data.
488
 */
489
static int
490
cmp_grgid(const void *v1, const void *v2)
491
381
{
492
381
    const struct cache_item *ci1 = (const struct cache_item *) v1;
493
381
    const struct cache_item *ci2 = (const struct cache_item *) v2;
494
381
    if (ci1->k.gid == ci2->k.gid)
495
381
  return strcmp(ci1->registry, ci2->registry);
496
0
    if (ci1->k.gid < ci2->k.gid)
497
0
  return -1;
498
0
    return 1;
499
0
}
500
501
void
502
sudo_gr_addref(struct group *gr)
503
0
{
504
0
    debug_decl(sudo_gr_addref, SUDOERS_DEBUG_NSS);
505
0
    ptr_to_item(gr)->refcnt++;
506
0
    debug_return;
507
0
}
508
509
static void
510
sudo_gr_delref_item(void *v)
511
2.06k
{
512
2.06k
    struct cache_item *item = v;
513
2.06k
    debug_decl(sudo_gr_delref_item, SUDOERS_DEBUG_NSS);
514
515
2.06k
    if (--item->refcnt == 0)
516
1.47k
  free(item);
517
518
2.06k
    debug_return;
519
2.06k
}
520
521
void
522
sudo_gr_delref(struct group *gr)
523
592
{
524
592
    debug_decl(sudo_gr_delref, SUDOERS_DEBUG_NSS);
525
592
    sudo_gr_delref_item(ptr_to_item(gr));
526
592
    debug_return;
527
592
}
528
529
/*
530
 * Get a group entry by gid and allocate space for it.
531
 */
532
struct group *
533
sudo_getgrgid(gid_t gid)
534
542
{
535
542
    struct cache_item key, *item;
536
542
    struct rbnode *node;
537
542
    debug_decl(sudo_getgrgid, SUDOERS_DEBUG_NSS);
538
539
542
    if (grcache_bygid == NULL) {
540
542
  grcache_bygid = rbcreate(cmp_grgid);
541
542
  if (grcache_bygid == NULL) {
542
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
543
0
      debug_return_ptr(NULL);
544
0
  }
545
542
    }
546
547
542
    key.k.gid = gid;
548
542
    getauthregistry(NULL, key.registry);
549
542
    if ((node = rbfind(grcache_bygid, &key)) != NULL) {
550
0
  item = node->data;
551
0
  goto done;
552
0
    }
553
    /*
554
     * Cache group db entry if it exists or a negative response if not.
555
     */
556
542
    item = make_gritem(gid, NULL);
557
542
    if (item == NULL) {
558
381
  if (errno != ENOENT || (item = calloc(1, sizeof(*item))) == NULL) {
559
0
      sudo_warn(U_("unable to cache gid %u"), (unsigned int) gid);
560
      /* cppcheck-suppress memleak */
561
0
      debug_return_ptr(NULL);
562
0
  }
563
381
  item->refcnt = 1;
564
381
  item->k.gid = gid;
565
  /* item->d.gr = NULL; */
566
381
    }
567
542
    strlcpy(item->registry, key.registry, sizeof(item->registry));
568
542
    switch (rbinsert(grcache_bygid, item, NULL)) {
569
0
    case 1:
570
  /* should not happen */
571
0
  sudo_warnx(U_("unable to cache gid %u, already exists"),
572
0
      (unsigned int) gid);
573
0
  item->refcnt = 0;
574
0
  break;
575
0
    case -1:
576
  /* can't cache item, just return it */
577
0
  sudo_warn(U_("unable to cache gid %u"), (unsigned int) gid);
578
0
  item->refcnt = 0;
579
0
  break;
580
542
    }
581
542
done:
582
542
    if (item->refcnt != 0) {
583
542
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
584
542
      "%s: gid %u [%s] -> group %s [%s] (%s)", __func__,
585
542
      (unsigned int)gid, key.registry,
586
542
      item->d.gr ? item->d.gr->gr_name : "unknown",
587
542
      item->registry, node ? "cache hit" : "cached");
588
542
    }
589
542
    if (item->d.gr != NULL)
590
161
  item->refcnt++;
591
542
    debug_return_ptr(item->d.gr);
592
542
}
593
594
/*
595
 * Get a group entry by name and allocate space for it.
596
 */
597
struct group *
598
sudo_getgrnam(const char *name)
599
170
{
600
170
    struct cache_item key, *item;
601
170
    struct rbnode *node;
602
170
    debug_decl(sudo_getgrnam, SUDOERS_DEBUG_NSS);
603
604
170
    if (grcache_byname == NULL) {
605
170
  grcache_byname = rbcreate(cmp_grnam);
606
170
  if (grcache_byname == NULL) {
607
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
608
0
      debug_return_ptr(NULL);
609
0
  }
610
170
    }
611
612
170
    key.k.name = (char *) name;
613
170
    getauthregistry(NULL, key.registry);
614
170
    if ((node = rbfind(grcache_byname, &key)) != NULL) {
615
0
  item = node->data;
616
0
  goto done;
617
0
    }
618
    /*
619
     * Cache group db entry if it exists or a negative response if not.
620
     */
621
170
    item = make_gritem((gid_t)-1, name);
622
170
    if (item == NULL) {
623
120
  const size_t len = strlen(name) + 1;
624
120
  if (errno != ENOENT || (item = calloc(1, sizeof(*item) + len)) == NULL) {
625
0
      sudo_warn(U_("unable to cache group %s"), name);
626
      /* cppcheck-suppress memleak */
627
0
      debug_return_ptr(NULL);
628
0
  }
629
120
  item->refcnt = 1;
630
120
  item->k.name = (char *) item + sizeof(*item);
631
120
  memcpy(item->k.name, name, len);
632
  /* item->d.gr = NULL; */
633
120
    }
634
170
    strlcpy(item->registry, key.registry, sizeof(item->registry));
635
170
    switch (rbinsert(grcache_byname, item, NULL)) {
636
0
    case 1:
637
  /* should not happen */
638
0
  sudo_warnx(U_("unable to cache group %s, already exists"), name);
639
0
  item->refcnt = 0;
640
0
  break;
641
0
    case -1:
642
  /* can't cache item, just return it */
643
0
  sudo_warn(U_("unable to cache group %s"), name);
644
0
  item->refcnt = 0;
645
0
  break;
646
170
    }
647
170
done:
648
170
    if (item->refcnt != 0) {
649
170
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
650
170
      "%s: group %s [%s] -> gid %d [%s] (%s)", __func__, name,
651
170
      key.registry, item->d.gr ? (int)item->d.gr->gr_gid : -1,
652
170
      item->registry, node ? "cache hit" : "cached");
653
170
    }
654
170
    if (item->d.gr != NULL)
655
50
  item->refcnt++;
656
170
    debug_return_ptr(item->d.gr);
657
170
}
658
659
/*
660
 * Take a group name, ID, members and return a faked up group struct.
661
 */
662
struct group *
663
sudo_mkgrent(const char *group, gid_t gid, ...)
664
381
{
665
381
    struct cache_item_gr *gritem;
666
381
    struct cache_item *item;
667
381
    struct group *gr;
668
381
    size_t nmem, nsize, total;
669
381
    char *cp, *mem;
670
381
    va_list ap;
671
381
    unsigned int i;
672
381
    debug_decl(sudo_mkgrent, SUDOERS_DEBUG_NSS);
673
674
381
    if (grcache_bygid == NULL)
675
0
  grcache_bygid = rbcreate(cmp_grgid);
676
381
    if (grcache_byname == NULL)
677
381
  grcache_byname = rbcreate(cmp_grnam);
678
381
    if (grcache_bygid == NULL || grcache_byname == NULL) {
679
0
  sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
680
0
  debug_return_ptr(NULL);
681
0
    }
682
683
    /* Allocate in one big chunk for easy freeing. */
684
381
    nsize = strlen(group) + 1;
685
381
    total = sizeof(*gritem) + nsize;
686
381
    va_start(ap, gid);
687
381
    for (nmem = 1; (mem = va_arg(ap, char *)) != NULL; nmem++) {
688
0
  total += strlen(mem) + 1;
689
0
    }
690
381
    va_end(ap);
691
381
    total += sizeof(char *) * nmem;
692
693
1.14k
    for (i = 0; i < 2; i++) {
694
762
  struct rbtree *grcache;
695
762
  struct rbnode *node;
696
697
  /*
698
   * Fill in group contents and make strings relative to space
699
   * at the end of the buffer.  Note that gr_mem must come
700
   * immediately after struct group to guarantee proper alignment.
701
   */
702
762
  gritem = calloc(1, total);
703
762
  if (gritem == NULL) {
704
0
      sudo_warn(U_("unable to cache group %s"), group);
705
0
      debug_return_ptr(NULL);
706
0
  }
707
762
  gr = &gritem->gr;
708
762
  gr->gr_gid = gid;
709
762
  gr->gr_passwd = (char *)"*";
710
762
  cp = (char *)(gritem + 1);
711
762
  gr->gr_mem = (char **)cp;
712
762
  cp += sizeof(char *) * nmem;
713
762
  va_start(ap, gid);
714
762
  for (nmem = 0; (mem = va_arg(ap, char *)) != NULL; nmem++) {
715
0
      size_t len = strlen(mem) + 1;
716
0
      memcpy(cp, mem, len);
717
0
      gr->gr_mem[nmem] = cp;
718
0
      cp += len;
719
0
  }
720
762
  va_end(ap);
721
762
  gr->gr_mem[nmem] = NULL;
722
762
  gr->gr_name = cp;
723
762
  memcpy(gr->gr_name, group, nsize);
724
725
762
  item = &gritem->cache;
726
762
  item->refcnt = 1;
727
762
  item->d.gr = gr;
728
762
  if (i == 0) {
729
      /* Store by gid if it doesn't already exist. */
730
381
      item->k.gid = gr->gr_gid;
731
381
      grcache = grcache_bygid;
732
381
  } else {
733
      /* Store by name, overwriting cached version. */
734
381
      gritem->cache.k.name = gr->gr_name;
735
381
      grcache = grcache_byname;
736
381
  }
737
762
  getauthregistry(NULL, item->registry);
738
762
  switch (rbinsert(grcache, item, &node)) {
739
381
  case 1:
740
      /* Already exists. */
741
381
      item = node->data;
742
381
      if (item->d.gr == NULL) {
743
    /* Negative cache entry, replace with ours. */
744
381
    sudo_gr_delref_item(item);
745
381
    item = node->data = &gritem->cache;
746
381
      } else {
747
    /* Good entry, discard our fake one. */
748
0
    free(gritem);
749
0
      }
750
381
      break;
751
0
  case -1:
752
      /* can't cache item, just return it */
753
0
      sudo_warn(U_("unable to cache group %s"), group);
754
0
      item->refcnt = 0;
755
0
      break;
756
762
  }
757
762
    }
758
381
    if (item->d.gr != NULL)
759
381
  item->refcnt++;
760
381
    debug_return_ptr(item->d.gr);
761
381
}
762
763
/*
764
 * Take a gid in string form "#123" and return a faked up group struct.
765
 */
766
struct group *
767
sudo_fakegrnam(const char *group)
768
381
{
769
381
    const char *errstr;
770
381
    gid_t gid;
771
381
    debug_decl(sudo_fakegrnam, SUDOERS_DEBUG_NSS);
772
773
381
    gid = (gid_t) sudo_strtoid(group + 1, &errstr);
774
381
    if (errstr != NULL) {
775
0
  sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
776
0
      "gid %s %s", group, errstr);
777
0
  debug_return_ptr(NULL);
778
0
    }
779
780
381
    debug_return_ptr(sudo_mkgrent(group, gid, (char *)NULL));
781
381
}
782
783
void
784
sudo_gidlist_addref(struct gid_list *gidlist)
785
0
{
786
0
    debug_decl(sudo_gidlist_addref, SUDOERS_DEBUG_NSS);
787
0
    ptr_to_item(gidlist)->refcnt++;
788
0
    debug_return;
789
0
}
790
791
static void
792
sudo_gidlist_delref_item(void *v)
793
64.2k
{
794
64.2k
    struct cache_item *item = v;
795
64.2k
    debug_decl(sudo_gidlist_delref_item, SUDOERS_DEBUG_NSS);
796
797
64.2k
    if (--item->refcnt == 0)
798
28.4k
  free(item);
799
800
64.2k
    debug_return;
801
64.2k
}
802
803
void
804
sudo_gidlist_delref(struct gid_list *gidlist)
805
35.7k
{
806
35.7k
    debug_decl(sudo_gidlist_delref, SUDOERS_DEBUG_NSS);
807
35.7k
    sudo_gidlist_delref_item(ptr_to_item(gidlist));
808
35.7k
    debug_return;
809
35.7k
}
810
811
void
812
sudo_grlist_addref(struct group_list *grlist)
813
0
{
814
0
    debug_decl(sudo_grlist_addref, SUDOERS_DEBUG_NSS);
815
0
    ptr_to_item(grlist)->refcnt++;
816
0
    debug_return;
817
0
}
818
819
static void
820
sudo_grlist_delref_item(void *v)
821
0
{
822
0
    struct cache_item *item = v;
823
0
    debug_decl(sudo_grlist_delref_item, SUDOERS_DEBUG_NSS);
824
825
0
    if (--item->refcnt == 0)
826
0
  free(item);
827
828
0
    debug_return;
829
0
}
830
831
void
832
sudo_grlist_delref(struct group_list *grlist)
833
0
{
834
0
    debug_decl(sudo_grlist_delref, SUDOERS_DEBUG_NSS);
835
0
    sudo_grlist_delref_item(ptr_to_item(grlist));
836
0
    debug_return;
837
0
}
838
839
void
840
sudo_freegrcache(void)
841
26.8k
{
842
26.8k
    debug_decl(sudo_freegrcache, SUDOERS_DEBUG_NSS);
843
844
26.8k
    if (grcache_bygid != NULL) {
845
542
  rbdestroy(grcache_bygid, sudo_gr_delref_item);
846
542
  grcache_bygid = NULL;
847
542
    }
848
26.8k
    if (grcache_byname != NULL) {
849
551
  rbdestroy(grcache_byname, sudo_gr_delref_item);
850
551
  grcache_byname = NULL;
851
551
    }
852
26.8k
    if (grlist_cache != NULL) {
853
0
  rbdestroy(grlist_cache, sudo_grlist_delref_item);
854
0
  grlist_cache = NULL;
855
0
    }
856
26.8k
    if (gidlist_cache != NULL) {
857
21.7k
  rbdestroy(gidlist_cache, sudo_gidlist_delref_item);
858
21.7k
  gidlist_cache = NULL;
859
21.7k
    }
860
861
26.8k
    debug_return;
862
26.8k
}
863
864
struct group_list *
865
sudo_get_grlist(const struct passwd *pw)
866
0
{
867
0
    struct cache_item key, *item;
868
0
    struct rbnode *node;
869
0
    debug_decl(sudo_get_grlist, SUDOERS_DEBUG_NSS);
870
871
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: looking up group names for %s",
872
0
  __func__, pw->pw_name);
873
874
0
    if (grlist_cache == NULL) {
875
0
  grlist_cache = rbcreate(cmp_pwnam);
876
0
  if (grlist_cache == NULL) {
877
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
878
0
      debug_return_ptr(NULL);
879
0
  }
880
0
    }
881
882
0
    key.k.name = pw->pw_name;
883
0
    getauthregistry(pw->pw_name, key.registry);
884
0
    if ((node = rbfind(grlist_cache, &key)) != NULL) {
885
0
  item = node->data;
886
0
  goto done;
887
0
    }
888
    /*
889
     * Cache group db entry if it exists or a negative response if not.
890
     */
891
0
    item = make_grlist_item(pw, NULL);
892
0
    if (item == NULL) {
893
  /* Out of memory? */
894
0
  debug_return_ptr(NULL);
895
0
    }
896
0
    strlcpy(item->registry, key.registry, sizeof(item->registry));
897
0
    switch (rbinsert(grlist_cache, item, NULL)) {
898
0
    case 1:
899
  /* should not happen */
900
0
  sudo_warnx(U_("unable to cache group list for %s, already exists"),
901
0
      pw->pw_name);
902
0
  item->refcnt = 0;
903
0
  break;
904
0
    case -1:
905
  /* can't cache item, just return it */
906
0
  sudo_warn(U_("unable to cache group list for %s"), pw->pw_name);
907
0
  item->refcnt = 0;
908
0
  break;
909
0
    }
910
0
    if (item->d.grlist != NULL) {
911
0
  int i;
912
0
  for (i = 0; i < item->d.grlist->ngroups; i++) {
913
0
      sudo_debug_printf(SUDO_DEBUG_DEBUG,
914
0
    "%s: user %s is a member of group %s", __func__,
915
0
    pw->pw_name, item->d.grlist->groups[i]);
916
0
  }
917
0
    }
918
0
done:
919
0
    if (item->d.grlist != NULL)
920
0
  item->refcnt++;
921
0
    debug_return_ptr(item->d.grlist);
922
0
}
923
924
static void
925
sudo_debug_group_list(const char *user, char * const *groups,
926
    unsigned int level)
927
1.13k
{
928
1.13k
    size_t i, len = 0;
929
1.13k
    debug_decl(sudo_debug_group_list, SUDOERS_DEBUG_NSS);
930
931
1.13k
    if (groups == NULL || !sudo_debug_needed(level))
932
1.13k
  debug_return;
933
934
0
    for (i = 0; groups[i] != NULL; i++) {
935
0
  len += strlen(groups[i]) + 1;
936
0
    }
937
0
    if (len != 0) {
938
0
  char *groupstr = malloc(len);
939
0
  if (groupstr != NULL) {
940
0
      char *cp = groupstr;
941
0
      for (i = 0; groups[i] != NULL; i++) {
942
0
    size_t n = (size_t)snprintf(cp, len, "%s%s", i ? "," : "",
943
0
        groups[i]);
944
0
    if (n >= len)
945
0
        break;
946
0
    cp += n;
947
0
    len -= n;
948
0
      }
949
0
      sudo_debug_printf(level, "%s: %s", user, groupstr);
950
0
      free(groupstr);
951
0
  }
952
0
    }
953
0
    debug_return;
954
0
}
955
956
int
957
sudo_set_grlist(struct passwd *pw, char * const *groups)
958
0
{
959
0
    struct cache_item key, *item;
960
0
    debug_decl(sudo_set_grlist, SUDOERS_DEBUG_NSS);
961
962
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: setting group names for %s",
963
0
  __func__, pw->pw_name);
964
965
0
    sudo_debug_group_list(pw->pw_name, groups, SUDO_DEBUG_DEBUG);
966
967
0
    if (grlist_cache == NULL) {
968
0
  grlist_cache = rbcreate(cmp_pwnam);
969
0
  if (grlist_cache == NULL) {
970
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
971
0
      debug_return_int(-1);
972
0
  }
973
0
    }
974
975
    /*
976
     * Cache group db entry if it doesn't already exist
977
     */
978
0
    key.k.name = pw->pw_name;
979
0
    getauthregistry(pw->pw_name, key.registry);
980
0
    if (rbfind(grlist_cache, &key) == NULL) {
981
0
  if ((item = make_grlist_item(pw, groups)) == NULL) {
982
0
      sudo_warnx(U_("unable to parse groups for %s"), pw->pw_name);
983
0
      debug_return_int(-1);
984
0
  }
985
0
  strlcpy(item->registry, key.registry, sizeof(item->registry));
986
0
  switch (rbinsert(grlist_cache, item, NULL)) {
987
0
  case 1:
988
0
      sudo_warnx(U_("unable to cache group list for %s, already exists"),
989
0
    pw->pw_name);
990
0
      sudo_grlist_delref_item(item);
991
0
      break;
992
0
  case -1:
993
0
      sudo_warn(U_("unable to cache group list for %s"), pw->pw_name);
994
0
      sudo_grlist_delref_item(item);
995
0
      debug_return_int(-1);
996
0
  }
997
0
    } else {
998
0
  sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
999
0
      "groups for user %s are already cached", pw->pw_name);
1000
0
    }
1001
1002
0
    debug_return_int(0);
1003
0
}
1004
1005
struct gid_list *
1006
sudo_get_gidlist(const struct passwd *pw, unsigned int type)
1007
35.7k
{
1008
35.7k
    struct cache_item key, *item;
1009
35.7k
    struct rbnode *node;
1010
35.7k
    debug_decl(sudo_get_gidlist, SUDOERS_DEBUG_NSS);
1011
1012
35.7k
    sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: looking up group-IDs for %s",
1013
35.7k
  __func__, pw->pw_name);
1014
1015
35.7k
    if (gidlist_cache == NULL) {
1016
20.6k
  gidlist_cache = rbcreate(cmp_gidlist);
1017
20.6k
  if (gidlist_cache == NULL) {
1018
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1019
0
      debug_return_ptr(NULL);
1020
0
  }
1021
20.6k
    }
1022
1023
35.7k
    key.k.name = pw->pw_name;
1024
35.7k
    key.type = type;
1025
35.7k
    getauthregistry(pw->pw_name, key.registry);
1026
35.7k
    if ((node = rbfind(gidlist_cache, &key)) != NULL) {
1027
8.42k
  item = node->data;
1028
8.42k
  goto done;
1029
8.42k
    }
1030
    /*
1031
     * Cache group db entry if it exists or a negative response if not.
1032
     */
1033
27.3k
    item = make_gidlist_item(pw, -1, NULL, NULL, type);
1034
27.3k
    if (item == NULL) {
1035
  /* Out of memory? */
1036
0
  debug_return_ptr(NULL);
1037
0
    }
1038
27.3k
    strlcpy(item->registry, key.registry, sizeof(item->registry));
1039
27.3k
    switch (rbinsert(gidlist_cache, item, NULL)) {
1040
0
    case 1:
1041
  /* should not happen */
1042
0
  sudo_warnx(U_("unable to cache group list for %s, already exists"),
1043
0
      pw->pw_name);
1044
0
  item->refcnt = 0;
1045
0
  break;
1046
0
    case -1:
1047
  /* can't cache item, just return it */
1048
0
  sudo_warn(U_("unable to cache group list for %s"), pw->pw_name);
1049
0
  item->refcnt = 0;
1050
0
  break;
1051
27.3k
    }
1052
27.3k
    if (item->d.gidlist != NULL) {
1053
27.3k
  int i;
1054
54.6k
  for (i = 0; i < item->d.gidlist->ngids; i++) {
1055
27.3k
      sudo_debug_printf(SUDO_DEBUG_DEBUG,
1056
27.3k
    "%s: user %s has supplementary gid %u", __func__,
1057
27.3k
    pw->pw_name, (unsigned int)item->d.gidlist->gids[i]);
1058
27.3k
  }
1059
27.3k
    }
1060
35.7k
done:
1061
35.7k
    if (item->d.gidlist != NULL)
1062
35.7k
  item->refcnt++;
1063
35.7k
    debug_return_ptr(item->d.gidlist);
1064
35.7k
}
1065
1066
int
1067
sudo_set_gidlist(struct passwd *pw, int ngids, GETGROUPS_T *gids,
1068
    char * const *gidstrs, unsigned int type)
1069
1.13k
{
1070
1.13k
    struct cache_item key, *item;
1071
1.13k
    debug_decl(sudo_set_gidlist, SUDOERS_DEBUG_NSS);
1072
1073
1.13k
    sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: setting group-IDs for %s",
1074
1.13k
  __func__, pw->pw_name);
1075
1076
    /* XXX - ngids/gids too */
1077
1.13k
    sudo_debug_group_list(pw->pw_name, gidstrs, SUDO_DEBUG_DEBUG);
1078
1079
1.13k
    if (gidlist_cache == NULL) {
1080
1.13k
  gidlist_cache = rbcreate(cmp_gidlist);
1081
1.13k
  if (gidlist_cache == NULL) {
1082
0
      sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1083
0
      debug_return_int(-1);
1084
0
  }
1085
1.13k
    }
1086
1087
    /*
1088
     * Cache group db entry if it doesn't already exist
1089
     */
1090
1.13k
    key.k.name = pw->pw_name;
1091
1.13k
    key.type = type;
1092
1.13k
    getauthregistry(pw->pw_name, key.registry);
1093
1.13k
    if (rbfind(gidlist_cache, &key) == NULL) {
1094
1.13k
  if ((item = make_gidlist_item(pw, ngids, gids, gidstrs, type)) == NULL) {
1095
0
      sudo_warnx(U_("unable to parse gids for %s"), pw->pw_name);
1096
0
      debug_return_int(-1);
1097
0
  }
1098
1.13k
  strlcpy(item->registry, key.registry, sizeof(item->registry));
1099
1.13k
  switch (rbinsert(gidlist_cache, item, NULL)) {
1100
0
  case 1:
1101
0
      sudo_warnx(U_("unable to cache group list for %s, already exists"),
1102
0
    pw->pw_name);
1103
0
      sudo_gidlist_delref_item(item);
1104
0
      break;
1105
0
  case -1:
1106
0
      sudo_warn(U_("unable to cache group list for %s"), pw->pw_name);
1107
0
      sudo_gidlist_delref_item(item);
1108
0
      debug_return_int(-1);
1109
1.13k
  }
1110
1.13k
    } else {
1111
0
  sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO,
1112
0
      "gids for user %s are already cached", pw->pw_name);
1113
0
    }
1114
1115
1.13k
    debug_return_int(0);
1116
1.13k
}
1117
1118
bool
1119
user_in_group(const struct passwd *pw, const char *group)
1120
0
{
1121
0
    struct group_list *grlist = NULL;
1122
0
    struct gid_list *gidlist = NULL;
1123
0
    struct group *grp = NULL;
1124
0
    bool matched = false;
1125
0
    int i;
1126
0
    debug_decl(user_in_group, SUDOERS_DEBUG_NSS);
1127
1128
    /*
1129
     * If it could be a sudo-style group-ID check gids first.
1130
     */
1131
0
    if (group[0] == '#') {
1132
0
  const char *errstr;
1133
0
  gid_t gid = (gid_t) sudo_strtoid(group + 1, &errstr);
1134
0
  if (errstr != NULL) {
1135
0
      sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
1136
0
    "gid %s %s", group, errstr);
1137
0
  } else {
1138
0
      if (gid == pw->pw_gid) {
1139
0
    matched = true;
1140
0
    goto done;
1141
0
      }
1142
0
      if ((gidlist = sudo_get_gidlist(pw, ENTRY_TYPE_ANY)) != NULL) {
1143
0
    for (i = 0; i < gidlist->ngids; i++) {
1144
0
        if (gid == gidlist->gids[i]) {
1145
0
      matched = true;
1146
0
      goto done;
1147
0
        }
1148
0
    }
1149
0
      }
1150
0
  }
1151
0
    }
1152
1153
    /*
1154
     * Next match the group name.  By default, sudoers resolves all the user's
1155
     * group-IDs to names and matches by name.  If match_group_by_gid is
1156
     * set, each group is sudoers is resolved and matching is by group-ID.
1157
     */
1158
0
    if (def_match_group_by_gid) {
1159
0
  gid_t gid;
1160
1161
  /* Look up the ID of the group in sudoers. */
1162
0
  if ((grp = sudo_getgrnam(group)) == NULL)
1163
0
      goto done;
1164
0
  gid = grp->gr_gid;
1165
1166
  /* Check against user's primary (passwd file) group-ID. */
1167
0
  if (gid == pw->pw_gid) {
1168
0
      matched = true;
1169
0
      goto done;
1170
0
  }
1171
1172
  /* Check the supplementary group vector. */
1173
0
  if (gidlist == NULL) {
1174
0
      if ((gidlist = sudo_get_gidlist(pw, ENTRY_TYPE_ANY)) != NULL) {
1175
0
    for (i = 0; i < gidlist->ngids; i++) {
1176
0
        if (gid == gidlist->gids[i]) {
1177
0
      matched = true;
1178
0
      goto done;
1179
0
        }
1180
0
    }
1181
0
      }
1182
0
  }
1183
0
    } else if ((grlist = sudo_get_grlist(pw)) != NULL) {
1184
0
  int (*compare)(const char *, const char *);
1185
0
  if (def_case_insensitive_group)
1186
0
      compare = strcasecmp;
1187
0
  else
1188
0
      compare = strcmp;
1189
1190
  /* Check the user's group vector, which includes the primary group. */
1191
0
  for (i = 0; i < grlist->ngroups; i++) {
1192
0
      if (compare(group, grlist->groups[i]) == 0) {
1193
0
    matched = true;
1194
0
    goto done;
1195
0
      }
1196
0
  }
1197
0
    }
1198
1199
0
done:
1200
0
    if (grp != NULL)
1201
0
  sudo_gr_delref(grp);
1202
0
    if (grlist != NULL)
1203
0
  sudo_grlist_delref(grlist);
1204
0
    if (gidlist != NULL)
1205
0
  sudo_gidlist_delref(gidlist);
1206
1207
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: user %s %sin group %s",
1208
0
  __func__, pw->pw_name, matched ? "" : "NOT ", group);
1209
0
    debug_return_bool(matched);
1210
0
}
1211
1212
/*
1213
 * Returns true if the user's shell is considered to be valid.
1214
 */
1215
bool
1216
user_shell_valid(const struct passwd *pw)
1217
15.8k
{
1218
15.8k
    debug_decl(user_shell_valid, SUDOERS_DEBUG_NSS);
1219
1220
15.8k
    if (!def_runas_check_shell)
1221
15.8k
  debug_return_bool(true);
1222
1223
0
    debug_return_bool(valid_shell(pw->pw_shell));
1224
0
}