Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/plugins/sudoers/cvtsudoers_pwutil.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 1996, 1998-2005, 2007-2020, 2022-2025
5
* Todd C. Miller <[email protected]>
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
#include <config.h>
25
26
#include <stddef.h>
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#ifdef HAVE_STRINGS_H
31
# include <strings.h>
32
#endif /* HAVE_STRINGS_H */
33
#include <unistd.h>
34
#include <errno.h>
35
#include <limits.h>
36
#include <pwd.h>
37
#include <grp.h>
38
39
#include <sudoers.h>
40
#include <cvtsudoers.h>
41
#include <pwutil.h>
42
43
#define FIELD_SIZE(src, name, size) \
44
do { \
45
if ((src)->name) { \
46
size = strlen((src)->name) + 1; \
47
total += size; \
48
} else { \
49
size = 0; \
50
} \
51
} while (0)
52
53
#define FIELD_COPY(src, dst, name, size) \
54
do { \
55
if ((src)->name) { \
56
memcpy(cp, (src)->name, size); \
57
(dst)->name = cp; \
58
cp += size; \
59
} \
60
} while (0)
61
62
/*
63
* Dynamically allocate space for a struct item plus the key and data
64
* elements. If name is non-NULL it is used as the key, else the
65
* uid is the key. Fills in datum from the users filter.
66
* Returns NULL on calloc error or unknown name/id, setting errno
67
* to ENOMEM or ENOENT respectively.
68
*/
69
struct cache_item *
70
cvtsudoers_make_pwitem(uid_t uid, const char *name)
71
{
72
char *cp, uidstr[STRLEN_MAX_UNSIGNED(uid_t) + 2];
73
size_t nsize, psize, gsize, dsize, ssize, total;
74
#ifdef HAVE_LOGIN_CAP_H
75
size_t csize;
76
#endif
77
struct cache_item_pw *pwitem;
78
struct passwd pw, *newpw;
79
struct sudoers_string *s = NULL;
80
debug_decl(cvtsudoers_make_pwitem, SUDOERS_DEBUG_NSS);
81
82
/* Look up name or uid in filter list. */
83
if (name != NULL) {
84
STAILQ_FOREACH(s, &filters->users, entries) {
85
if (strcasecmp(name, s->str) == 0) {
86
uid = (uid_t)-1;
87
break;
88
}
89
}
90
} else {
91
STAILQ_FOREACH(s, &filters->users, entries) {
92
const char *errstr;
93
uid_t filter_uid;
94
95
if (s->str[0] != '#')
96
continue;
97
98
filter_uid = sudo_strtoid(s->str + 1, &errstr);
99
if (errstr == NULL) {
100
if (uid != filter_uid)
101
continue;
102
(void)snprintf(uidstr, sizeof(uidstr), "#%u",
103
(unsigned int)uid);
104
break;
105
}
106
}
107
}
108
if (s == NULL) {
109
errno = ENOENT;
110
debug_return_ptr(NULL);
111
}
112
113
/* Fake up a passwd struct. */
114
memset(&pw, 0, sizeof(pw));
115
pw.pw_name = name ? s->str : uidstr;
116
pw.pw_passwd = (char *)"*";
117
pw.pw_uid = uid;
118
pw.pw_gid = (gid_t)-1;
119
pw.pw_shell = (char *)_PATH_BSHELL;
120
pw.pw_dir = (char *)"/";
121
122
/* Allocate in one big chunk for easy freeing. */
123
total = sizeof(*pwitem);
124
FIELD_SIZE(&pw, pw_name, nsize);
125
FIELD_SIZE(&pw, pw_passwd, psize);
126
#ifdef HAVE_LOGIN_CAP_H
127
FIELD_SIZE(&pw, pw_class, csize);
128
#endif
129
FIELD_SIZE(&pw, pw_gecos, gsize);
130
FIELD_SIZE(&pw, pw_dir, dsize);
131
FIELD_SIZE(&pw, pw_shell, ssize);
132
if (name != NULL)
133
total += strlen(name) + 1;
134
135
/* Allocate space for struct item, struct passwd and the strings. */
136
if ((pwitem = calloc(1, total)) == NULL) {
137
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
138
"unable to allocate memory");
139
debug_return_ptr(NULL);
140
}
141
newpw = &pwitem->pw;
142
143
/*
144
* Copy in passwd contents and make strings relative to space
145
* at the end of the struct.
146
*/
147
memcpy(newpw, &pw, sizeof(pw));
148
cp = (char *)(pwitem + 1);
149
FIELD_COPY(&pw, newpw, pw_name, nsize);
150
FIELD_COPY(&pw, newpw, pw_passwd, psize);
151
#ifdef HAVE_LOGIN_CAP_H
152
FIELD_COPY(&pw, newpw, pw_class, csize);
153
#endif
154
FIELD_COPY(&pw, newpw, pw_gecos, gsize);
155
FIELD_COPY(&pw, newpw, pw_dir, dsize);
156
FIELD_COPY(&pw, newpw, pw_shell, ssize);
157
158
/* Set key and datum. */
159
if (name != NULL) {
160
memcpy(cp, name, strlen(name) + 1);
161
pwitem->cache.k.name = cp;
162
} else {
163
pwitem->cache.k.uid = pw.pw_uid;
164
}
165
pwitem->cache.d.pw = newpw;
166
pwitem->cache.refcnt = 1;
167
168
debug_return_ptr(&pwitem->cache);
169
}
170
171
/*
172
* Dynamically allocate space for a struct item plus the key and data
173
* elements. If name is non-NULL it is used as the key, else the
174
* gid is the key. Fills in datum from the groups filter.
175
* Returns NULL on calloc error or unknown name/id, setting errno
176
* to ENOMEM or ENOENT respectively.
177
*/
178
struct cache_item *
179
cvtsudoers_make_gritem(gid_t gid, const char *name)
180
{
181
char *cp, gidstr[STRLEN_MAX_UNSIGNED(gid_t) + 2];
182
size_t nsize, psize, total, len, nmem = 0;
183
struct cache_item_gr *gritem;
184
struct group gr, *newgr;
185
struct sudoers_string *s = NULL;
186
debug_decl(cvtsudoers_make_gritem, SUDOERS_DEBUG_NSS);
187
188
/* Look up name or gid in filter list. */
189
if (name != NULL) {
190
STAILQ_FOREACH(s, &filters->groups, entries) {
191
if (strcasecmp(name, s->str) == 0) {
192
gid = (gid_t)-1;
193
break;
194
}
195
}
196
} else {
197
STAILQ_FOREACH(s, &filters->groups, entries) {
198
const char *errstr;
199
gid_t filter_gid;
200
201
if (s->str[0] != '#')
202
continue;
203
204
filter_gid = sudo_strtoid(s->str + 1, &errstr);
205
if (errstr == NULL) {
206
if (gid != filter_gid)
207
continue;
208
(void)snprintf(gidstr, sizeof(gidstr), "#%u",
209
(unsigned int)gid);
210
break;
211
}
212
}
213
}
214
if (s == NULL) {
215
errno = ENOENT;
216
debug_return_ptr(NULL);
217
}
218
219
/* Fake up a group struct with all filter users as members. */
220
memset(&gr, 0, sizeof(gr));
221
gr.gr_name = name ? s->str : gidstr;
222
gr.gr_gid = gid;
223
224
/* Allocate in one big chunk for easy freeing. */
225
total = sizeof(*gritem);
226
FIELD_SIZE(&gr, gr_name, nsize);
227
FIELD_SIZE(&gr, gr_passwd, psize);
228
if (!STAILQ_EMPTY(&filters->users)) {
229
STAILQ_FOREACH(s, &filters->users, entries) {
230
total += strlen(s->str) + 1;
231
nmem++;
232
}
233
total += sizeof(char *) * nmem;
234
}
235
if (name != NULL)
236
total += strlen(name) + 1;
237
238
if ((gritem = calloc(1, total)) == NULL) {
239
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
240
"unable to allocate memory");
241
debug_return_ptr(NULL);
242
}
243
244
/*
245
* Copy in group contents and make strings relative to space
246
* at the end of the buffer. Note that gr_mem must come
247
* immediately after struct group to guarantee proper alignment.
248
*/
249
newgr = &gritem->gr;
250
memcpy(newgr, &gr, sizeof(gr));
251
cp = (char *)(gritem + 1);
252
if (nmem != 0) {
253
newgr->gr_mem = (char **)cp;
254
cp += sizeof(char *) * nmem;
255
nmem = 0;
256
STAILQ_FOREACH(s, &filters->users, entries) {
257
len = strlen(s->str) + 1;
258
memcpy(cp, s->str, len);
259
newgr->gr_mem[nmem++] = cp;
260
cp += len;
261
}
262
newgr->gr_mem[nmem] = NULL;
263
}
264
FIELD_COPY(&gr, newgr, gr_passwd, psize);
265
FIELD_COPY(&gr, newgr, gr_name, nsize);
266
267
/* Set key and datum. */
268
if (name != NULL) {
269
memcpy(cp, name, strlen(name) + 1);
270
gritem->cache.k.name = cp;
271
} else {
272
gritem->cache.k.gid = gr.gr_gid;
273
}
274
gritem->cache.d.gr = newgr;
275
gritem->cache.refcnt = 1;
276
277
debug_return_ptr(&gritem->cache);
278
}
279
280
static struct cache_item_gidlist *gidlist_item;
281
282
/*
283
* Dynamically allocate space for a struct item plus the key and data
284
* elements. Fills in datum from the groups filter.
285
*/
286
struct cache_item *
287
cvtsudoers_make_gidlist_item(const struct passwd *pw, int unused1,
288
GETGROUPS_T *unused2, char * const *unused3, unsigned int type)
289
{
290
char *cp;
291
size_t nsize, total;
292
struct cache_item_gidlist *glitem;
293
struct sudoers_string *s;
294
struct gid_list *gidlist;
295
GETGROUPS_T *gids = NULL;
296
int i, ngids = 0;
297
debug_decl(cvtsudoers_make_gidlist_item, SUDOERS_DEBUG_NSS);
298
299
/*
300
* There's only a single gid list.
301
*/
302
if (gidlist_item != NULL) {
303
gidlist_item->cache.refcnt++;
304
debug_return_ptr(&gidlist_item->cache);
305
}
306
307
/* Count number of possible gids in the filter. */
308
STAILQ_FOREACH(s, &filters->groups, entries) {
309
if (s->str[0] == '#')
310
ngids++;
311
}
312
313
/* Allocate gids[] array and fill it with parsed gids. */
314
if (ngids != 0) {
315
gids = reallocarray(NULL, (size_t)ngids, sizeof(GETGROUPS_T));
316
if (gids == NULL) {
317
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
318
"unable to allocate memory");
319
debug_return_ptr(NULL);
320
}
321
ngids = 0;
322
STAILQ_FOREACH(s, &filters->groups, entries) {
323
if (s->str[0] == '#') {
324
const char *errstr;
325
gid_t gid = sudo_strtoid(s->str + 1, &errstr);
326
if (errstr == NULL) {
327
/* Valid gid. */
328
gids[ngids++] = gid;
329
}
330
}
331
}
332
}
333
if (ngids == 0) {
334
free(gids);
335
errno = ENOENT;
336
debug_return_ptr(NULL);
337
}
338
339
/* Allocate in one big chunk for easy freeing. */
340
nsize = strlen(pw->pw_name) + 1;
341
total = sizeof(*glitem) + nsize;
342
total += sizeof(gid_t *) * (size_t)ngids;
343
344
if ((glitem = calloc(1, total)) == NULL) {
345
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
346
"unable to allocate memory");
347
free(gids);
348
debug_return_ptr(NULL);
349
}
350
351
/*
352
* Copy in group list and make pointers relative to space
353
* at the end of the buffer. Note that the groups array must come
354
* immediately after struct group to guarantee proper alignment.
355
*/
356
gidlist = &glitem->gidlist;
357
cp = (char *)(glitem + 1);
358
gidlist->gids = (gid_t *)cp;
359
cp += sizeof(gid_t) * (size_t)ngids;
360
361
/* Set key and datum. */
362
memcpy(cp, pw->pw_name, nsize);
363
glitem->cache.k.name = cp;
364
glitem->cache.d.gidlist = gidlist;
365
glitem->cache.refcnt = 1;
366
glitem->cache.type = type;
367
368
/*
369
* Store group IDs.
370
*/
371
for (i = 0; i < ngids; i++)
372
gidlist->gids[i] = gids[i];
373
gidlist->ngids = ngids;
374
free(gids);
375
376
debug_return_ptr(&glitem->cache);
377
}
378
379
static struct cache_item_gidlist *grlist_item;
380
381
/*
382
* Dynamically allocate space for a struct item plus the key and data
383
* elements. Fills in group names from the groups filter.
384
*/
385
struct cache_item *
386
cvtsudoers_make_grlist_item(const struct passwd *pw, char * const *unused1)
387
{
388
char *cp;
389
size_t nsize, ngroups, total, len;
390
struct cache_item_grlist *grlitem;
391
struct sudoers_string *s;
392
struct group_list *grlist;
393
const size_t groupname_len = sudo_login_name_max();
394
debug_decl(cvtsudoers_make_grlist_item, SUDOERS_DEBUG_NSS);
395
396
/*
397
* There's only a single group list.
398
*/
399
if (grlist_item != NULL) {
400
grlist_item->cache.refcnt++;
401
debug_return_ptr(&grlist_item->cache);
402
}
403
404
/* Count number of groups in the filter. */
405
ngroups = 0;
406
STAILQ_FOREACH(s, &filters->groups, entries) {
407
ngroups++;
408
}
409
410
/* Allocate in one big chunk for easy freeing. */
411
nsize = strlen(pw->pw_name) + 1;
412
total = sizeof(*grlitem) + nsize;
413
total += groupname_len * ngroups;
414
415
again:
416
if ((grlitem = calloc(1, total)) == NULL) {
417
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
418
"unable to allocate memory");
419
debug_return_ptr(NULL);
420
}
421
422
/*
423
* Copy in group list and make pointers relative to space
424
* at the end of the buffer. Note that the groups array must come
425
* immediately after struct group to guarantee proper alignment.
426
*/
427
grlist = &grlitem->grlist;
428
cp = (char *)(grlitem + 1);
429
grlist->groups = (char **)cp;
430
cp += sizeof(char *) * ngroups;
431
432
/* Set key and datum. */
433
memcpy(cp, pw->pw_name, nsize);
434
grlitem->cache.k.name = cp;
435
grlitem->cache.d.grlist = grlist;
436
grlitem->cache.refcnt = 1;
437
cp += nsize;
438
439
/*
440
* Copy groups from filter.
441
*/
442
ngroups = 0;
443
STAILQ_FOREACH(s, &filters->groups, entries) {
444
if (s->str[0] == '#') {
445
const char *errstr;
446
sudo_strtoid(s->str + 1, &errstr);
447
if (errstr == NULL) {
448
/* Group ID not name, ignore it. */
449
continue;
450
}
451
}
452
len = strlen(s->str) + 1;
453
if ((size_t)(cp - (char *)grlitem) + len > total) {
454
total += len + groupname_len;
455
free(grlitem);
456
goto again;
457
}
458
memcpy(cp, s->str, len);
459
grlist->groups[ngroups++] = cp;
460
cp += len;
461
}
462
grlist->ngroups = (int)ngroups;
463
464
debug_return_ptr(&grlitem->cache);
465
}
466
467