Coverage Report

Created: 2025-07-12 06:26

/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
0
#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
0
# 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
0
{
115
0
    return max_groups;
116
0
}
117
118
/* Set the max number of user groups (negative values ignored). */
119
void
120
sudo_pwutil_set_max_groups(int n)
121
0
{
122
0
    max_groups = n > 0 ? n : 0;
123
0
}
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
0
{
132
0
    const struct cache_item *ci1 = (const struct cache_item *) v1;
133
0
    const struct cache_item *ci2 = (const struct cache_item *) v2;
134
0
    if (ci1->k.uid == ci2->k.uid)
135
0
  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
0
{
148
0
    const struct cache_item *ci1 = (const struct cache_item *) v1;
149
0
    const struct cache_item *ci2 = (const struct cache_item *) v2;
150
0
    int ret = strcmp(ci1->k.name, ci2->k.name);
151
0
    if (ret == 0)
152
0
  ret = strcmp(ci1->registry, ci2->registry);
153
0
    return ret;
154
0
}
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
0
{
165
0
    const struct cache_item *ci1 = (const struct cache_item *) v1;
166
0
    const struct cache_item *ci2 = (const struct cache_item *) v2;
167
0
    int ret = strcmp(ci1->k.name, ci2->k.name);
168
0
    if (ret == 0) {
169
0
  if (ci1->type == ENTRY_TYPE_ANY || ci1->type == ci2->type)
170
0
      return strcmp(ci1->registry, ci2->registry);
171
0
  if (ci1->type < ci2->type)
172
0
      return -1;
173
0
  return 1;
174
0
    }
175
0
    return ret;
176
0
}
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
0
{
189
0
    struct cache_item *item = v;
190
0
    debug_decl(sudo_pw_delref_item, SUDOERS_DEBUG_NSS);
191
192
0
    if (--item->refcnt == 0)
193
0
  free(item);
194
195
0
    debug_return;
196
0
}
197
198
void
199
sudo_pw_delref(struct passwd *pw)
200
0
{
201
0
    debug_decl(sudo_pw_delref, SUDOERS_DEBUG_NSS);
202
0
    sudo_pw_delref_item(ptr_to_item(pw));
203
0
    debug_return;
204
0
}
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
0
{
212
0
    struct cache_item key, *item;
213
0
    struct rbnode *node;
214
0
    debug_decl(sudo_getpwuid, SUDOERS_DEBUG_NSS);
215
216
0
    if (pwcache_byuid == NULL) {
217
0
  pwcache_byuid = rbcreate(cmp_pwuid);
218
0
  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
0
    }
223
224
0
    key.k.uid = uid;
225
0
    getauthregistry(IDtouser(uid), key.registry);
226
0
    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
0
    item = make_pwitem(uid, NULL);
237
#ifdef HAVE_SETAUTHDB
238
    aix_restoreauthdb();
239
#endif
240
0
    if (item == NULL) {
241
0
  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
0
  item->refcnt = 1;
247
0
  item->k.uid = uid;
248
  /* item->d.pw = NULL; */
249
0
    }
250
0
    strlcpy(item->registry, key.registry, sizeof(item->registry));
251
0
    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
0
    }
264
0
done:
265
0
    if (item->refcnt != 0) {
266
0
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
267
0
      "%s: uid %u [%s] -> user %s [%s] (%s)", __func__,
268
0
      (unsigned int)uid, key.registry,
269
0
      item->d.pw ? item->d.pw->pw_name : "unknown",
270
0
      item->registry, node ? "cache hit" : "cached");
271
0
    }
272
0
    if (item->d.pw != NULL)
273
0
  item->refcnt++;
274
0
    debug_return_ptr(item->d.pw);
275
0
}
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
0
{
283
0
    struct cache_item key, *item;
284
0
    struct rbnode *node;
285
0
    debug_decl(sudo_getpwnam, SUDOERS_DEBUG_NSS);
286
287
0
    if (pwcache_byname == NULL) {
288
0
  pwcache_byname = rbcreate(cmp_pwnam);
289
0
  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
0
    }
294
295
0
    key.k.name = (char *) name;
296
0
    getauthregistry((char *) name, key.registry);
297
0
    if ((node = rbfind(pwcache_byname, &key)) != NULL) {
298
0
  item = node->data;
299
0
  goto done;
300
0
    }
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
0
    item = make_pwitem((uid_t)-1, name);
308
#ifdef HAVE_SETAUTHDB
309
    aix_restoreauthdb();
310
#endif
311
0
    if (item == NULL) {
312
0
  const size_t len = strlen(name) + 1;
313
0
  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
0
  item->refcnt = 1;
319
0
  item->k.name = (char *) item + sizeof(*item);
320
0
  memcpy(item->k.name, name, len);
321
  /* item->d.pw = NULL; */
322
0
    }
323
0
    strlcpy(item->registry, key.registry, sizeof(item->registry));
324
0
    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
0
    }
336
0
done:
337
0
    if (item->refcnt != 0) {
338
0
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
339
0
      "%s: user %s [%s] -> uid %d [%s] (%s)", __func__, name,
340
0
      key.registry, item->d.pw ? (int)item->d.pw->pw_uid : -1,
341
0
      item->registry, node ? "cache hit" : "cached");
342
0
    }
343
0
    if (item->d.pw != NULL)
344
0
  item->refcnt++;
345
0
    debug_return_ptr(item->d.pw);
346
0
}
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
0
{
356
0
    struct cache_item_pw *pwitem;
357
0
    struct cache_item *item;
358
0
    struct passwd *pw;
359
0
    size_t len, name_len, home_len, shell_len;
360
0
    unsigned int i;
361
0
    debug_decl(sudo_mkpwent, SUDOERS_DEBUG_NSS);
362
363
0
    if (pwcache_byuid == NULL)
364
0
  pwcache_byuid = rbcreate(cmp_pwuid);
365
0
    if (pwcache_byname == NULL)
366
0
  pwcache_byname = rbcreate(cmp_pwnam);
367
0
    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
0
    if (home == NULL)
374
0
  home = "/";
375
0
    if (shell == NULL)
376
0
  shell = _PATH_BSHELL;
377
378
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG,
379
0
  "%s: creating and caching passwd struct for %s:%u:%u:%s:%s", __func__,
380
0
  user, (unsigned int)uid, (unsigned int)gid, home, shell);
381
382
0
    name_len = strlen(user);
383
0
    home_len = strlen(home);
384
0
    shell_len = strlen(shell);
385
0
    len = sizeof(*pwitem) + name_len + 1 /* pw_name */ +
386
0
  sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
387
0
  home_len + 1 /* pw_dir */ + shell_len + 1 /* pw_shell */;
388
389
0
    for (i = 0; i < 2; i++) {
390
0
  struct rbtree *pwcache;
391
0
  struct rbnode *node;
392
393
0
  pwitem = calloc(1, len);
394
0
  if (pwitem == NULL) {
395
0
      sudo_warn(U_("unable to cache user %s"), user);
396
0
      debug_return_ptr(NULL);
397
0
  }
398
0
  pw = &pwitem->pw;
399
0
  pw->pw_uid = uid;
400
0
  pw->pw_gid = gid;
401
0
  pw->pw_name = (char *)(pwitem + 1);
402
0
  memcpy(pw->pw_name, user, name_len + 1);
403
0
  pw->pw_passwd = pw->pw_name + name_len + 1;
404
0
  memcpy(pw->pw_passwd, "*", 2);
405
0
  pw->pw_gecos = pw->pw_passwd + 2;
406
0
  pw->pw_gecos[0] = '\0';
407
0
  pw->pw_dir = pw->pw_gecos + 1;
408
0
  memcpy(pw->pw_dir, home, home_len + 1);
409
0
  pw->pw_shell = pw->pw_dir + home_len + 1;
410
0
  memcpy(pw->pw_shell, shell, shell_len + 1);
411
412
0
  item = &pwitem->cache;
413
0
  item->refcnt = 1;
414
0
  item->d.pw = pw;
415
0
  if (i == 0) {
416
      /* Store by uid. */
417
0
      item->k.uid = pw->pw_uid;
418
0
      pwcache = pwcache_byuid;
419
0
  } else {
420
      /* Store by name. */
421
0
      item->k.name = pw->pw_name;
422
0
      pwcache = pwcache_byname;
423
0
  }
424
0
  getauthregistry(NULL, item->registry);
425
0
  switch (rbinsert(pwcache, item, &node)) {
426
0
  case 1:
427
      /* Already exists. */
428
0
      item = node->data;
429
0
      if (item->d.pw == NULL) {
430
    /* Negative cache entry, replace with ours. */
431
0
    sudo_pw_delref_item(item);
432
0
    item = node->data = &pwitem->cache;
433
0
      } else {
434
    /* Good entry, discard our fake one. */
435
0
    free(pwitem);
436
0
      }
437
0
      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
0
  }
444
0
    }
445
0
    item->refcnt++;
446
0
    debug_return_ptr(item->d.pw);
447
0
}
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
0
{
455
0
    const char *errstr;
456
0
    uid_t uid;
457
0
    debug_decl(sudo_fakepwnam, SUDOERS_DEBUG_NSS);
458
459
0
    uid = (uid_t) sudo_strtoid(user + 1, &errstr);
460
0
    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
0
    debug_return_ptr(sudo_mkpwent(user, uid, gid, NULL, NULL));
466
0
}
467
468
void
469
sudo_freepwcache(void)
470
0
{
471
0
    debug_decl(sudo_freepwcache, SUDOERS_DEBUG_NSS);
472
473
0
    if (pwcache_byuid != NULL) {
474
0
  rbdestroy(pwcache_byuid, sudo_pw_delref_item);
475
0
  pwcache_byuid = NULL;
476
0
    }
477
0
    if (pwcache_byname != NULL) {
478
0
  rbdestroy(pwcache_byname, sudo_pw_delref_item);
479
0
  pwcache_byname = NULL;
480
0
    }
481
482
0
    debug_return;
483
0
}
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
0
{
492
0
    const struct cache_item *ci1 = (const struct cache_item *) v1;
493
0
    const struct cache_item *ci2 = (const struct cache_item *) v2;
494
0
    if (ci1->k.gid == ci2->k.gid)
495
0
  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
0
{
512
0
    struct cache_item *item = v;
513
0
    debug_decl(sudo_gr_delref_item, SUDOERS_DEBUG_NSS);
514
515
0
    if (--item->refcnt == 0)
516
0
  free(item);
517
518
0
    debug_return;
519
0
}
520
521
void
522
sudo_gr_delref(struct group *gr)
523
0
{
524
0
    debug_decl(sudo_gr_delref, SUDOERS_DEBUG_NSS);
525
0
    sudo_gr_delref_item(ptr_to_item(gr));
526
0
    debug_return;
527
0
}
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
0
{
535
0
    struct cache_item key, *item;
536
0
    struct rbnode *node;
537
0
    debug_decl(sudo_getgrgid, SUDOERS_DEBUG_NSS);
538
539
0
    if (grcache_bygid == NULL) {
540
0
  grcache_bygid = rbcreate(cmp_grgid);
541
0
  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
0
    }
546
547
0
    key.k.gid = gid;
548
0
    getauthregistry(NULL, key.registry);
549
0
    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
0
    item = make_gritem(gid, NULL);
557
0
    if (item == NULL) {
558
0
  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
0
  item->refcnt = 1;
564
0
  item->k.gid = gid;
565
  /* item->d.gr = NULL; */
566
0
    }
567
0
    strlcpy(item->registry, key.registry, sizeof(item->registry));
568
0
    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
0
    }
581
0
done:
582
0
    if (item->refcnt != 0) {
583
0
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
584
0
      "%s: gid %u [%s] -> group %s [%s] (%s)", __func__,
585
0
      (unsigned int)gid, key.registry,
586
0
      item->d.gr ? item->d.gr->gr_name : "unknown",
587
0
      item->registry, node ? "cache hit" : "cached");
588
0
    }
589
0
    if (item->d.gr != NULL)
590
0
  item->refcnt++;
591
0
    debug_return_ptr(item->d.gr);
592
0
}
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
0
{
600
0
    struct cache_item key, *item;
601
0
    struct rbnode *node;
602
0
    debug_decl(sudo_getgrnam, SUDOERS_DEBUG_NSS);
603
604
0
    if (grcache_byname == NULL) {
605
0
  grcache_byname = rbcreate(cmp_grnam);
606
0
  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
0
    }
611
612
0
    key.k.name = (char *) name;
613
0
    getauthregistry(NULL, key.registry);
614
0
    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
0
    item = make_gritem((gid_t)-1, name);
622
0
    if (item == NULL) {
623
0
  const size_t len = strlen(name) + 1;
624
0
  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
0
  item->refcnt = 1;
630
0
  item->k.name = (char *) item + sizeof(*item);
631
0
  memcpy(item->k.name, name, len);
632
  /* item->d.gr = NULL; */
633
0
    }
634
0
    strlcpy(item->registry, key.registry, sizeof(item->registry));
635
0
    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
0
    }
647
0
done:
648
0
    if (item->refcnt != 0) {
649
0
  sudo_debug_printf(SUDO_DEBUG_DEBUG,
650
0
      "%s: group %s [%s] -> gid %d [%s] (%s)", __func__, name,
651
0
      key.registry, item->d.gr ? (int)item->d.gr->gr_gid : -1,
652
0
      item->registry, node ? "cache hit" : "cached");
653
0
    }
654
0
    if (item->d.gr != NULL)
655
0
  item->refcnt++;
656
0
    debug_return_ptr(item->d.gr);
657
0
}
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
0
{
665
0
    struct cache_item_gr *gritem;
666
0
    struct cache_item *item;
667
0
    struct group *gr;
668
0
    size_t nmem, nsize, total;
669
0
    char *cp, *mem;
670
0
    va_list ap;
671
0
    unsigned int i;
672
0
    debug_decl(sudo_mkgrent, SUDOERS_DEBUG_NSS);
673
674
0
    if (grcache_bygid == NULL)
675
0
  grcache_bygid = rbcreate(cmp_grgid);
676
0
    if (grcache_byname == NULL)
677
0
  grcache_byname = rbcreate(cmp_grnam);
678
0
    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
0
    nsize = strlen(group) + 1;
685
0
    total = sizeof(*gritem) + nsize;
686
0
    va_start(ap, gid);
687
0
    for (nmem = 1; (mem = va_arg(ap, char *)) != NULL; nmem++) {
688
0
  total += strlen(mem) + 1;
689
0
    }
690
0
    va_end(ap);
691
0
    total += sizeof(char *) * nmem;
692
693
0
    for (i = 0; i < 2; i++) {
694
0
  struct rbtree *grcache;
695
0
  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
0
  gritem = calloc(1, total);
703
0
  if (gritem == NULL) {
704
0
      sudo_warn(U_("unable to cache group %s"), group);
705
0
      debug_return_ptr(NULL);
706
0
  }
707
0
  gr = &gritem->gr;
708
0
  gr->gr_gid = gid;
709
0
  gr->gr_passwd = (char *)"*";
710
0
  cp = (char *)(gritem + 1);
711
0
  gr->gr_mem = (char **)cp;
712
0
  cp += sizeof(char *) * nmem;
713
0
  va_start(ap, gid);
714
0
  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
0
  va_end(ap);
721
0
  gr->gr_mem[nmem] = NULL;
722
0
  gr->gr_name = cp;
723
0
  memcpy(gr->gr_name, group, nsize);
724
725
0
  item = &gritem->cache;
726
0
  item->refcnt = 1;
727
0
  item->d.gr = gr;
728
0
  if (i == 0) {
729
      /* Store by gid if it doesn't already exist. */
730
0
      item->k.gid = gr->gr_gid;
731
0
      grcache = grcache_bygid;
732
0
  } else {
733
      /* Store by name, overwriting cached version. */
734
0
      gritem->cache.k.name = gr->gr_name;
735
0
      grcache = grcache_byname;
736
0
  }
737
0
  getauthregistry(NULL, item->registry);
738
0
  switch (rbinsert(grcache, item, &node)) {
739
0
  case 1:
740
      /* Already exists. */
741
0
      item = node->data;
742
0
      if (item->d.gr == NULL) {
743
    /* Negative cache entry, replace with ours. */
744
0
    sudo_gr_delref_item(item);
745
0
    item = node->data = &gritem->cache;
746
0
      } else {
747
    /* Good entry, discard our fake one. */
748
0
    free(gritem);
749
0
      }
750
0
      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
0
  }
757
0
    }
758
0
    if (item->d.gr != NULL)
759
0
  item->refcnt++;
760
0
    debug_return_ptr(item->d.gr);
761
0
}
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
0
{
769
0
    const char *errstr;
770
0
    gid_t gid;
771
0
    debug_decl(sudo_fakegrnam, SUDOERS_DEBUG_NSS);
772
773
0
    gid = (gid_t) sudo_strtoid(group + 1, &errstr);
774
0
    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
0
    debug_return_ptr(sudo_mkgrent(group, gid, (char *)NULL));
781
0
}
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
0
{
794
0
    struct cache_item *item = v;
795
0
    debug_decl(sudo_gidlist_delref_item, SUDOERS_DEBUG_NSS);
796
797
0
    if (--item->refcnt == 0)
798
0
  free(item);
799
800
0
    debug_return;
801
0
}
802
803
void
804
sudo_gidlist_delref(struct gid_list *gidlist)
805
0
{
806
0
    debug_decl(sudo_gidlist_delref, SUDOERS_DEBUG_NSS);
807
0
    sudo_gidlist_delref_item(ptr_to_item(gidlist));
808
0
    debug_return;
809
0
}
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
0
{
842
0
    debug_decl(sudo_freegrcache, SUDOERS_DEBUG_NSS);
843
844
0
    if (grcache_bygid != NULL) {
845
0
  rbdestroy(grcache_bygid, sudo_gr_delref_item);
846
0
  grcache_bygid = NULL;
847
0
    }
848
0
    if (grcache_byname != NULL) {
849
0
  rbdestroy(grcache_byname, sudo_gr_delref_item);
850
0
  grcache_byname = NULL;
851
0
    }
852
0
    if (grlist_cache != NULL) {
853
0
  rbdestroy(grlist_cache, sudo_grlist_delref_item);
854
0
  grlist_cache = NULL;
855
0
    }
856
0
    if (gidlist_cache != NULL) {
857
0
  rbdestroy(gidlist_cache, sudo_gidlist_delref_item);
858
0
  gidlist_cache = NULL;
859
0
    }
860
861
0
    debug_return;
862
0
}
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
0
{
928
0
    size_t i, len = 0;
929
0
    debug_decl(sudo_debug_group_list, SUDOERS_DEBUG_NSS);
930
931
0
    if (groups == NULL || !sudo_debug_needed(level))
932
0
  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
0
{
1008
0
    struct cache_item key, *item;
1009
0
    struct rbnode *node;
1010
0
    debug_decl(sudo_get_gidlist, SUDOERS_DEBUG_NSS);
1011
1012
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: looking up group-IDs for %s",
1013
0
  __func__, pw->pw_name);
1014
1015
0
    if (gidlist_cache == NULL) {
1016
0
  gidlist_cache = rbcreate(cmp_gidlist);
1017
0
  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
0
    }
1022
1023
0
    key.k.name = pw->pw_name;
1024
0
    key.type = type;
1025
0
    getauthregistry(pw->pw_name, key.registry);
1026
0
    if ((node = rbfind(gidlist_cache, &key)) != NULL) {
1027
0
  item = node->data;
1028
0
  goto done;
1029
0
    }
1030
    /*
1031
     * Cache group db entry if it exists or a negative response if not.
1032
     */
1033
0
    item = make_gidlist_item(pw, -1, NULL, NULL, type);
1034
0
    if (item == NULL) {
1035
  /* Out of memory? */
1036
0
  debug_return_ptr(NULL);
1037
0
    }
1038
0
    strlcpy(item->registry, key.registry, sizeof(item->registry));
1039
0
    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
0
    }
1052
0
    if (item->d.gidlist != NULL) {
1053
0
  int i;
1054
0
  for (i = 0; i < item->d.gidlist->ngids; i++) {
1055
0
      sudo_debug_printf(SUDO_DEBUG_DEBUG,
1056
0
    "%s: user %s has supplementary gid %u", __func__,
1057
0
    pw->pw_name, (unsigned int)item->d.gidlist->gids[i]);
1058
0
  }
1059
0
    }
1060
0
done:
1061
0
    if (item->d.gidlist != NULL)
1062
0
  item->refcnt++;
1063
0
    debug_return_ptr(item->d.gidlist);
1064
0
}
1065
1066
int
1067
sudo_set_gidlist(struct passwd *pw, int ngids, GETGROUPS_T *gids,
1068
    char * const *gidstrs, unsigned int type)
1069
0
{
1070
0
    struct cache_item key, *item;
1071
0
    debug_decl(sudo_set_gidlist, SUDOERS_DEBUG_NSS);
1072
1073
0
    sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: setting group-IDs for %s",
1074
0
  __func__, pw->pw_name);
1075
1076
    /* XXX - ngids/gids too */
1077
0
    sudo_debug_group_list(pw->pw_name, gidstrs, SUDO_DEBUG_DEBUG);
1078
1079
0
    if (gidlist_cache == NULL) {
1080
0
  gidlist_cache = rbcreate(cmp_gidlist);
1081
0
  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
0
    }
1086
1087
    /*
1088
     * Cache group db entry if it doesn't already exist
1089
     */
1090
0
    key.k.name = pw->pw_name;
1091
0
    key.type = type;
1092
0
    getauthregistry(pw->pw_name, key.registry);
1093
0
    if (rbfind(gidlist_cache, &key) == NULL) {
1094
0
  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
0
  strlcpy(item->registry, key.registry, sizeof(item->registry));
1099
0
  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
0
  }
1110
0
    } 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
0
    debug_return_int(0);
1116
0
}
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
0
{
1218
0
    debug_decl(user_shell_valid, SUDOERS_DEBUG_NSS);
1219
1220
0
    if (!def_runas_check_shell)
1221
0
  debug_return_bool(true);
1222
1223
0
    debug_return_bool(valid_shell(pw->pw_shell));
1224
0
}