Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/sudo.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2009-2024 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#ifdef __TANDEM
20
# include <floss.h>
21
#endif
22
23
#include <config.h>
24
25
#include <sys/types.h>
26
#include <sys/stat.h>
27
#include <sys/wait.h>
28
#include <sys/resource.h>
29
#include <sys/socket.h>
30
#ifdef __linux__
31
# include <sys/prctl.h>
32
#endif
33
#include <stddef.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
#include <ctype.h>
39
#include <errno.h>
40
#include <fcntl.h>
41
#include <limits.h>
42
#include <signal.h>
43
#include <grp.h>
44
#include <pwd.h>
45
#include <time.h>
46
#ifdef HAVE_SELINUX
47
# include <selinux/selinux.h> /* for is_selinux_enabled() */
48
#endif
49
#ifdef HAVE_SETAUTHDB
50
# include <usersec.h>
51
#endif /* HAVE_SETAUTHDB */
52
#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
53
# ifdef __hpux
54
# undef MAXINT
55
# include <hpsecurity.h>
56
# else
57
# include <sys/security.h>
58
# endif /* __hpux */
59
# include <prot.h>
60
#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
61
62
#include <sudo.h>
63
#include <sudo_plugin.h>
64
#include <sudo_plugin_int.h>
65
66
/*
67
* Local variables
68
*/
69
struct plugin_container policy_plugin;
70
struct plugin_container_list io_plugins = TAILQ_HEAD_INITIALIZER(io_plugins);
71
struct plugin_container_list audit_plugins = TAILQ_HEAD_INITIALIZER(audit_plugins);
72
struct plugin_container_list approval_plugins = TAILQ_HEAD_INITIALIZER(approval_plugins);
73
int sudo_debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
74
static struct sudo_event_base *sudo_event_base;
75
76
struct sudo_gc_entry {
77
SLIST_ENTRY(sudo_gc_entry) entries;
78
enum sudo_gc_types type;
79
union {
80
char **vec;
81
void *ptr;
82
} u;
83
};
84
SLIST_HEAD(sudo_gc_list, sudo_gc_entry);
85
#ifdef NO_LEAKS
86
static struct sudo_gc_list sudo_gc_list = SLIST_HEAD_INITIALIZER(sudo_gc_list);
87
#endif
88
89
/*
90
* Local functions
91
*/
92
static void fix_fds(void);
93
static void sudo_check_suid(const char *path);
94
static char **get_user_info(struct user_details *);
95
static void command_info_to_details(char * const info[],
96
struct command_details *details);
97
static void gc_init(void);
98
99
/* Policy plugin convenience functions. */
100
static void policy_open(void);
101
static void policy_close(const char *cmnd, int exit_status, int error);
102
static int policy_show_version(int verbose);
103
static bool policy_check(int argc, char * const argv[], char *env_add[],
104
char **command_info[], char **run_argv[], char **run_envp[]);
105
static void policy_list(int argc, char * const argv[],
106
int verbose, const char *user);
107
static void policy_validate(char * const argv[]);
108
static void policy_invalidate(int unlinkit);
109
110
/* I/O log plugin convenience functions. */
111
static bool iolog_open(char * const command_info[], int run_argc,
112
char * const run_argv[], char * const run_envp[]);
113
static void iolog_close(int exit_status, int error);
114
static void iolog_show_version(int verbose, int argc, char * const argv[],
115
char * const envp[]);
116
static void unlink_plugin(struct plugin_container_list *plugin_list, struct plugin_container *plugin);
117
static void free_plugin_container(struct plugin_container *plugin, bool ioplugin);
118
119
/* Audit plugin convenience functions (some are public). */
120
static void audit_open(void);
121
static void audit_close(int exit_status, int error);
122
static void audit_show_version(int verbose);
123
124
/* Approval plugin convenience functions (some are public). */
125
static void approval_show_version(int verbose);
126
127
sudo_dso_public int main(int argc, char *argv[], char *envp[]);
128
129
static struct sudo_settings *sudo_settings;
130
static char * const *user_info, * const *submit_argv, * const *submit_envp;
131
static int submit_optind;
132
133
int
134
main(int argc, char *argv[], char *envp[])
135
{
136
struct command_details command_details;
137
struct user_details user_details;
138
unsigned int sudo_mode;
139
int nargc, status = 0;
140
char **nargv, **env_add;
141
char **command_info = NULL, **argv_out = NULL, **run_envp = NULL;
142
const char * const allowed_prognames[] = { "sudo", "sudoedit", NULL };
143
const char *list_user;
144
sigset_t mask;
145
debug_decl_vars(main, SUDO_DEBUG_MAIN);
146
147
/* Only allow "sudo" or "sudoedit" as the program name. */
148
initprogname2(argc > 0 ? argv[0] : "sudo", allowed_prognames);
149
150
/* Crank resource limits to unlimited. */
151
unlimit_sudo();
152
153
/* Make sure fds 0-2 are open and do OS-specific initialization. */
154
fix_fds();
155
os_init(argc, argv, envp);
156
157
setlocale(LC_ALL, "");
158
bindtextdomain(PACKAGE_NAME, LOCALEDIR);
159
textdomain(PACKAGE_NAME);
160
161
(void) tzset();
162
163
/* Must be done before we do any password lookups */
164
#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
165
(void) set_auth_parameters(argc, argv);
166
# ifdef HAVE_INITPRIVS
167
initprivs();
168
# endif
169
#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
170
171
/* Initialize the debug subsystem. */
172
if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1)
173
return EXIT_FAILURE;
174
sudo_debug_instance = sudo_debug_register(getprogname(),
175
NULL, NULL, sudo_conf_debug_files(getprogname()), -1);
176
if (sudo_debug_instance == SUDO_DEBUG_INSTANCE_ERROR)
177
return EXIT_FAILURE;
178
179
/* Make sure we are setuid root. */
180
sudo_check_suid(argc > 0 ? argv[0] : "sudo");
181
182
/* Save original signal state and setup default signal handlers. */
183
save_signals();
184
init_signals();
185
186
/* Reset signal mask to the default value (unblock). */
187
(void) sigemptyset(&mask);
188
(void) sigprocmask(SIG_SETMASK, &mask, NULL);
189
190
/* Parse the rest of sudo.conf. */
191
sudo_conf_read(NULL, SUDO_CONF_ALL & ~SUDO_CONF_DEBUG);
192
193
/* Fill in user_info with user name, uid, cwd, etc. */
194
if ((user_info = get_user_info(&user_details)) == NULL)
195
return EXIT_FAILURE; /* get_user_info printed error message */
196
197
/* Disable core dumps if not enabled in sudo.conf. */
198
if (sudo_conf_disable_coredump())
199
disable_coredump();
200
201
/* Parse command line arguments, preserving the original argv/envp. */
202
submit_argv = argv;
203
submit_envp = envp;
204
sudo_mode = parse_args(argc, argv, user_details.shell, &submit_optind,
205
&nargc, &nargv, &sudo_settings, &env_add, &list_user);
206
sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_mode 0x%x", sudo_mode);
207
208
/* Print sudo version early, in case of plugin init failure. */
209
if (ISSET(sudo_mode, MODE_VERSION)) {
210
printf(_("Sudo version %s\n"), PACKAGE_VERSION);
211
if (user_details.cred.uid == ROOT_UID)
212
(void) printf(_("Configure options: %s\n"), CONFIGURE_ARGS);
213
}
214
215
/* Use conversation function for sudo_(warn|fatal)x? for plugins. */
216
sudo_warn_set_conversation(sudo_conversation);
217
218
/* Load plugins. */
219
if (!sudo_load_plugins())
220
sudo_fatalx("%s", U_("fatal error, unable to load plugins"));
221
222
/* Allocate event base so plugin can use it. */
223
if ((sudo_event_base = sudo_ev_base_alloc()) == NULL)
224
sudo_fatalx("%s", U_("unable to allocate memory"));
225
226
/* Open policy and audit plugins. */
227
/* XXX - audit policy_open errors */
228
audit_open();
229
policy_open();
230
231
switch (sudo_mode & MODE_MASK) {
232
case MODE_VERSION:
233
policy_show_version(!user_details.cred.uid);
234
iolog_show_version(!user_details.cred.uid, nargc, nargv,
235
submit_envp);
236
approval_show_version(!user_details.cred.uid);
237
audit_show_version(!user_details.cred.uid);
238
break;
239
case MODE_VALIDATE:
240
case MODE_VALIDATE|MODE_INVALIDATE:
241
policy_validate(nargv);
242
break;
243
case MODE_KILL:
244
case MODE_INVALIDATE:
245
policy_invalidate(sudo_mode == MODE_KILL);
246
break;
247
case MODE_CHECK:
248
case MODE_CHECK|MODE_INVALIDATE:
249
case MODE_LIST:
250
case MODE_LIST|MODE_INVALIDATE:
251
policy_list(nargc, nargv, ISSET(sudo_mode, MODE_LONG_LIST),
252
list_user);
253
break;
254
case MODE_EDIT:
255
case MODE_RUN:
256
if (!policy_check(nargc, nargv, env_add, &command_info, &argv_out,
257
&run_envp))
258
goto access_denied;
259
260
/* Reset nargv/nargc based on argv_out. */
261
/* XXX - leaks old nargv in shell mode */
262
for (nargv = argv_out, nargc = 0; nargv[nargc] != NULL; nargc++)
263
continue;
264
if (nargc == 0)
265
sudo_fatalx("%s",
266
U_("plugin did not return a command to execute"));
267
268
/* Approval plugins run after policy plugin accepts the command. */
269
if (!approval_check(command_info, nargv, run_envp))
270
goto access_denied;
271
272
/* Open I/O plugin once policy and approval plugins succeed. */
273
if (!iolog_open(command_info, nargc, nargv, run_envp))
274
goto access_denied;
275
276
/* Audit the accept event on behalf of the sudo front-end. */
277
if (!audit_accept("sudo", SUDO_FRONT_END, command_info,
278
nargv, run_envp))
279
goto access_denied;
280
281
/* Setup command details and run command/edit. */
282
command_info_to_details(command_info, &command_details);
283
if (command_details.utmp_user == NULL)
284
command_details.utmp_user = user_details.username;
285
command_details.submitcwd = user_details.cwd;
286
command_details.tty = user_details.tty;
287
command_details.argv = nargv;
288
command_details.argc = nargc;
289
command_details.envp = run_envp;
290
if (ISSET(sudo_mode, MODE_LOGIN_SHELL))
291
SET(command_details.flags, CD_LOGIN_SHELL);
292
if (ISSET(sudo_mode, MODE_BACKGROUND))
293
SET(command_details.flags, CD_BACKGROUND);
294
if (ISSET(command_details.flags, CD_SUDOEDIT)) {
295
status = sudo_edit(&command_details, &user_details);
296
} else {
297
status = run_command(&command_details, &user_details);
298
}
299
/* The close method was called by sudo_edit/run_command. */
300
break;
301
default:
302
sudo_fatalx(U_("unexpected sudo mode 0x%x"), sudo_mode);
303
}
304
305
/*
306
* If the command was terminated by a signal, sudo needs to terminated
307
* the same way. Otherwise, the shell may ignore a keyboard-generated
308
* signal. However, we want to avoid having sudo dump core itself.
309
*/
310
if (WIFSIGNALED(status)) {
311
struct sigaction sa;
312
313
/* Make sure sudo doesn't dump core itself. */
314
disable_coredump();
315
316
/* Re-send the signal to the main sudo process. */
317
memset(&sa, 0, sizeof(sa));
318
sigemptyset(&sa.sa_mask);
319
sa.sa_handler = SIG_DFL;
320
sigaction(WTERMSIG(status), &sa, NULL);
321
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
322
WTERMSIG(status) | 128);
323
kill(getpid(), WTERMSIG(status));
324
}
325
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
326
WEXITSTATUS(status));
327
return WEXITSTATUS(status);
328
329
access_denied:
330
/* Policy/approval failure, close policy and audit plugins before exit. */
331
if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15))
332
policy_close(NULL, 0, EACCES);
333
audit_close(SUDO_PLUGIN_NO_STATUS, 0);
334
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys,
335
EXIT_FAILURE);
336
return EXIT_FAILURE;
337
}
338
339
int
340
os_init_common(int argc, char *argv[], char *envp[])
341
{
342
#ifdef STATIC_SUDOERS_PLUGIN
343
preload_static_symbols();
344
#endif
345
gc_init();
346
return 0;
347
}
348
349
/*
350
* Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
351
* Some operating systems do this automatically in the kernel or libc.
352
*/
353
static void
354
fix_fds(void)
355
{
356
int miss[3];
357
debug_decl(fix_fds, SUDO_DEBUG_UTIL);
358
359
/*
360
* stdin, stdout and stderr must be open; set them to /dev/null
361
* if they are closed.
362
*/
363
miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
364
miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
365
miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
366
if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
367
int devnull =
368
open(_PATH_DEVNULL, O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
369
if (devnull == -1)
370
sudo_fatal(U_("unable to open %s"), _PATH_DEVNULL);
371
if (miss[STDIN_FILENO] && dup2(devnull, STDIN_FILENO) == -1)
372
sudo_fatal("dup2");
373
if (miss[STDOUT_FILENO] && dup2(devnull, STDOUT_FILENO) == -1)
374
sudo_fatal("dup2");
375
if (miss[STDERR_FILENO] && dup2(devnull, STDERR_FILENO) == -1)
376
sudo_fatal("dup2");
377
if (devnull > STDERR_FILENO)
378
close(devnull);
379
}
380
debug_return;
381
}
382
383
/*
384
* Allocate space for groups and fill in using sudo_getgrouplist2()
385
* for when we cannot (or don't want to) use getgroups().
386
* Returns 0 on success and -1 on failure.
387
*/
388
static int
389
fill_group_list(const char *user, struct sudo_cred *cred)
390
{
391
int ret = -1;
392
debug_decl(fill_group_list, SUDO_DEBUG_UTIL);
393
394
/*
395
* If user specified a max number of groups, use it, otherwise let
396
* sudo_getgrouplist2() allocate the group vector.
397
*/
398
cred->ngroups = sudo_conf_max_groups();
399
if (cred->ngroups > 0) {
400
cred->groups =
401
reallocarray(NULL, (size_t)cred->ngroups, sizeof(GETGROUPS_T));
402
if (cred->groups != NULL) {
403
/* Clamp to max_groups if insufficient space for all groups. */
404
if (sudo_getgrouplist2(user, cred->gid, &cred->groups,
405
&cred->ngroups) == -1) {
406
cred->ngroups = sudo_conf_max_groups();
407
}
408
ret = 0;
409
}
410
} else {
411
cred->groups = NULL;
412
ret = sudo_getgrouplist2(user, cred->gid, &cred->groups,
413
&cred->ngroups);
414
}
415
if (ret == -1) {
416
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
417
"%s: %s: unable to get groups via sudo_getgrouplist2()",
418
__func__, user);
419
} else {
420
sudo_debug_printf(SUDO_DEBUG_INFO,
421
"%s: %s: got %d groups via sudo_getgrouplist2()",
422
__func__, user, cred->ngroups);
423
}
424
debug_return_int(ret);
425
}
426
427
static char *
428
get_user_groups(const char *user, struct sudo_cred *cred)
429
{
430
char *cp, *gid_list = NULL;
431
size_t glsize;
432
int i, len, group_source;
433
debug_decl(get_user_groups, SUDO_DEBUG_UTIL);
434
435
cred->groups = NULL;
436
group_source = sudo_conf_group_source();
437
if (group_source != GROUP_SOURCE_DYNAMIC) {
438
long maxgroups = sysconf(_SC_NGROUPS_MAX);
439
if (maxgroups < 0)
440
maxgroups = NGROUPS_MAX;
441
442
/* Note that macOS may return ngroups > NGROUPS_MAX. */
443
cred->ngroups = getgroups(0, NULL); // -V575
444
if (cred->ngroups > 0) {
445
/* Use groups from kernel if not at limit or source is static. */
446
if (cred->ngroups != maxgroups || group_source == GROUP_SOURCE_STATIC) {
447
cred->groups = reallocarray(NULL, (size_t)cred->ngroups,
448
sizeof(GETGROUPS_T));
449
if (cred->groups == NULL)
450
goto done;
451
cred->ngroups = getgroups(cred->ngroups, cred->groups);
452
if (cred->ngroups < 0) {
453
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO,
454
"%s: unable to get %d groups via getgroups()",
455
__func__, cred->ngroups);
456
free(cred->groups);
457
cred->groups = NULL;
458
} else {
459
sudo_debug_printf(SUDO_DEBUG_INFO,
460
"%s: got %d groups via getgroups()",
461
__func__, cred->ngroups);
462
}
463
}
464
}
465
}
466
if (cred->groups == NULL) {
467
/*
468
* Query group database if kernel list is too small or disabled.
469
* Typically, this is because NFS can only support up to 16 groups.
470
*/
471
if (fill_group_list(user, cred) == -1)
472
goto done;
473
}
474
475
/*
476
* Format group list as a comma-separated string of gids.
477
*/
478
glsize = sizeof("groups=") - 1 +
479
((size_t)cred->ngroups * (STRLEN_MAX_UNSIGNED(gid_t) + 1));
480
if ((gid_list = malloc(glsize)) == NULL)
481
goto done;
482
memcpy(gid_list, "groups=", sizeof("groups=") - 1);
483
cp = gid_list + sizeof("groups=") - 1;
484
glsize -= (size_t)(cp - gid_list);
485
for (i = 0; i < cred->ngroups; i++) {
486
len = snprintf(cp, glsize, "%s%u", i ? "," : "",
487
(unsigned int)cred->groups[i]);
488
if (len < 0 || (size_t)len >= glsize)
489
sudo_fatalx(U_("internal error, %s overflow"), __func__);
490
cp += len;
491
glsize -= (size_t)len;
492
}
493
done:
494
debug_return_str(gid_list);
495
}
496
497
/*
498
* Return user information as an array of name=value pairs.
499
* and fill in struct user_details (which shares the same strings).
500
*/
501
static char **
502
get_user_info(struct user_details *ud)
503
{
504
char *cp, **info, path[PATH_MAX];
505
size_t info_max = 33 + RLIM_NLIMITS;
506
size_t i = 0, n;
507
dev_t ttydev;
508
mode_t mask;
509
struct passwd *pw;
510
int ttyfd;
511
debug_decl(get_user_info, SUDO_DEBUG_UTIL);
512
513
/*
514
* On BSD systems you can set a hint to keep the password and
515
* group databases open instead of having to open and close
516
* them all the time. Since sudo does a lot of password and
517
* group lookups, keeping the file open can speed things up.
518
*/
519
#ifdef HAVE_SETPASSENT
520
setpassent(1);
521
#endif /* HAVE_SETPASSENT */
522
#ifdef HAVE_SETGROUPENT
523
setgroupent(1);
524
#endif /* HAVE_SETGROUPENT */
525
526
memset(ud, 0, sizeof(*ud));
527
528
/* XXX - bound check number of entries */
529
info = reallocarray(NULL, info_max, sizeof(char *));
530
if (info == NULL)
531
goto oom;
532
533
ud->pid = getpid();
534
ud->ppid = getppid();
535
ud->pgid = getpgrp();
536
ttyfd = open(_PATH_TTY, O_RDWR);
537
sudo_get_ttysize(ttyfd, &ud->ts_rows, &ud->ts_cols);
538
if (ttyfd != -1) {
539
if ((ud->tcpgid = tcgetpgrp(ttyfd)) == -1)
540
ud->tcpgid = 0;
541
close(ttyfd);
542
}
543
if ((ud->sid = getsid(0)) == -1)
544
ud->sid = 0;
545
546
ud->cred.uid = getuid();
547
ud->cred.euid = geteuid();
548
ud->cred.gid = getgid();
549
ud->cred.egid = getegid();
550
551
/* Store cred for use by sudo_askpass(). */
552
sudo_askpass_cred(&ud->cred);
553
554
#ifdef HAVE_SETAUTHDB
555
aix_setauthdb(IDtouser(ud->cred.uid), NULL);
556
#endif
557
pw = getpwuid(ud->cred.uid);
558
#ifdef HAVE_SETAUTHDB
559
aix_restoreauthdb();
560
#endif
561
if (pw == NULL)
562
sudo_fatalx(U_("you do not exist in the %s database"), "passwd");
563
564
info[i] = sudo_new_key_val("user", pw->pw_name);
565
if (info[i] == NULL)
566
goto oom;
567
ud->username = info[i] + sizeof("user=") - 1;
568
569
/* Stash user's shell for use with the -s flag; don't pass to plugin. */
570
if ((ud->shell = getenv("SHELL")) == NULL || ud->shell[0] == '\0') {
571
ud->shell = pw->pw_shell[0] ? pw->pw_shell : _PATH_SUDO_BSHELL;
572
}
573
if ((cp = strdup(ud->shell)) == NULL)
574
goto oom;
575
if (!gc_add(GC_PTR, cp))
576
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
577
ud->shell = cp;
578
579
if (asprintf(&info[++i], "pid=%d", (int)ud->pid) == -1)
580
goto oom;
581
if (asprintf(&info[++i], "ppid=%d", (int)ud->ppid) == -1)
582
goto oom;
583
if (asprintf(&info[++i], "pgid=%d", (int)ud->pgid) == -1)
584
goto oom;
585
if (asprintf(&info[++i], "tcpgid=%d", (int)ud->tcpgid) == -1)
586
goto oom;
587
if (asprintf(&info[++i], "sid=%d", (int)ud->sid) == -1)
588
goto oom;
589
if (asprintf(&info[++i], "uid=%u", (unsigned int)ud->cred.uid) == -1)
590
goto oom;
591
if (asprintf(&info[++i], "euid=%u", (unsigned int)ud->cred.euid) == -1)
592
goto oom;
593
if (asprintf(&info[++i], "gid=%u", (unsigned int)ud->cred.gid) == -1)
594
goto oom;
595
if (asprintf(&info[++i], "egid=%u", (unsigned int)ud->cred.egid) == -1)
596
goto oom;
597
598
if ((cp = get_user_groups(ud->username, &ud->cred)) == NULL)
599
goto oom;
600
info[++i] = cp;
601
if (!gc_add(GC_PTR, ud->cred.groups))
602
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
603
604
mask = umask(0);
605
umask(mask);
606
if (asprintf(&info[++i], "umask=0%o", (unsigned int)mask) == -1)
607
goto oom;
608
609
if (getcwd(path, sizeof(path)) != NULL) {
610
info[++i] = sudo_new_key_val("cwd", path);
611
if (info[i] == NULL)
612
goto oom;
613
ud->cwd = info[i] + sizeof("cwd=") - 1;
614
}
615
616
ttydev = get_process_ttyname(path, sizeof(path));
617
if (ttydev != NODEV) {
618
if (asprintf(&info[++i], "ttydev=%lld", (long long)ttydev) == -1)
619
goto oom;
620
/* The terminal device file may be missing in a chroot() jail. */
621
if (path[0] != '\0') {
622
info[++i] = sudo_new_key_val("tty", path);
623
if (info[i] == NULL)
624
goto oom;
625
ud->tty = info[i] + sizeof("tty=") - 1;
626
}
627
} else {
628
/* tty may not always be present */
629
if (errno != ENOENT)
630
sudo_warn("%s", U_("unable to determine tty"));
631
}
632
633
cp = sudo_gethostname();
634
info[++i] = sudo_new_key_val("host", cp ? cp : "localhost");
635
free(cp);
636
if (info[i] == NULL)
637
goto oom;
638
ud->host = info[i] + sizeof("host=") - 1;
639
640
if (asprintf(&info[++i], "lines=%d", ud->ts_rows) == -1)
641
goto oom;
642
if (asprintf(&info[++i], "cols=%d", ud->ts_cols) == -1)
643
goto oom;
644
645
n = serialize_rlimits(&info[i + 1], info_max - (i + 1));
646
if (n == (size_t)-1)
647
goto oom;
648
i += n;
649
650
info[++i] = NULL;
651
652
/* Add to list of vectors to be garbage collected at exit. */
653
if (!gc_add(GC_VECTOR, info))
654
goto bad;
655
656
debug_return_ptr(info);
657
oom:
658
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
659
bad:
660
while (i)
661
free(info[--i]);
662
free(info);
663
debug_return_ptr(NULL);
664
}
665
666
/*
667
* Convert a command_info array into a command_details structure.
668
*/
669
static void
670
command_info_to_details(char * const info[], struct command_details *details)
671
{
672
const char *errstr;
673
char *cp;
674
id_t id;
675
size_t i;
676
debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM);
677
678
memset(details, 0, sizeof(*details));
679
details->info = info;
680
details->closefrom = -1;
681
details->execfd = -1;
682
details->flags = CD_SUDOEDIT_CHECKDIR | CD_SET_GROUPS;
683
TAILQ_INIT(&details->preserved_fds);
684
685
#define SET_STRING(s, n) \
686
if (strncmp(s, info[i], sizeof(s) - 1) == 0 && info[i][sizeof(s) - 1]) { \
687
details->n = info[i] + sizeof(s) - 1; \
688
break; \
689
}
690
#define SET_FLAG(s, n) \
691
if (strncmp(s, info[i], sizeof(s) - 1) == 0) { \
692
switch (sudo_strtobool(info[i] + sizeof(s) - 1)) { \
693
case true: \
694
SET(details->flags, n); \
695
break; \
696
case false: \
697
CLR(details->flags, n); \
698
break; \
699
default: \
700
sudo_debug_printf(SUDO_DEBUG_ERROR, \
701
"invalid boolean value for %s", info[i]); \
702
break; \
703
} \
704
break; \
705
}
706
707
sudo_debug_printf(SUDO_DEBUG_INFO, "command info from plugin:");
708
for (i = 0; info[i] != NULL; i++) {
709
sudo_debug_printf(SUDO_DEBUG_INFO, " %zu: %s", i, info[i]);
710
switch (info[i][0]) {
711
case 'a':
712
SET_STRING("apparmor_profile=", apparmor_profile);
713
break;
714
case 'c':
715
SET_STRING("chroot=", chroot)
716
SET_STRING("command=", command)
717
SET_STRING("cwd=", runcwd)
718
SET_FLAG("cwd_optional=", CD_CWD_OPTIONAL)
719
if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) {
720
cp = info[i] + sizeof("closefrom=") - 1;
721
details->closefrom =
722
(int)sudo_strtonum(cp, 0, INT_MAX, &errstr);
723
if (errstr != NULL)
724
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
725
break;
726
}
727
break;
728
case 'e':
729
SET_FLAG("exec_background=", CD_EXEC_BG)
730
if (strncmp("execfd=", info[i], sizeof("execfd=") - 1) == 0) {
731
cp = info[i] + sizeof("execfd=") - 1;
732
details->execfd =
733
(int)sudo_strtonum(cp, 0, INT_MAX, &errstr);
734
if (errstr != NULL)
735
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
736
#ifdef HAVE_FEXECVE
737
/* Must keep fd open during exec. */
738
add_preserved_fd(&details->preserved_fds, details->execfd);
739
SET(details->flags, CD_FEXECVE);
740
#else
741
/* Plugin thinks we support fexecve() but we don't. */
742
(void)fcntl(details->execfd, F_SETFD, FD_CLOEXEC);
743
details->execfd = -1;
744
#endif
745
break;
746
}
747
break;
748
case 'i':
749
SET_FLAG("intercept=", CD_INTERCEPT)
750
SET_FLAG("intercept_verify=", CD_INTERCEPT_VERIFY)
751
break;
752
case 'l':
753
SET_STRING("login_class=", login_class)
754
SET_FLAG("log_subcmds=", CD_LOG_SUBCMDS)
755
break;
756
case 'n':
757
if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
758
cp = info[i] + sizeof("nice=") - 1;
759
details->priority = (int)sudo_strtonum(cp, INT_MIN, INT_MAX,
760
&errstr);
761
if (errstr != NULL)
762
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
763
SET(details->flags, CD_SET_PRIORITY);
764
break;
765
}
766
SET_FLAG("noexec=", CD_NOEXEC)
767
break;
768
case 'p':
769
SET_FLAG("preserve_groups=", CD_PRESERVE_GROUPS)
770
if (strncmp("preserve_fds=", info[i], sizeof("preserve_fds=") - 1) == 0) {
771
parse_preserved_fds(&details->preserved_fds,
772
info[i] + sizeof("preserve_fds=") - 1);
773
break;
774
}
775
break;
776
case 'r':
777
if (strncmp("rlimit_", info[i], sizeof("rlimit_") - 1) == 0) {
778
parse_policy_rlimit(info[i] + sizeof("rlimit_") - 1);
779
break;
780
}
781
if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
782
cp = info[i] + sizeof("runas_egid=") - 1;
783
id = sudo_strtoid(cp, &errstr);
784
if (errstr != NULL)
785
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
786
details->cred.egid = (gid_t)id;
787
SET(details->flags, CD_SET_EGID);
788
break;
789
}
790
if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
791
cp = info[i] + sizeof("runas_euid=") - 1;
792
id = sudo_strtoid(cp, &errstr);
793
if (errstr != NULL)
794
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
795
details->cred.euid = (uid_t)id;
796
SET(details->flags, CD_SET_EUID);
797
break;
798
}
799
if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
800
cp = info[i] + sizeof("runas_gid=") - 1;
801
id = sudo_strtoid(cp, &errstr);
802
if (errstr != NULL)
803
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
804
details->cred.gid = (gid_t)id;
805
SET(details->flags, CD_SET_GID);
806
break;
807
}
808
if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
809
cp = info[i] + sizeof("runas_groups=") - 1;
810
details->cred.ngroups = sudo_parse_gids(cp, NULL,
811
&details->cred.groups);
812
/* sudo_parse_gids() will print a warning on error. */
813
if (details->cred.ngroups == -1)
814
exit(EXIT_FAILURE); /* XXX */
815
if (!gc_add(GC_PTR, details->cred.groups)) {
816
sudo_fatalx(U_("%s: %s"), __func__,
817
U_("unable to allocate memory"));
818
}
819
break;
820
}
821
if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
822
cp = info[i] + sizeof("runas_uid=") - 1;
823
id = sudo_strtoid(cp, &errstr);
824
if (errstr != NULL)
825
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
826
details->cred.uid = (uid_t)id;
827
SET(details->flags, CD_SET_UID);
828
break;
829
}
830
#ifdef HAVE_PRIV_SET
831
if (strncmp("runas_privs=", info[i], sizeof("runas_privs=") - 1) == 0) {
832
const char *endp;
833
cp = info[i] + sizeof("runas_privs=") - 1;
834
if (*cp != '\0') {
835
details->privs = priv_str_to_set(cp, ",", &endp);
836
if (details->privs == NULL)
837
sudo_warn("invalid runas_privs %s", endp);
838
}
839
break;
840
}
841
if (strncmp("runas_limitprivs=", info[i], sizeof("runas_limitprivs=") - 1) == 0) {
842
const char *endp;
843
cp = info[i] + sizeof("runas_limitprivs=") - 1;
844
if (*cp != '\0') {
845
details->limitprivs = priv_str_to_set(cp, ",", &endp);
846
if (details->limitprivs == NULL)
847
sudo_warn("invalid runas_limitprivs %s", endp);
848
}
849
break;
850
}
851
#endif /* HAVE_PRIV_SET */
852
SET_STRING("runas_user=", runas_user)
853
break;
854
case 's':
855
SET_STRING("selinux_role=", selinux_role)
856
SET_STRING("selinux_type=", selinux_type)
857
SET_FLAG("set_utmp=", CD_SET_UTMP)
858
SET_FLAG("sudoedit=", CD_SUDOEDIT)
859
SET_FLAG("sudoedit_checkdir=", CD_SUDOEDIT_CHECKDIR)
860
SET_FLAG("sudoedit_follow=", CD_SUDOEDIT_FOLLOW)
861
if (strncmp("sudoedit_nfiles=", info[i], sizeof("sudoedit_nfiles=") - 1) == 0) {
862
cp = info[i] + sizeof("sudoedit_nfiles=") - 1;
863
details->nfiles = (int)sudo_strtonum(cp, 1, INT_MAX,
864
&errstr);
865
if (errstr != NULL)
866
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
867
break;
868
}
869
break;
870
case 't':
871
if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
872
cp = info[i] + sizeof("timeout=") - 1;
873
details->timeout =
874
(unsigned int)sudo_strtonum(cp, 0, UINT_MAX, &errstr);
875
if (errstr != NULL)
876
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
877
SET(details->flags, CD_SET_TIMEOUT);
878
break;
879
}
880
break;
881
case 'u':
882
if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
883
cp = info[i] + sizeof("umask=") - 1;
884
details->umask = sudo_strtomode(cp, &errstr);
885
if (errstr != NULL)
886
sudo_fatalx(U_("%s: %s"), info[i], U_(errstr));
887
SET(details->flags, CD_SET_UMASK);
888
break;
889
}
890
SET_FLAG("umask_override=", CD_OVERRIDE_UMASK)
891
SET_FLAG("use_ptrace=", CD_USE_PTRACE)
892
SET_FLAG("use_pty=", CD_USE_PTY)
893
SET_STRING("utmp_user=", utmp_user)
894
break;
895
}
896
}
897
898
/* Only use ptrace(2) for intercept/log_subcmds if supported. */
899
exec_ptrace_fix_flags(details);
900
901
if (!ISSET(details->flags, CD_SET_EUID))
902
details->cred.euid = details->cred.uid;
903
if (!ISSET(details->flags, CD_SET_EGID))
904
details->cred.egid = details->cred.gid;
905
if (!ISSET(details->flags, CD_SET_UMASK))
906
CLR(details->flags, CD_OVERRIDE_UMASK);
907
908
#ifdef HAVE_SETAUTHDB
909
aix_setauthdb(IDtouser(details->cred.euid), NULL);
910
#endif
911
if (details->runas_user != NULL)
912
details->pw = getpwnam(details->runas_user);
913
if (details->pw == NULL)
914
details->pw = getpwuid(details->cred.euid);
915
#ifdef HAVE_SETAUTHDB
916
aix_restoreauthdb();
917
#endif
918
if (details->pw != NULL && (details->pw = pw_dup(details->pw)) == NULL)
919
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
920
if (!gc_add(GC_PTR, details->pw))
921
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
922
923
#ifdef HAVE_SELINUX
924
if (details->selinux_role != NULL && is_selinux_enabled() > 0) {
925
SET(details->flags, CD_RBAC_ENABLED);
926
if (selinux_getexeccon(details->selinux_role, details->selinux_type) != 0)
927
exit(EXIT_FAILURE);
928
}
929
#endif
930
931
#ifdef HAVE_APPARMOR
932
if (details->apparmor_profile != NULL && apparmor_is_enabled()) {
933
if (apparmor_prepare(details->apparmor_profile) != 0)
934
exit(EXIT_FAILURE);
935
}
936
#endif
937
938
debug_return;
939
}
940
941
static void
942
sudo_check_suid(const char *sudo)
943
{
944
char pathbuf[PATH_MAX];
945
struct stat sb;
946
bool qualified;
947
debug_decl(sudo_check_suid, SUDO_DEBUG_PCOMM);
948
949
if (geteuid() != ROOT_UID) {
950
#if defined(__linux__) && defined(PR_GET_NO_NEW_PRIVS)
951
/* The no_new_privs flag disables set-user-ID at execve(2) time. */
952
if (prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
953
sudo_warnx("%s", U_("The \"no new privileges\" flag is set, which "
954
"prevents sudo from running as root."));
955
sudo_warnx("%s", U_("If sudo is running in a container, you may need"
956
" to adjust the container configuration to disable the flag."));
957
exit(EXIT_FAILURE);
958
}
959
#endif /* __linux__ && PR_GET_NO_NEW_PRIVS */
960
961
/* Search for sudo binary in PATH if not fully qualified. */
962
qualified = strchr(sudo, '/') != NULL;
963
if (!qualified) {
964
char *path = getenv_unhooked("PATH");
965
if (path != NULL) {
966
const char *cp, *ep;
967
const char *pathend = path + strlen(path);
968
969
for (cp = sudo_strsplit(path, pathend, ":", &ep); cp != NULL;
970
cp = sudo_strsplit(NULL, pathend, ":", &ep)) {
971
972
int len = snprintf(pathbuf, sizeof(pathbuf), "%.*s/%s",
973
(int)(ep - cp), cp, sudo);
974
if (len < 0 || len >= ssizeof(pathbuf))
975
continue;
976
if (access(pathbuf, X_OK) == 0) {
977
sudo = pathbuf;
978
qualified = true;
979
break;
980
}
981
}
982
}
983
}
984
985
if (qualified && stat(sudo, &sb) == 0) {
986
/* Try to determine why sudo was not running as root. */
987
if (sb.st_uid != ROOT_UID || !ISSET(sb.st_mode, S_ISUID)) {
988
sudo_fatalx(
989
U_("%s must be owned by uid %d and have the setuid bit set"),
990
sudo, ROOT_UID);
991
} else {
992
sudo_fatalx(U_("effective uid is not %d, is %s on a file system "
993
"with the 'nosuid' option set or an NFS file system without"
994
" root privileges?"), ROOT_UID, sudo);
995
}
996
} else {
997
sudo_fatalx(
998
U_("effective uid is not %d, is sudo installed setuid root?"),
999
ROOT_UID);
1000
}
1001
}
1002
debug_return;
1003
}
1004
1005
bool
1006
set_user_groups(struct command_details *details)
1007
{
1008
bool ret = false;
1009
debug_decl(set_user_groups, SUDO_DEBUG_EXEC);
1010
1011
if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
1012
if (details->cred.ngroups >= 0) {
1013
if (sudo_setgroups(details->cred.ngroups, details->cred.groups) < 0) {
1014
sudo_warn("%s", U_("unable to set supplementary group IDs"));
1015
goto done;
1016
}
1017
}
1018
}
1019
#ifdef HAVE_SETEUID
1020
if (ISSET(details->flags, CD_SET_EGID) && setegid(details->cred.egid)) {
1021
sudo_warn(U_("unable to set effective gid to runas gid %u"),
1022
(unsigned int)details->cred.egid);
1023
goto done;
1024
}
1025
#endif
1026
if (ISSET(details->flags, CD_SET_GID) && setgid(details->cred.gid)) {
1027
sudo_warn(U_("unable to set gid to runas gid %u"),
1028
(unsigned int)details->cred.gid);
1029
goto done;
1030
}
1031
ret = true;
1032
1033
done:
1034
CLR(details->flags, CD_SET_GROUPS);
1035
debug_return_bool(ret);
1036
}
1037
1038
/*
1039
* Run the command and wait for it to complete.
1040
* Returns wait status suitable for use with the wait(2) macros.
1041
*/
1042
int
1043
run_command(struct command_details *command_details,
1044
const struct user_details *user_details)
1045
{
1046
struct command_status cstat;
1047
int status = W_EXITCODE(1, 0);
1048
debug_decl(run_command, SUDO_DEBUG_EXEC);
1049
1050
cstat.type = CMD_INVALID;
1051
cstat.val = 0;
1052
1053
if (command_details->command == NULL) {
1054
sudo_warnx("%s", U_("command not set by the security policy"));
1055
debug_return_int(status);
1056
}
1057
if (command_details->argv == NULL) {
1058
sudo_warnx("%s", U_("argv not set by the security policy"));
1059
debug_return_int(status);
1060
}
1061
if (command_details->envp == NULL) {
1062
sudo_warnx("%s", U_("envp not set by the security policy"));
1063
debug_return_int(status);
1064
}
1065
1066
sudo_execute(command_details, user_details, sudo_event_base, &cstat);
1067
1068
switch (cstat.type) {
1069
case CMD_ERRNO:
1070
/* exec_setup() or execve() returned an error. */
1071
iolog_close(0, cstat.val);
1072
policy_close(command_details->command, 0, cstat.val);
1073
audit_close(SUDO_PLUGIN_EXEC_ERROR, cstat.val);
1074
break;
1075
case CMD_WSTATUS:
1076
/* Command ran, exited or was killed. */
1077
status = cstat.val;
1078
iolog_close(status, 0);
1079
policy_close(command_details->command, status, 0);
1080
audit_close(SUDO_PLUGIN_WAIT_STATUS, cstat.val);
1081
break;
1082
default:
1083
/* TODO: handle front end error conditions. */
1084
sudo_warnx(U_("unexpected child termination condition: %d"), cstat.type);
1085
break;
1086
}
1087
debug_return_int(status);
1088
}
1089
1090
/*
1091
* Format struct sudo_settings as name=value pairs for the plugin
1092
* to consume. Returns a NULL-terminated plugin-style array of pairs.
1093
*/
1094
static char **
1095
format_plugin_settings(struct plugin_container *plugin)
1096
{
1097
size_t plugin_settings_size;
1098
struct sudo_debug_file *debug_file;
1099
struct sudo_settings *setting;
1100
char **plugin_settings;
1101
size_t i = 0;
1102
debug_decl(format_plugin_settings, SUDO_DEBUG_PCOMM);
1103
1104
/* We update the ticket entry by default. */
1105
if (sudo_settings[ARG_IGNORE_TICKET].value == NULL &&
1106
sudo_settings[ARG_UPDATE_TICKET].value == NULL) {
1107
sudo_settings[ARG_UPDATE_TICKET].value = "true";
1108
}
1109
1110
/* Determine sudo_settings array size (including plugin_path and NULL) */
1111
plugin_settings_size = 2;
1112
for (setting = sudo_settings; setting->name != NULL; setting++)
1113
plugin_settings_size++;
1114
if (plugin->debug_files != NULL) {
1115
TAILQ_FOREACH(debug_file, plugin->debug_files, entries)
1116
plugin_settings_size++;
1117
}
1118
1119
/* Allocate and fill in. */
1120
plugin_settings = reallocarray(NULL, plugin_settings_size, sizeof(char *));
1121
if (plugin_settings == NULL)
1122
goto bad;
1123
plugin_settings[i] = sudo_new_key_val("plugin_path", plugin->path);
1124
if (plugin_settings[i] == NULL)
1125
goto bad;
1126
for (setting = sudo_settings; setting->name != NULL; setting++) {
1127
if (setting->value != NULL) {
1128
sudo_debug_printf(SUDO_DEBUG_INFO, "settings: %s=%s",
1129
setting->name, setting->value);
1130
plugin_settings[++i] =
1131
sudo_new_key_val(setting->name, setting->value);
1132
if (plugin_settings[i] == NULL)
1133
goto bad;
1134
}
1135
}
1136
if (plugin->debug_files != NULL) {
1137
TAILQ_FOREACH(debug_file, plugin->debug_files, entries) {
1138
/* XXX - quote filename? */
1139
if (asprintf(&plugin_settings[++i], "debug_flags=%s %s",
1140
debug_file->debug_file, debug_file->debug_flags) == -1)
1141
goto bad;
1142
}
1143
}
1144
plugin_settings[++i] = NULL;
1145
1146
/* Add to list of vectors to be garbage collected at exit. */
1147
if (!gc_add(GC_VECTOR, plugin_settings))
1148
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1149
1150
debug_return_ptr(plugin_settings);
1151
bad:
1152
while (i)
1153
free(plugin_settings[--i]);
1154
free(plugin_settings);
1155
debug_return_ptr(NULL);
1156
}
1157
1158
static void
1159
policy_open(void)
1160
{
1161
char **plugin_settings;
1162
const char *errstr = NULL;
1163
int ok;
1164
debug_decl(policy_open, SUDO_DEBUG_PCOMM);
1165
1166
/* Convert struct sudo_settings to plugin_settings[] */
1167
plugin_settings = format_plugin_settings(&policy_plugin);
1168
if (plugin_settings == NULL)
1169
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1170
1171
/*
1172
* Backward compatibility for older API versions
1173
*/
1174
sudo_debug_set_active_instance(SUDO_DEBUG_INSTANCE_INITIALIZER);
1175
switch (policy_plugin.u.generic->version) {
1176
case SUDO_API_MKVERSION(1, 0):
1177
case SUDO_API_MKVERSION(1, 1):
1178
ok = policy_plugin.u.policy_1_0->open(policy_plugin.u.io_1_0->version,
1179
sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
1180
user_info, submit_envp);
1181
break;
1182
default:
1183
ok = policy_plugin.u.policy->open(SUDO_API_VERSION, sudo_conversation,
1184
sudo_conversation_printf, plugin_settings, user_info, submit_envp,
1185
policy_plugin.options, &errstr);
1186
}
1187
1188
/* Stash plugin debug instance ID if set in open() function. */
1189
policy_plugin.debug_instance = sudo_debug_get_active_instance();
1190
sudo_debug_set_active_instance(sudo_debug_instance);
1191
1192
if (ok != 1) {
1193
if (ok == -2)
1194
usage();
1195
/* XXX - audit */
1196
sudo_fatalx("%s", U_("unable to initialize policy plugin"));
1197
}
1198
1199
debug_return;
1200
}
1201
1202
static void
1203
policy_close(const char *cmnd, int exit_status, int error_code)
1204
{
1205
debug_decl(policy_close, SUDO_DEBUG_PCOMM);
1206
1207
if (error_code != 0) {
1208
sudo_debug_printf(SUDO_DEBUG_DEBUG,
1209
"%s: calling policy close with errno %d",
1210
policy_plugin.name, error_code);
1211
} else {
1212
sudo_debug_printf(SUDO_DEBUG_DEBUG,
1213
"%s: calling policy close with wait status %d",
1214
policy_plugin.name, exit_status);
1215
}
1216
if (policy_plugin.u.policy->close != NULL) {
1217
sudo_debug_set_active_instance(policy_plugin.debug_instance);
1218
policy_plugin.u.policy->close(exit_status, error_code);
1219
sudo_debug_set_active_instance(sudo_debug_instance);
1220
} else if (error_code != 0) {
1221
if (cmnd != NULL) {
1222
errno = error_code;
1223
sudo_warn(U_("unable to execute %s"), cmnd);
1224
}
1225
}
1226
1227
debug_return;
1228
}
1229
1230
static int
1231
policy_show_version(int verbose)
1232
{
1233
int ret = true;
1234
debug_decl(policy_show_version, SUDO_DEBUG_PCOMM);
1235
1236
sudo_debug_set_active_instance(policy_plugin.debug_instance);
1237
if (policy_plugin.u.policy->show_version != NULL)
1238
ret = policy_plugin.u.policy->show_version(verbose);
1239
if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) {
1240
if (policy_plugin.u.policy->close != NULL)
1241
policy_plugin.u.policy->close(0, 0);
1242
}
1243
sudo_debug_set_active_instance(sudo_debug_instance);
1244
1245
debug_return_int(ret);
1246
}
1247
1248
static bool
1249
policy_check(int argc, char * const argv[], char *env_add[],
1250
char **command_info[], char **run_argv[], char **run_envp[])
1251
{
1252
const char *errstr = NULL;
1253
int ok;
1254
debug_decl(policy_check, SUDO_DEBUG_PCOMM);
1255
1256
if (policy_plugin.u.policy->check_policy == NULL) {
1257
sudo_fatalx(U_("policy plugin %s is missing the \"check_policy\" method"),
1258
policy_plugin.name);
1259
}
1260
sudo_debug_set_active_instance(policy_plugin.debug_instance);
1261
ok = policy_plugin.u.policy->check_policy(argc, argv, env_add,
1262
command_info, run_argv, run_envp, &errstr);
1263
sudo_debug_set_active_instance(sudo_debug_instance);
1264
sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d (%s)",
1265
ok, errstr ? errstr : "");
1266
1267
/* On success, the close method will be called by sudo_edit/run_command. */
1268
if (ok != 1) {
1269
switch (ok) {
1270
case 0:
1271
audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
1272
errstr ? errstr : _("command rejected by policy"),
1273
*command_info);
1274
break;
1275
case -1:
1276
audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1277
errstr ? errstr : _("policy plugin error"),
1278
*command_info);
1279
break;
1280
case -2:
1281
usage();
1282
/* NOTREACHED */
1283
}
1284
debug_return_bool(false);
1285
}
1286
debug_return_bool(audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN,
1287
*command_info, *run_argv, *run_envp));
1288
}
1289
1290
sudo_noreturn static void
1291
policy_list(int argc, char * const argv[], int verbose, const char *user)
1292
{
1293
const char *errstr = NULL;
1294
/* TODO: add list_user */
1295
const char * const command_info[] = {
1296
"command=list",
1297
NULL
1298
};
1299
int ok;
1300
debug_decl(policy_list, SUDO_DEBUG_PCOMM);
1301
1302
if (policy_plugin.u.policy->list == NULL) {
1303
sudo_fatalx(U_("policy plugin %s does not support listing privileges"),
1304
policy_plugin.name);
1305
}
1306
sudo_debug_set_active_instance(policy_plugin.debug_instance);
1307
ok = policy_plugin.u.policy->list(argc, argv, verbose, user, &errstr);
1308
sudo_debug_set_active_instance(sudo_debug_instance);
1309
1310
switch (ok) {
1311
case 1:
1312
audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN,
1313
(char **)command_info, argv, submit_envp);
1314
break;
1315
case 0:
1316
audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
1317
errstr ? errstr : _("command rejected by policy"),
1318
(char **)command_info);
1319
break;
1320
default:
1321
audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1322
errstr ? errstr : _("policy plugin error"),
1323
(char **)command_info);
1324
break;
1325
}
1326
1327
/* Policy must be closed after auditing to avoid use after free. */
1328
if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15))
1329
policy_close(NULL, 0, 0);
1330
audit_close(SUDO_PLUGIN_NO_STATUS, 0);
1331
1332
exit(ok != 1);
1333
}
1334
1335
sudo_noreturn static void
1336
policy_validate(char * const argv[])
1337
{
1338
const char *errstr = NULL;
1339
const char * const command_info[] = {
1340
"command=validate",
1341
NULL
1342
};
1343
int ok = 0;
1344
debug_decl(policy_validate, SUDO_DEBUG_PCOMM);
1345
1346
if (policy_plugin.u.policy->validate == NULL) {
1347
sudo_fatalx(U_("policy plugin %s does not support the -v option"),
1348
policy_plugin.name);
1349
}
1350
sudo_debug_set_active_instance(policy_plugin.debug_instance);
1351
ok = policy_plugin.u.policy->validate(&errstr);
1352
sudo_debug_set_active_instance(sudo_debug_instance);
1353
1354
switch (ok) {
1355
case 1:
1356
audit_accept(policy_plugin.name, SUDO_POLICY_PLUGIN,
1357
(char **)command_info, argv, submit_envp);
1358
break;
1359
case 0:
1360
audit_reject(policy_plugin.name, SUDO_POLICY_PLUGIN,
1361
errstr ? errstr : _("command rejected by policy"),
1362
(char **)command_info);
1363
break;
1364
default:
1365
audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1366
errstr ? errstr : _("policy plugin error"),
1367
(char **)command_info);
1368
break;
1369
}
1370
1371
/* Policy must be closed after auditing to avoid use after free. */
1372
if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15))
1373
policy_close(NULL, 0, 0);
1374
audit_close(SUDO_PLUGIN_NO_STATUS, 0);
1375
1376
exit(ok != 1);
1377
}
1378
1379
sudo_noreturn static void
1380
policy_invalidate(int unlinkit)
1381
{
1382
debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM);
1383
1384
if (policy_plugin.u.policy->invalidate == NULL) {
1385
sudo_fatalx(U_("policy plugin %s does not support the -k/-K options"),
1386
policy_plugin.name);
1387
}
1388
sudo_debug_set_active_instance(policy_plugin.debug_instance);
1389
policy_plugin.u.policy->invalidate(unlinkit);
1390
if (policy_plugin.u.policy->version >= SUDO_API_MKVERSION(1, 15)) {
1391
if (policy_plugin.u.policy->close != NULL)
1392
policy_plugin.u.policy->close(0, 0);
1393
}
1394
sudo_debug_set_active_instance(sudo_debug_instance);
1395
1396
audit_close(SUDO_PLUGIN_NO_STATUS, 0);
1397
1398
exit(EXIT_SUCCESS);
1399
}
1400
1401
int
1402
policy_init_session(struct command_details *details)
1403
{
1404
const char *errstr = NULL;
1405
int ret = true;
1406
debug_decl(policy_init_session, SUDO_DEBUG_PCOMM);
1407
1408
/*
1409
* We set groups, including supplementary group vector,
1410
* as part of the session setup. This allows for dynamic
1411
* groups to be set via pam_group(8) in pam_setcred(3).
1412
*/
1413
if (ISSET(details->flags, CD_SET_GROUPS)) {
1414
/* set_user_groups() prints error message on failure. */
1415
if (!set_user_groups(details))
1416
goto done;
1417
}
1418
1419
/* Session setup may override sudoers umask so set it first. */
1420
if (ISSET(details->flags, CD_SET_UMASK))
1421
(void) umask(details->umask);
1422
1423
if (policy_plugin.u.policy->init_session) {
1424
/*
1425
* Backward compatibility for older API versions
1426
*/
1427
sudo_debug_set_active_instance(policy_plugin.debug_instance);
1428
switch (policy_plugin.u.generic->version) {
1429
case SUDO_API_MKVERSION(1, 0):
1430
case SUDO_API_MKVERSION(1, 1):
1431
ret = policy_plugin.u.policy_1_0->init_session(details->pw);
1432
break;
1433
default:
1434
ret = policy_plugin.u.policy->init_session(details->pw,
1435
&details->envp, &errstr);
1436
}
1437
sudo_debug_set_active_instance(sudo_debug_instance);
1438
if (ret != 1) {
1439
audit_error(policy_plugin.name, SUDO_POLICY_PLUGIN,
1440
errstr ? errstr : _("policy plugin error"),
1441
details->info);
1442
}
1443
}
1444
1445
done:
1446
debug_return_int(ret);
1447
}
1448
1449
static int
1450
iolog_open_int(struct plugin_container *plugin, char * const command_info[],
1451
int argc, char * const argv[], char * const run_envp[], const char **errstr)
1452
{
1453
char **plugin_settings;
1454
int ret;
1455
debug_decl(iolog_open_int, SUDO_DEBUG_PCOMM);
1456
1457
/* Convert struct sudo_settings to plugin_settings[] */
1458
plugin_settings = format_plugin_settings(plugin);
1459
if (plugin_settings == NULL) {
1460
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1461
debug_return_int(-1);
1462
}
1463
1464
/*
1465
* Backward compatibility for older API versions
1466
*/
1467
sudo_debug_set_active_instance(plugin->debug_instance);
1468
switch (plugin->u.generic->version) {
1469
case SUDO_API_MKVERSION(1, 0):
1470
ret = plugin->u.io_1_0->open(plugin->u.io_1_0->version,
1471
sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
1472
user_info, argc, argv, run_envp);
1473
break;
1474
case SUDO_API_MKVERSION(1, 1):
1475
ret = plugin->u.io_1_1->open(plugin->u.io_1_1->version,
1476
sudo_conversation_1_7, sudo_conversation_printf, plugin_settings,
1477
user_info, command_info, argc, argv, run_envp);
1478
break;
1479
default:
1480
ret = plugin->u.io->open(SUDO_API_VERSION, sudo_conversation,
1481
sudo_conversation_printf, plugin_settings, user_info, command_info,
1482
argc, argv, run_envp, plugin->options, errstr);
1483
}
1484
1485
/* Stash plugin debug instance ID if set in open() function. */
1486
plugin->debug_instance = sudo_debug_get_active_instance();
1487
sudo_debug_set_active_instance(sudo_debug_instance);
1488
1489
debug_return_int(ret);
1490
}
1491
1492
static bool
1493
iolog_open(char * const command_info[], int argc, char * const argv[],
1494
char * const run_envp[])
1495
{
1496
struct plugin_container *plugin, *next;
1497
const char *errstr = NULL;
1498
debug_decl(iolog_open, SUDO_DEBUG_PCOMM);
1499
1500
TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) {
1501
int ok = iolog_open_int(plugin, command_info, argc, argv, run_envp,
1502
&errstr);
1503
switch (ok) {
1504
case 1:
1505
break;
1506
case 0:
1507
/* I/O plugin asked to be disabled, remove and free. */
1508
unlink_plugin(&io_plugins, plugin);
1509
break;
1510
case -2:
1511
usage();
1512
/* NOTREACHED */
1513
default:
1514
sudo_warnx(U_("error initializing I/O plugin %s"),
1515
plugin->name);
1516
audit_error(plugin->name, SUDO_IO_PLUGIN,
1517
errstr ? errstr : _("error initializing I/O plugin"),
1518
command_info);
1519
debug_return_bool(false);
1520
}
1521
}
1522
1523
debug_return_bool(true);
1524
}
1525
1526
static void
1527
iolog_close(int exit_status, int error_code)
1528
{
1529
struct plugin_container *plugin;
1530
debug_decl(iolog_close, SUDO_DEBUG_PCOMM);
1531
1532
TAILQ_FOREACH(plugin, &io_plugins, entries) {
1533
if (plugin->u.io->close != NULL) {
1534
if (error_code != 0) {
1535
sudo_debug_printf(SUDO_DEBUG_DEBUG,
1536
"%s: calling I/O close with errno %d",
1537
plugin->name, error_code);
1538
} else {
1539
sudo_debug_printf(SUDO_DEBUG_DEBUG,
1540
"%s: calling I/O close with wait status %d",
1541
plugin->name, exit_status);
1542
}
1543
sudo_debug_set_active_instance(plugin->debug_instance);
1544
plugin->u.io->close(exit_status, error_code);
1545
sudo_debug_set_active_instance(sudo_debug_instance);
1546
}
1547
}
1548
1549
debug_return;
1550
}
1551
1552
static void
1553
iolog_show_version(int verbose, int argc, char * const argv[],
1554
char * const envp[])
1555
{
1556
const char *errstr = NULL;
1557
struct plugin_container *plugin;
1558
debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM);
1559
1560
TAILQ_FOREACH(plugin, &io_plugins, entries) {
1561
int ok = iolog_open_int(plugin, NULL, argc, argv, envp, &errstr);
1562
if (ok != -1) {
1563
sudo_debug_set_active_instance(plugin->debug_instance);
1564
if (plugin->u.io->show_version != NULL) {
1565
/* Return value of show_version currently ignored. */
1566
plugin->u.io->show_version(verbose);
1567
}
1568
if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 15)) {
1569
if (plugin->u.io->close != NULL)
1570
plugin->u.io->close(0, 0);
1571
}
1572
sudo_debug_set_active_instance(sudo_debug_instance);
1573
}
1574
}
1575
1576
debug_return;
1577
}
1578
1579
/*
1580
* Remove the specified plugin from the plugins list.
1581
* Deregisters any hooks before unlinking, then frees the container.
1582
*/
1583
static void
1584
unlink_plugin(struct plugin_container_list *plugin_list,
1585
struct plugin_container *plugin)
1586
{
1587
void (*deregister_hooks)(int , int (*)(struct sudo_hook *)) = NULL;
1588
debug_decl(unlink_plugin, SUDO_DEBUG_PCOMM);
1589
1590
/* Deregister hooks, if any. */
1591
if (plugin->u.generic->version >= SUDO_API_MKVERSION(1, 2)) {
1592
switch (plugin->u.generic->type) {
1593
case SUDO_IO_PLUGIN:
1594
deregister_hooks = plugin->u.io->deregister_hooks;
1595
break;
1596
case SUDO_AUDIT_PLUGIN:
1597
deregister_hooks = plugin->u.audit->deregister_hooks;
1598
break;
1599
default:
1600
sudo_debug_printf(SUDO_DEBUG_ERROR,
1601
"%s: unsupported plugin type %d", __func__,
1602
plugin->u.generic->type);
1603
break;
1604
}
1605
}
1606
if (deregister_hooks != NULL) {
1607
sudo_debug_set_active_instance(plugin->debug_instance);
1608
deregister_hooks(SUDO_HOOK_VERSION, deregister_hook);
1609
sudo_debug_set_active_instance(sudo_debug_instance);
1610
}
1611
1612
/* Remove from plugin list and free. */
1613
TAILQ_REMOVE(plugin_list, plugin, entries);
1614
free_plugin_container(plugin, true);
1615
1616
debug_return;
1617
}
1618
1619
static int
1620
audit_open_int(struct plugin_container *plugin, const char **errstr)
1621
{
1622
char **plugin_settings;
1623
int ret;
1624
debug_decl(audit_open_int, SUDO_DEBUG_PCOMM);
1625
1626
/* Convert struct sudo_settings to plugin_settings[] */
1627
plugin_settings = format_plugin_settings(plugin);
1628
if (plugin_settings == NULL) {
1629
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1630
debug_return_int(-1);
1631
}
1632
1633
sudo_debug_set_active_instance(plugin->debug_instance);
1634
ret = plugin->u.audit->open(SUDO_API_VERSION, sudo_conversation,
1635
sudo_conversation_printf, plugin_settings, user_info,
1636
submit_optind, submit_argv, submit_envp, plugin->options, errstr);
1637
1638
/* Stash plugin debug instance ID if set in open() function. */
1639
plugin->debug_instance = sudo_debug_get_active_instance();
1640
sudo_debug_set_active_instance(sudo_debug_instance);
1641
1642
debug_return_int(ret);
1643
}
1644
1645
static void
1646
audit_open(void)
1647
{
1648
struct plugin_container *plugin, *next;
1649
const char *errstr = NULL;
1650
debug_decl(audit_open, SUDO_DEBUG_PCOMM);
1651
1652
TAILQ_FOREACH_SAFE(plugin, &audit_plugins, entries, next) {
1653
int ok = audit_open_int(plugin, &errstr);
1654
switch (ok) {
1655
case 1:
1656
break;
1657
case 0:
1658
/* Audit plugin asked to be disabled, remove and free. */
1659
unlink_plugin(&audit_plugins, plugin);
1660
break;
1661
case -2:
1662
usage();
1663
/* NOTREACHED */
1664
default:
1665
/* TODO: pass error message to other audit plugins */
1666
sudo_fatalx(U_("error initializing audit plugin %s"),
1667
plugin->name);
1668
}
1669
}
1670
1671
debug_return;
1672
}
1673
1674
static void
1675
audit_close(int status_type, int status)
1676
{
1677
struct plugin_container *plugin;
1678
debug_decl(audit_close, SUDO_DEBUG_PCOMM);
1679
1680
TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1681
if (plugin->u.audit->close != NULL) {
1682
sudo_debug_set_active_instance(plugin->debug_instance);
1683
plugin->u.audit->close(status_type, status);
1684
sudo_debug_set_active_instance(sudo_debug_instance);
1685
}
1686
}
1687
1688
debug_return;
1689
}
1690
1691
static void
1692
audit_show_version(int verbose)
1693
{
1694
struct plugin_container *plugin;
1695
debug_decl(audit_show_version, SUDO_DEBUG_PCOMM);
1696
1697
TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1698
sudo_debug_set_active_instance(plugin->debug_instance);
1699
if (plugin->u.audit->show_version != NULL) {
1700
/* Return value of show_version currently ignored. */
1701
plugin->u.audit->show_version(verbose);
1702
}
1703
if (plugin->u.audit->close != NULL)
1704
plugin->u.audit->close(SUDO_PLUGIN_NO_STATUS, 0);
1705
sudo_debug_set_active_instance(sudo_debug_instance);
1706
}
1707
1708
debug_return;
1709
}
1710
1711
/*
1712
* Error from plugin or front-end.
1713
* The error will not be sent to plugin source, if specified.
1714
*/
1715
static bool
1716
audit_error2(struct plugin_container *source, const char *plugin_name,
1717
unsigned int plugin_type, const char *audit_msg, char * const command_info[])
1718
{
1719
struct plugin_container *plugin;
1720
const char *errstr = NULL;
1721
bool ret = true;
1722
int ok;
1723
debug_decl(audit_error2, SUDO_DEBUG_PCOMM);
1724
1725
TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1726
if (plugin->u.audit->error == NULL)
1727
continue;
1728
1729
/* Avoid a loop if the audit plugin itself has an error. */
1730
if (plugin == source)
1731
continue;
1732
1733
sudo_debug_set_active_instance(plugin->debug_instance);
1734
ok = plugin->u.audit->error(plugin_name, plugin_type,
1735
audit_msg, command_info, &errstr);
1736
sudo_debug_set_active_instance(sudo_debug_instance);
1737
if (ok != 1) {
1738
/*
1739
* Don't propagate the error to other audit plugins.
1740
* It is not worth the trouble to avoid potential loops.
1741
*/
1742
sudo_debug_printf(SUDO_DEBUG_ERROR,
1743
"%s: plugin %s error failed, ret %d", __func__,
1744
plugin->name, ok);
1745
sudo_warnx(U_("%s: unable to log error event%s%s"),
1746
plugin->name, errstr ? ": " : "", errstr ? errstr : "");
1747
ret = false;
1748
}
1749
}
1750
1751
debug_return_bool(ret);
1752
}
1753
1754
/*
1755
* Command accepted by policy.
1756
* See command_info[] for additional info.
1757
* XXX - actual environment may be updated by policy_init_session().
1758
*/
1759
bool
1760
audit_accept(const char *plugin_name, unsigned int plugin_type,
1761
char * const command_info[], char * const run_argv[],
1762
char * const run_envp[])
1763
{
1764
struct plugin_container *plugin;
1765
const char *errstr = NULL;
1766
int ok;
1767
debug_decl(audit_accept, SUDO_DEBUG_PCOMM);
1768
1769
TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1770
if (plugin->u.audit->accept == NULL)
1771
continue;
1772
1773
sudo_debug_set_active_instance(plugin->debug_instance);
1774
ok = plugin->u.audit->accept(plugin_name, plugin_type,
1775
command_info, run_argv, run_envp, &errstr);
1776
sudo_debug_set_active_instance(sudo_debug_instance);
1777
if (ok != 1) {
1778
sudo_debug_printf(SUDO_DEBUG_ERROR,
1779
"%s: plugin %s accept failed, ret %d", __func__,
1780
plugin->name, ok);
1781
sudo_warnx(U_("%s: unable to log accept event%s%s"),
1782
plugin->name, errstr ? ": " : "", errstr ? errstr : "");
1783
1784
/* Notify other audit plugins and return. */
1785
audit_error2(plugin, plugin->name, SUDO_AUDIT_PLUGIN,
1786
errstr ? errstr : _("audit plugin error"), command_info);
1787
debug_return_bool(false);
1788
}
1789
}
1790
1791
debug_return_bool(true);
1792
}
1793
1794
/*
1795
* Command rejected by policy or I/O plugin.
1796
*/
1797
bool
1798
audit_reject(const char *plugin_name, unsigned int plugin_type,
1799
const char *audit_msg, char * const command_info[])
1800
{
1801
struct plugin_container *plugin;
1802
const char *errstr = NULL;
1803
bool ret = true;
1804
int ok;
1805
debug_decl(audit_reject, SUDO_DEBUG_PCOMM);
1806
1807
TAILQ_FOREACH(plugin, &audit_plugins, entries) {
1808
if (plugin->u.audit->reject == NULL)
1809
continue;
1810
1811
sudo_debug_set_active_instance(plugin->debug_instance);
1812
ok = plugin->u.audit->reject(plugin_name, plugin_type,
1813
audit_msg, command_info, &errstr);
1814
sudo_debug_set_active_instance(sudo_debug_instance);
1815
if (ok != 1) {
1816
sudo_debug_printf(SUDO_DEBUG_ERROR,
1817
"%s: plugin %s reject failed, ret %d", __func__,
1818
plugin->name, ok);
1819
sudo_warnx(U_("%s: unable to log reject event%s%s"),
1820
plugin->name, errstr ? ": " : "", errstr ? errstr : "");
1821
1822
/* Notify other audit plugins. */
1823
audit_error2(plugin, plugin->name, SUDO_AUDIT_PLUGIN,
1824
errstr ? errstr : _("audit plugin error"), command_info);
1825
1826
ret = false;
1827
break;
1828
}
1829
}
1830
1831
debug_return_bool(ret);
1832
}
1833
1834
/*
1835
* Error from plugin or front-end.
1836
*/
1837
bool
1838
audit_error(const char *plugin_name, unsigned int plugin_type,
1839
const char *audit_msg, char * const command_info[])
1840
{
1841
return audit_error2(NULL, plugin_name, plugin_type, audit_msg,
1842
command_info);
1843
}
1844
1845
static int
1846
approval_open_int(struct plugin_container *plugin)
1847
{
1848
char **plugin_settings;
1849
const char *errstr = NULL;
1850
int ret;
1851
debug_decl(approval_open_int, SUDO_DEBUG_PCOMM);
1852
1853
/* Convert struct sudo_settings to plugin_settings[] */
1854
plugin_settings = format_plugin_settings(plugin);
1855
if (plugin_settings == NULL)
1856
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1857
1858
sudo_debug_set_active_instance(SUDO_DEBUG_INSTANCE_INITIALIZER);
1859
ret = plugin->u.approval->open(SUDO_API_VERSION, sudo_conversation,
1860
sudo_conversation_printf, plugin_settings, user_info, submit_optind,
1861
submit_argv, submit_envp, plugin->options, &errstr);
1862
1863
/* Stash plugin debug instance ID if set in open() function. */
1864
plugin->debug_instance = sudo_debug_get_active_instance();
1865
sudo_debug_set_active_instance(sudo_debug_instance);
1866
1867
switch (ret) {
1868
case 1:
1869
break;
1870
case 0:
1871
/* approval plugin asked to be disabled, remove and free. */
1872
unlink_plugin(&approval_plugins, plugin);
1873
break;
1874
case -2:
1875
usage();
1876
/* NOTREACHED */
1877
default:
1878
/* XXX - audit */
1879
sudo_fatalx(U_("error initializing approval plugin %s"),
1880
plugin->name);
1881
}
1882
1883
debug_return_int(ret);
1884
}
1885
1886
static void
1887
approval_show_version(int verbose)
1888
{
1889
struct plugin_container *plugin, *next;
1890
int ok;
1891
debug_decl(approval_show_version, SUDO_DEBUG_PCOMM);
1892
1893
/*
1894
* Approval plugin us only open for the life of the show_version() call.
1895
*/
1896
TAILQ_FOREACH_SAFE(plugin, &approval_plugins, entries, next) {
1897
if (plugin->u.approval->show_version == NULL)
1898
continue;
1899
1900
ok = approval_open_int(plugin);
1901
if (ok == 1) {
1902
/* Return value of show_version currently ignored. */
1903
sudo_debug_set_active_instance(plugin->debug_instance);
1904
plugin->u.approval->show_version(verbose);
1905
if (plugin->u.approval->close != NULL)
1906
plugin->u.approval->close();
1907
sudo_debug_set_active_instance(sudo_debug_instance);
1908
}
1909
}
1910
1911
debug_return;
1912
}
1913
1914
/*
1915
* Run approval checks (there may be more than one).
1916
* This is a "one-shot" plugin that has no open/close and is only
1917
* called if the policy plugin accepts the command first.
1918
*/
1919
bool
1920
approval_check(char * const command_info[], char * const run_argv[],
1921
char * const run_envp[])
1922
{
1923
struct plugin_container *plugin, *next;
1924
const char *errstr = NULL;
1925
int ok;
1926
debug_decl(approval_check, SUDO_DEBUG_PCOMM);
1927
1928
/*
1929
* Approval plugin is only open for the life of the check() call.
1930
*/
1931
TAILQ_FOREACH_SAFE(plugin, &approval_plugins, entries, next) {
1932
if (plugin->u.approval->check == NULL)
1933
continue;
1934
1935
ok = approval_open_int(plugin);
1936
if (ok != 1)
1937
continue;
1938
1939
sudo_debug_set_active_instance(plugin->debug_instance);
1940
ok = plugin->u.approval->check(command_info, run_argv, run_envp,
1941
&errstr);
1942
sudo_debug_set_active_instance(sudo_debug_instance);
1943
sudo_debug_printf(SUDO_DEBUG_INFO, "approval plugin %s returns %d (%s)",
1944
plugin->name, ok, errstr ? errstr : "");
1945
1946
switch (ok) {
1947
case 0:
1948
audit_reject(plugin->name, SUDO_APPROVAL_PLUGIN,
1949
errstr ? errstr : _("command rejected by approver"),
1950
command_info);
1951
break;
1952
case 1:
1953
if (!audit_accept(plugin->name, SUDO_APPROVAL_PLUGIN, command_info,
1954
run_argv, run_envp))
1955
ok = -1;
1956
break;
1957
case -1:
1958
audit_error(plugin->name, SUDO_APPROVAL_PLUGIN,
1959
errstr ? errstr : _("approval plugin error"),
1960
command_info);
1961
break;
1962
case -2:
1963
usage();
1964
}
1965
1966
/* Close approval plugin now that errstr has been consumed. */
1967
if (plugin->u.approval->close != NULL) {
1968
sudo_debug_set_active_instance(plugin->debug_instance);
1969
plugin->u.approval->close();
1970
sudo_debug_set_active_instance(sudo_debug_instance);
1971
}
1972
1973
if (ok != 1)
1974
debug_return_bool(false);
1975
}
1976
1977
debug_return_bool(true);
1978
}
1979
1980
static void
1981
plugin_event_callback(int fd, int what, void *v)
1982
{
1983
struct sudo_plugin_event_int *ev_int = v;
1984
int old_instance;
1985
debug_decl(plugin_event_callback, SUDO_DEBUG_PCOMM);
1986
1987
/* Run the real callback using the plugin's debug instance. */
1988
old_instance = sudo_debug_set_active_instance(ev_int->debug_instance);
1989
ev_int->callback(fd, what, ev_int->closure);
1990
sudo_debug_set_active_instance(old_instance);
1991
1992
debug_return;
1993
}
1994
1995
/*
1996
* Fill in a previously allocated struct sudo_plugin_event.
1997
*/
1998
static int
1999
plugin_event_set(struct sudo_plugin_event *pev, int fd, int events,
2000
sudo_ev_callback_t callback, void *closure)
2001
{
2002
struct sudo_plugin_event_int *ev_int;
2003
debug_decl(plugin_event_set, SUDO_DEBUG_PCOMM);
2004
2005
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2006
if (sudo_ev_set(&ev_int->private, fd, events, plugin_event_callback, ev_int) == -1)
2007
debug_return_int(-1);
2008
2009
/* Stash active instance so we can restore it when callback runs. */
2010
ev_int->debug_instance = sudo_debug_get_active_instance();
2011
2012
/* Actual user-specified callback and closure. */
2013
ev_int->callback = callback;
2014
ev_int->closure = closure;
2015
2016
/* Plugin can only operate on the main event loop. */
2017
ev_int->private.base = sudo_event_base;
2018
2019
debug_return_int(1);
2020
}
2021
2022
/*
2023
* Add a struct sudo_plugin_event to the main event loop.
2024
*/
2025
static int
2026
plugin_event_add(struct sudo_plugin_event *pev, struct timespec *timo)
2027
{
2028
struct sudo_plugin_event_int *ev_int;
2029
debug_decl(plugin_event_add, SUDO_DEBUG_PCOMM);
2030
2031
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2032
if (sudo_ev_add(NULL, &ev_int->private, timo, 0) == -1)
2033
debug_return_int(-1);
2034
debug_return_int(1);
2035
}
2036
2037
/*
2038
* Delete a struct sudo_plugin_event from the main event loop.
2039
*/
2040
static int
2041
plugin_event_del(struct sudo_plugin_event *pev)
2042
{
2043
struct sudo_plugin_event_int *ev_int;
2044
debug_decl(plugin_event_del, SUDO_DEBUG_PCOMM);
2045
2046
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2047
if (sudo_ev_del(NULL, &ev_int->private) == -1)
2048
debug_return_int(-1);
2049
debug_return_int(1);
2050
}
2051
2052
/*
2053
* Get the amount of time remaining in a timeout event.
2054
*/
2055
static int
2056
plugin_event_pending(struct sudo_plugin_event *pev, int events,
2057
struct timespec *ts)
2058
{
2059
struct sudo_plugin_event_int *ev_int;
2060
debug_decl(plugin_event_pending, SUDO_DEBUG_PCOMM);
2061
2062
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2063
debug_return_int(sudo_ev_pending(&ev_int->private, events, ts));
2064
}
2065
2066
/*
2067
* Get the file descriptor associated with an event.
2068
*/
2069
static int
2070
plugin_event_fd(struct sudo_plugin_event *pev)
2071
{
2072
struct sudo_plugin_event_int *ev_int;
2073
debug_decl(plugin_event_fd, SUDO_DEBUG_PCOMM);
2074
2075
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2076
debug_return_int(sudo_ev_get_fd(&ev_int->private));
2077
}
2078
2079
/*
2080
* Break out of the event loop, killing the command if it is running.
2081
*/
2082
static void
2083
plugin_event_loopbreak(struct sudo_plugin_event *pev)
2084
{
2085
struct sudo_plugin_event_int *ev_int;
2086
debug_decl(plugin_event_loopbreak, SUDO_DEBUG_PCOMM);
2087
2088
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2089
sudo_ev_loopbreak(ev_int->private.base);
2090
debug_return;
2091
}
2092
2093
/*
2094
* Reset the event base of a struct sudo_plugin_event.
2095
* The event is removed from the old base (if any) first.
2096
* A NULL base can be used to set the default sudo event base.
2097
*/
2098
static void
2099
plugin_event_setbase(struct sudo_plugin_event *pev, void *base)
2100
{
2101
struct sudo_plugin_event_int *ev_int;
2102
debug_decl(plugin_event_setbase, SUDO_DEBUG_PCOMM);
2103
2104
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2105
if (ev_int->private.base != NULL)
2106
sudo_ev_del(ev_int->private.base, &ev_int->private);
2107
ev_int->private.base = base ? base : sudo_event_base;
2108
debug_return;
2109
}
2110
2111
/*
2112
* Free a struct sudo_plugin_event allocated by plugin_event_alloc().
2113
*/
2114
static void
2115
plugin_event_free(struct sudo_plugin_event *pev)
2116
{
2117
struct sudo_plugin_event_int *ev_int;
2118
debug_decl(plugin_event_free, SUDO_DEBUG_PCOMM);
2119
2120
/* The private field is first so sudo_ev_free() can free the struct. */
2121
ev_int = __containerof(pev, struct sudo_plugin_event_int, public);
2122
sudo_ev_free(&ev_int->private);
2123
2124
debug_return;
2125
}
2126
2127
/*
2128
* Allocate a struct sudo_plugin_event and fill in the public fields.
2129
*/
2130
struct sudo_plugin_event *
2131
sudo_plugin_event_alloc(void)
2132
{
2133
struct sudo_plugin_event_int *ev_int;
2134
debug_decl(plugin_event_alloc, SUDO_DEBUG_PCOMM);
2135
2136
if ((ev_int = malloc(sizeof(*ev_int))) == NULL)
2137
debug_return_ptr(NULL);
2138
2139
/* Init public fields. */
2140
ev_int->public.set = plugin_event_set;
2141
ev_int->public.add = plugin_event_add;
2142
ev_int->public.del = plugin_event_del;
2143
ev_int->public.fd = plugin_event_fd;
2144
ev_int->public.pending = plugin_event_pending;
2145
ev_int->public.setbase = plugin_event_setbase;
2146
ev_int->public.loopbreak = plugin_event_loopbreak;
2147
ev_int->public.free = plugin_event_free;
2148
2149
/* Debug instance to use with the callback. */
2150
ev_int->debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
2151
2152
/* Clear private portion in case caller tries to use us uninitialized. */
2153
memset(&ev_int->private, 0, sizeof(ev_int->private));
2154
2155
debug_return_ptr(&ev_int->public);
2156
}
2157
2158
static void
2159
free_plugin_container(struct plugin_container *plugin, bool ioplugin)
2160
{
2161
debug_decl(free_plugin_container, SUDO_DEBUG_PLUGIN);
2162
2163
free(plugin->path);
2164
free(plugin->name);
2165
if (plugin->options != NULL) {
2166
int i = 0;
2167
while (plugin->options[i] != NULL)
2168
free(plugin->options[i++]);
2169
free(plugin->options);
2170
}
2171
if (ioplugin)
2172
free(plugin);
2173
2174
debug_return;
2175
}
2176
2177
bool
2178
gc_add(enum sudo_gc_types type, void *v)
2179
{
2180
#ifdef NO_LEAKS
2181
struct sudo_gc_entry *gc;
2182
debug_decl(gc_add, SUDO_DEBUG_MAIN);
2183
2184
if (v == NULL)
2185
debug_return_bool(false);
2186
2187
gc = calloc(1, sizeof(*gc));
2188
if (gc == NULL) {
2189
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
2190
debug_return_bool(false);
2191
}
2192
switch (type) {
2193
case GC_PTR:
2194
gc->u.ptr = v;
2195
break;
2196
case GC_VECTOR:
2197
gc->u.vec = v;
2198
break;
2199
default:
2200
free(gc);
2201
sudo_warnx("unexpected garbage type %d", type);
2202
debug_return_bool(false);
2203
}
2204
gc->type = type;
2205
SLIST_INSERT_HEAD(&sudo_gc_list, gc, entries);
2206
debug_return_bool(true);
2207
#else
2208
return true;
2209
#endif /* NO_LEAKS */
2210
}
2211
2212
#ifdef NO_LEAKS
2213
static void
2214
gc_run(void)
2215
{
2216
struct plugin_container *plugin;
2217
struct sudo_gc_entry *gc;
2218
char **cur;
2219
debug_decl(gc_run, SUDO_DEBUG_MAIN);
2220
2221
/* Collect garbage. */
2222
while ((gc = SLIST_FIRST(&sudo_gc_list))) {
2223
SLIST_REMOVE_HEAD(&sudo_gc_list, entries);
2224
switch (gc->type) {
2225
case GC_PTR:
2226
free(gc->u.ptr);
2227
free(gc);
2228
break;
2229
case GC_VECTOR:
2230
for (cur = gc->u.vec; *cur != NULL; cur++)
2231
free(*cur);
2232
free(gc->u.vec);
2233
free(gc);
2234
break;
2235
default:
2236
sudo_warnx("unexpected garbage type %d", gc->type);
2237
}
2238
}
2239
2240
/* Free plugin structs. */
2241
free_plugin_container(&policy_plugin, false);
2242
while ((plugin = TAILQ_FIRST(&io_plugins))) {
2243
TAILQ_REMOVE(&io_plugins, plugin, entries);
2244
free_plugin_container(plugin, true);
2245
}
2246
2247
debug_return;
2248
}
2249
#endif /* NO_LEAKS */
2250
2251
static void
2252
gc_init(void)
2253
{
2254
#ifdef NO_LEAKS
2255
atexit(gc_run);
2256
#endif
2257
}
2258
2259