Coverage Report

Created: 2023-06-07 06:47

/src/sudo/plugins/sudoers/pwutil_impl.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 <stddef.h>
32
#include <stdio.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include <unistd.h>
36
#include <errno.h>
37
#include <limits.h>
38
#include <pwd.h>
39
#include <grp.h>
40
41
#include "sudoers.h"
42
#include "pwutil.h"
43
44
#ifndef LOGIN_NAME_MAX
45
# ifdef _POSIX_LOGIN_NAME_MAX
46
#  define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
47
# else
48
#  define LOGIN_NAME_MAX 9
49
# endif
50
#endif /* LOGIN_NAME_MAX */
51
52
247k
#define FIELD_SIZE(src, name, size)     \
53
247k
do {             \
54
247k
  if (src->name) {       \
55
247k
    size = strlen(src->name) + 1;   \
56
247k
    total += size;        \
57
247k
  } else {                                        \
58
0
    size = 0;       \
59
0
  }                                               \
60
247k
} while (0)
61
62
247k
#define FIELD_COPY(src, dst, name, size)    \
63
247k
do {             \
64
247k
  if (src->name) {       \
65
247k
    memcpy(cp, src->name, size);    \
66
247k
    dst->name = cp;       \
67
247k
    cp += size;       \
68
247k
  }            \
69
247k
} while (0)
70
71
/*
72
 * Dynamically allocate space for a struct item plus the key and data
73
 * elements.  If name is non-NULL it is used as the key, else the
74
 * uid is the key.  Fills in datum from struct password.
75
 * Returns NULL on calloc error or unknown name/id, setting errno
76
 * to ENOMEM or ENOENT respectively.
77
 */
78
struct cache_item *
79
sudo_make_pwitem(uid_t uid, const char *name)
80
62.6k
{
81
62.6k
    char *cp;
82
62.6k
    const char *pw_shell;
83
62.6k
    size_t nsize, psize, gsize, dsize, ssize, total;
84
#ifdef HAVE_LOGIN_CAP_H
85
    size_t csize;
86
#endif
87
62.6k
    struct cache_item_pw *pwitem;
88
62.6k
    struct passwd *pw, *newpw;
89
62.6k
    debug_decl(sudo_make_pwitem, SUDOERS_DEBUG_NSS);
90
91
    /* Look up by name or uid. */
92
62.6k
    pw = name ? getpwnam(name) : getpwuid(uid);
93
62.6k
    if (pw == NULL) {
94
906
  errno = ENOENT;
95
906
  debug_return_ptr(NULL);
96
906
    }
97
98
    /* If shell field is empty, expand to _PATH_BSHELL. */
99
61.7k
    pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
100
61.7k
  ? _PATH_BSHELL : pw->pw_shell;
101
102
    /* Allocate in one big chunk for easy freeing. */
103
61.7k
    total = sizeof(*pwitem);
104
61.7k
    FIELD_SIZE(pw, pw_name, nsize);
105
61.7k
    FIELD_SIZE(pw, pw_passwd, psize);
106
#ifdef HAVE_LOGIN_CAP_H
107
    FIELD_SIZE(pw, pw_class, csize);
108
#endif
109
61.7k
    FIELD_SIZE(pw, pw_gecos, gsize);
110
61.7k
    FIELD_SIZE(pw, pw_dir, dsize);
111
    /* Treat shell specially since we expand "" -> _PATH_BSHELL */
112
61.7k
    ssize = strlen(pw_shell) + 1;
113
61.7k
    total += ssize;
114
61.7k
    if (name != NULL)
115
40.2k
  total += strlen(name) + 1;
116
117
    /* Allocate space for struct item, struct passwd and the strings. */
118
61.7k
    if ((pwitem = calloc(1, total)) == NULL) {
119
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
120
0
      "unable to allocate memory");
121
0
  debug_return_ptr(NULL);
122
0
    }
123
61.7k
    newpw = &pwitem->pw;
124
125
    /*
126
     * Copy in passwd contents and make strings relative to space
127
     * at the end of the struct.
128
     */
129
61.7k
    memcpy(newpw, pw, sizeof(*pw));
130
61.7k
    cp = (char *)(pwitem + 1);
131
61.7k
    FIELD_COPY(pw, newpw, pw_name, nsize);
132
61.7k
    FIELD_COPY(pw, newpw, pw_passwd, psize);
133
#ifdef HAVE_LOGIN_CAP_H
134
    FIELD_COPY(pw, newpw, pw_class, csize);
135
#endif
136
61.7k
    FIELD_COPY(pw, newpw, pw_gecos, gsize);
137
61.7k
    FIELD_COPY(pw, newpw, pw_dir, dsize);
138
    /* Treat shell specially since we expand "" -> _PATH_BSHELL */
139
61.7k
    memcpy(cp, pw_shell, ssize);
140
61.7k
    newpw->pw_shell = cp;
141
61.7k
    cp += ssize;
142
143
    /* Set key and datum. */
144
61.7k
    if (name != NULL) {
145
40.2k
  memcpy(cp, name, strlen(name) + 1);
146
40.2k
  pwitem->cache.k.name = cp;
147
40.2k
    } else {
148
21.5k
  pwitem->cache.k.uid = pw->pw_uid;
149
21.5k
    }
150
61.7k
    pwitem->cache.d.pw = newpw;
151
61.7k
    pwitem->cache.refcnt = 1;
152
153
61.7k
    debug_return_ptr(&pwitem->cache);
154
61.7k
}
155
156
/*
157
 * Dynamically allocate space for a struct item plus the key and data
158
 * elements.  If name is non-NULL it is used as the key, else the
159
 * gid is the key.  Fills in datum from struct group.
160
 * Returns NULL on calloc error or unknown name/id, setting errno
161
 * to ENOMEM or ENOENT respectively.
162
 */
163
struct cache_item *
164
sudo_make_gritem(gid_t gid, const char *name)
165
768
{
166
768
    char *cp;
167
768
    size_t nsize, psize, total, len, nmem = 0;
168
768
    struct cache_item_gr *gritem;
169
768
    struct group *gr, *newgr;
170
768
    debug_decl(sudo_make_gritem, SUDOERS_DEBUG_NSS);
171
172
    /* Look up by name or gid. */
173
768
    gr = name ? getgrnam(name) : getgrgid(gid);
174
768
    if (gr == NULL) {
175
596
  errno = ENOENT;
176
596
  debug_return_ptr(NULL);
177
596
    }
178
179
    /* Allocate in one big chunk for easy freeing. */
180
172
    total = sizeof(*gritem);
181
172
    FIELD_SIZE(gr, gr_name, nsize);
182
172
    FIELD_SIZE(gr, gr_passwd, psize);
183
172
    if (gr->gr_mem) {
184
172
  for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
185
0
      total += strlen(gr->gr_mem[nmem]) + 1;
186
172
  nmem++;
187
172
  total += sizeof(char *) * nmem;
188
172
    }
189
172
    if (name != NULL)
190
31
  total += strlen(name) + 1;
191
192
172
    if ((gritem = calloc(1, total)) == NULL) {
193
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
194
0
      "unable to allocate memory");
195
0
  debug_return_ptr(NULL);
196
0
    }
197
198
    /*
199
     * Copy in group contents and make strings relative to space
200
     * at the end of the buffer.  Note that gr_mem must come
201
     * immediately after struct group to guarantee proper alignment.
202
     */
203
172
    newgr = &gritem->gr;
204
172
    memcpy(newgr, gr, sizeof(*gr));
205
172
    cp = (char *)(gritem + 1);
206
172
    if (gr->gr_mem) {
207
172
  newgr->gr_mem = (char **)cp;
208
172
  cp += sizeof(char *) * nmem;
209
172
  for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
210
0
      len = strlen(gr->gr_mem[nmem]) + 1;
211
0
      memcpy(cp, gr->gr_mem[nmem], len);
212
0
      newgr->gr_mem[nmem] = cp;
213
0
      cp += len;
214
0
  }
215
172
  newgr->gr_mem[nmem] = NULL;
216
172
    }
217
172
    FIELD_COPY(gr, newgr, gr_passwd, psize);
218
172
    FIELD_COPY(gr, newgr, gr_name, nsize);
219
220
    /* Set key and datum. */
221
172
    if (name != NULL) {
222
31
  memcpy(cp, name, strlen(name) + 1);
223
31
  gritem->cache.k.name = cp;
224
141
    } else {
225
141
  gritem->cache.k.gid = gr->gr_gid;
226
141
    }
227
172
    gritem->cache.d.gr = newgr;
228
172
    gritem->cache.refcnt = 1;
229
230
172
    debug_return_ptr(&gritem->cache);
231
172
}
232
233
/*
234
 * Dynamically allocate space for a struct item plus the key and data
235
 * elements.  Fills in datum from user_gids or from sudo_getgrouplist2(3).
236
 */
237
struct cache_item *
238
sudo_make_gidlist_item(const struct passwd *pw, char * const *gidstrs,
239
    unsigned int type)
240
28.9k
{
241
28.9k
    char *cp;
242
28.9k
    size_t nsize, total;
243
28.9k
    struct cache_item_gidlist *glitem;
244
28.9k
    struct gid_list *gidlist;
245
28.9k
    GETGROUPS_T *gids;
246
28.9k
    int i, ngids;
247
28.9k
    debug_decl(sudo_make_gidlist_item, SUDOERS_DEBUG_NSS);
248
249
    /*
250
     * Ignore supplied gids if the entry type says we must query the group db.
251
     */
252
28.9k
    if (type != ENTRY_TYPE_QUERIED && (gidstrs != NULL ||
253
21.8k
      (pw == sudo_user.pw && sudo_user.gids != NULL))) {
254
355
  if (gidstrs != NULL) {
255
      /* Use supplied gids list (string format). */
256
27
      ngids = 1;
257
99
      for (i = 0; gidstrs[i] != NULL; i++)
258
72
    ngids++;
259
27
      gids = reallocarray(NULL, ngids, sizeof(GETGROUPS_T));
260
27
      if (gids == NULL) {
261
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
262
0
        "unable to allocate memory");
263
0
    debug_return_ptr(NULL);
264
0
      }
265
27
      ngids = 1;
266
27
      gids[0] = pw->pw_gid;
267
99
      for (i = 0; gidstrs[i] != NULL; i++) {
268
72
    const char *errstr;
269
72
    GETGROUPS_T gid = (gid_t) sudo_strtoid(gidstrs[i], &errstr);
270
72
    if (errstr != NULL) {
271
0
        sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO,
272
0
      "gid %s %s", gidstrs[i], errstr);
273
0
        continue;
274
0
    }
275
72
    if (gid != gids[0])
276
45
        gids[ngids++] = gid;
277
72
      }
278
328
  } else {
279
      /* Adopt sudo_user.gids. */
280
328
      gids = user_gids;
281
328
      ngids = user_ngids;
282
328
      user_gids = NULL;
283
328
      user_ngids = 0;
284
328
  }
285
355
  type = ENTRY_TYPE_FRONTEND;
286
28.6k
    } else {
287
28.6k
  type = ENTRY_TYPE_QUERIED;
288
28.6k
  if (sudo_user.max_groups > 0) {
289
56
      ngids = sudo_user.max_groups;
290
56
      gids = reallocarray(NULL, ngids, sizeof(GETGROUPS_T));
291
56
      if (gids == NULL) {
292
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
293
0
        "unable to allocate memory");
294
0
    debug_return_ptr(NULL);
295
0
      }
296
      /* Clamp to max_groups if insufficient space for all groups. */
297
56
      if (sudo_getgrouplist2(pw->pw_name, pw->pw_gid, &gids, &ngids) == -1)
298
0
    ngids = sudo_user.max_groups;
299
28.5k
  } else {
300
28.5k
      gids = NULL;
301
28.5k
      if (sudo_getgrouplist2(pw->pw_name, pw->pw_gid, &gids, &ngids) == -1) {
302
0
    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
303
0
        "unable to allocate memory");
304
0
    debug_return_ptr(NULL);
305
0
      }
306
28.5k
  }
307
28.6k
    }
308
28.9k
    if (ngids <= 0) {
309
0
  free(gids);
310
0
  errno = ENOENT;
311
0
  debug_return_ptr(NULL);
312
0
    }
313
314
    /* Allocate in one big chunk for easy freeing. */
315
28.9k
    nsize = strlen(pw->pw_name) + 1;
316
28.9k
    total = sizeof(*glitem) + nsize;
317
28.9k
    total += sizeof(gid_t *) * ngids;
318
319
28.9k
    if ((glitem = calloc(1, total)) == NULL) {
320
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
321
0
      "unable to allocate memory");
322
0
  free(gids);
323
0
  debug_return_ptr(NULL);
324
0
    }
325
326
    /*
327
     * Copy in group list and make pointers relative to space
328
     * at the end of the buffer.  Note that the groups array must come
329
     * immediately after struct group to guarantee proper alignment.
330
     */
331
28.9k
    gidlist = &glitem->gidlist;
332
28.9k
    cp = (char *)(glitem + 1);
333
28.9k
    gidlist->gids = (gid_t *)cp;
334
28.9k
    cp += sizeof(gid_t) * ngids;
335
336
    /* Set key and datum. */
337
28.9k
    memcpy(cp, pw->pw_name, nsize);
338
28.9k
    glitem->cache.k.name = cp;
339
28.9k
    glitem->cache.d.gidlist = gidlist;
340
28.9k
    glitem->cache.refcnt = 1;
341
28.9k
    glitem->cache.type = type;
342
343
    /*
344
     * Store group IDs.
345
     */
346
259k
    for (i = 0; i < ngids; i++)
347
230k
  gidlist->gids[i] = gids[i];
348
28.9k
    gidlist->ngids = ngids;
349
28.9k
    free(gids);
350
351
28.9k
    debug_return_ptr(&glitem->cache);
352
28.9k
}
353
354
/*
355
 * Dynamically allocate space for a struct item plus the key and data
356
 * elements.  Fills in group names from a call to sudo_get_gidlist().
357
 */
358
struct cache_item *
359
sudo_make_grlist_item(const struct passwd *pw, char * const *unused1)
360
0
{
361
0
    char *cp;
362
0
    size_t groupname_len, len, ngroups, nsize, total;
363
0
    struct cache_item_grlist *grlitem;
364
0
    struct group_list *grlist;
365
0
    struct gid_list *gidlist;
366
0
    struct group *grp = NULL;
367
0
    int i;
368
0
    debug_decl(sudo_make_grlist_item, SUDOERS_DEBUG_NSS);
369
370
0
    gidlist = sudo_get_gidlist(pw, ENTRY_TYPE_ANY);
371
0
    if (gidlist == NULL) {
372
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
373
0
      "no gid list for use %s", pw->pw_name);
374
0
  errno = ENOENT;
375
0
  debug_return_ptr(NULL);
376
0
    }
377
378
0
#ifdef _SC_LOGIN_NAME_MAX
379
0
    groupname_len = MAX(sysconf(_SC_LOGIN_NAME_MAX), 32);
380
#else
381
    groupname_len = MAX(LOGIN_NAME_MAX, 32);
382
#endif
383
384
    /* Allocate in one big chunk for easy freeing. */
385
0
    nsize = strlen(pw->pw_name) + 1;
386
0
    total = sizeof(*grlitem) + nsize;
387
0
    total += sizeof(char *) * gidlist->ngids;
388
0
    total += groupname_len * gidlist->ngids;
389
390
0
again:
391
0
    if ((grlitem = calloc(1, total)) == NULL) {
392
0
  sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
393
0
      "unable to allocate memory");
394
0
  sudo_gidlist_delref(gidlist);
395
0
  debug_return_ptr(NULL);
396
0
    }
397
398
    /*
399
     * Copy in group list and make pointers relative to space
400
     * at the end of the buffer.  Note that the groups array must come
401
     * immediately after struct group to guarantee proper alignment.
402
     */
403
0
    grlist = &grlitem->grlist;
404
0
    cp = (char *)(grlitem + 1);
405
0
    grlist->groups = (char **)cp;
406
0
    cp += sizeof(char *) * gidlist->ngids;
407
408
    /* Set key and datum. */
409
0
    memcpy(cp, pw->pw_name, nsize);
410
0
    grlitem->cache.k.name = cp;
411
0
    grlitem->cache.d.grlist = grlist;
412
0
    grlitem->cache.refcnt = 1;
413
0
    cp += nsize;
414
415
    /*
416
     * Resolve and store group names by ID.
417
     */
418
#ifdef HAVE_SETAUTHDB
419
    if (grp == NULL)
420
  aix_setauthdb((char *) pw->pw_name, NULL);
421
#endif
422
0
    ngroups = 0;
423
0
    for (i = 0; i < gidlist->ngids; i++) {
424
0
  if ((grp = sudo_getgrgid(gidlist->gids[i])) != NULL) {
425
0
      len = strlen(grp->gr_name) + 1;
426
0
      if (cp - (char *)grlitem + len > total) {
427
0
    total += len + groupname_len;
428
0
    free(grlitem);
429
0
    sudo_gr_delref(grp);
430
0
    goto again;
431
0
      }
432
0
      memcpy(cp, grp->gr_name, len);
433
0
      grlist->groups[ngroups++] = cp;
434
0
      cp += len;
435
0
      sudo_gr_delref(grp);
436
0
  }
437
0
    }
438
0
    grlist->ngroups = ngroups;
439
0
    sudo_gidlist_delref(gidlist);
440
441
#ifdef HAVE_SETAUTHDB
442
    aix_restoreauthdb();
443
#endif
444
445
0
    debug_return_ptr(&grlitem->cache);
446
0
}