Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/parse_args.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 1993-1996, 1998-2023 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
* Sponsored in part by the Defense Advanced Research Projects
19
* Agency (DARPA) and Air Force Research Laboratory, Air Force
20
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
21
*/
22
23
#include <config.h>
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
#include <ctype.h>
29
#include <assert.h>
30
#ifdef HAVE_GETOPT_LONG
31
# include <getopt.h>
32
# else
33
# include <compat/getopt.h>
34
#endif /* HAVE_GETOPT_LONG */
35
36
#include <sudo_usage.h>
37
#include <sudo.h>
38
#include <sudo_lbuf.h>
39
40
unsigned int tgetpass_flags;
41
42
/*
43
* Local functions.
44
*/
45
sudo_noreturn static void help(void);
46
sudo_noreturn static void usage_excl(void);
47
sudo_noreturn static void usage_excl_ticket(void);
48
49
/*
50
* Mapping of command line options to name/value settings.
51
* Do not reorder, indexes must match ARG_ defines in sudo.h.
52
*/
53
static struct sudo_settings sudo_settings[] = {
54
{ "bsdauth_type" },
55
{ "login_class" },
56
{ "preserve_environment" },
57
{ "runas_group" },
58
{ "set_home" },
59
{ "run_shell" },
60
{ "login_shell" },
61
{ "ignore_ticket" },
62
{ "update_ticket" },
63
{ "prompt" },
64
{ "selinux_role" },
65
{ "selinux_type" },
66
{ "runas_user" },
67
{ "progname" },
68
{ "implied_shell" },
69
{ "preserve_groups" },
70
{ "noninteractive" },
71
{ "sudoedit" },
72
{ "closefrom" },
73
{ "network_addrs" },
74
{ "max_groups" },
75
{ "plugin_dir" },
76
{ "remote_host" },
77
{ "timeout" },
78
{ "cmnd_chroot" },
79
{ "cmnd_cwd" },
80
{ "askpass" },
81
{ "intercept_setid" },
82
{ "intercept_ptrace" },
83
{ NULL }
84
};
85
86
struct environment {
87
char **envp; /* pointer to the new environment */
88
size_t env_size; /* size of new_environ in char **'s */
89
size_t env_len; /* number of slots used, not counting NULL */
90
};
91
92
/*
93
* Default flags allowed when running a command.
94
*/
95
#define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_PRESERVE_GROUPS|MODE_SHELL)
96
#define EDIT_VALID_FLAGS MODE_NONINTERACTIVE
97
#define LIST_VALID_FLAGS (MODE_NONINTERACTIVE|MODE_LONG_LIST)
98
#define VALIDATE_VALID_FLAGS MODE_NONINTERACTIVE
99
100
/* Option number for the --host long option due to ambiguity of the -h flag. */
101
#define OPT_HOSTNAME 256
102
103
/*
104
* Available command line options, both short and long.
105
* Note that we must disable arg permutation to support setting environment
106
* variables and to better support the optional arg of the -h flag.
107
* There is a more limited set of options for sudoedit (the sudo-specific
108
* long options are listed first).
109
*/
110
static const char sudo_short_opts[] = "+Aa:BbC:c:D:Eeg:Hh::iKklNnPp:R:r:SsT:t:U:u:Vv";
111
static const char edit_short_opts[] = "+Aa:BC:c:D:g:h::KkNnp:R:r:ST:t:u:V";
112
static struct option sudo_long_opts[] = {
113
/* sudo-specific long options */
114
{ "background", no_argument, NULL, 'b' },
115
{ "preserve-env", optional_argument, NULL, 'E' },
116
{ "edit", no_argument, NULL, 'e' },
117
{ "set-home", no_argument, NULL, 'H' },
118
{ "login", no_argument, NULL, 'i' },
119
{ "remove-timestamp", no_argument, NULL, 'K' },
120
{ "list", no_argument, NULL, 'l' },
121
{ "preserve-groups", no_argument, NULL, 'P' },
122
{ "shell", no_argument, NULL, 's' },
123
{ "other-user", required_argument, NULL, 'U' },
124
{ "validate", no_argument, NULL, 'v' },
125
/* common long options */
126
{ "askpass", no_argument, NULL, 'A' },
127
{ "auth-type", required_argument, NULL, 'a' },
128
{ "bell", no_argument, NULL, 'B' },
129
{ "close-from", required_argument, NULL, 'C' },
130
{ "login-class", required_argument, NULL, 'c' },
131
{ "chdir", required_argument, NULL, 'D' },
132
{ "group", required_argument, NULL, 'g' },
133
{ "help", no_argument, NULL, 'h' },
134
{ "host", required_argument, NULL, OPT_HOSTNAME },
135
{ "reset-timestamp", no_argument, NULL, 'k' },
136
{ "no-update", no_argument, NULL, 'N' },
137
{ "non-interactive", no_argument, NULL, 'n' },
138
{ "prompt", required_argument, NULL, 'p' },
139
{ "chroot", required_argument, NULL, 'R' },
140
{ "role", required_argument, NULL, 'r' },
141
{ "stdin", no_argument, NULL, 'S' },
142
{ "command-timeout",required_argument, NULL, 'T' },
143
{ "type", required_argument, NULL, 't' },
144
{ "user", required_argument, NULL, 'u' },
145
{ "version", no_argument, NULL, 'V' },
146
{ NULL, no_argument, NULL, '\0' },
147
};
148
static struct option *edit_long_opts = &sudo_long_opts[11];
149
150
/*
151
* Insert a key=value pair into the specified environment.
152
*/
153
static void
154
env_insert(struct environment *e, char *pair)
155
{
156
debug_decl(env_insert, SUDO_DEBUG_ARGS);
157
158
/* Make sure we have at least two slots free (one for NULL). */
159
if (e->env_len + 1 >= e->env_size) {
160
char **tmp;
161
162
if (e->env_size == 0)
163
e->env_size = 16;
164
tmp = reallocarray(e->envp, e->env_size, 2 * sizeof(char *));
165
if (tmp == NULL)
166
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
167
e->envp = tmp;
168
e->env_size *= 2;
169
}
170
e->envp[e->env_len++] = pair;
171
e->envp[e->env_len] = NULL;
172
173
debug_return;
174
}
175
176
/*
177
* Format as var=val and insert into the specified environment.
178
*/
179
static void
180
env_set(struct environment *e, char *var, char *val)
181
{
182
char *pair;
183
debug_decl(env_set, SUDO_DEBUG_ARGS);
184
185
pair = sudo_new_key_val(var, val);
186
if (pair == NULL) {
187
sudo_fatalx(U_("%s: %s"),
188
__func__, U_("unable to allocate memory"));
189
}
190
env_insert(e, pair);
191
192
debug_return;
193
}
194
195
/*
196
* Parse a comma-separated list of env vars and add to the
197
* specified environment.
198
*/
199
static void
200
parse_env_list(struct environment *e, char *list)
201
{
202
char *cp, *last, *val;
203
debug_decl(parse_env_list, SUDO_DEBUG_ARGS);
204
205
for ((cp = strtok_r(list, ",", &last)); cp != NULL;
206
(cp = strtok_r(NULL, ",", &last))) {
207
if (strchr(cp, '=') != NULL) {
208
sudo_warnx(U_("invalid environment variable name: %s"), cp);
209
usage();
210
}
211
if ((val = getenv(cp)) != NULL)
212
env_set(e, cp, val);
213
}
214
debug_return;
215
}
216
217
/*
218
* Command line argument parsing.
219
* Sets nargc and nargv which corresponds to the argc/argv we'll use
220
* for the command to be run (if we are running one).
221
*/
222
unsigned int
223
parse_args(int argc, char **argv, const char *shell, int *old_optind,
224
int *nargc, char ***nargv, struct sudo_settings **settingsp,
225
char ***env_addp, const char **list_userp)
226
{
227
const char *progname, *short_opts = sudo_short_opts;
228
struct option *long_opts = sudo_long_opts;
229
struct environment extra_env;
230
const char *list_user = NULL;
231
unsigned int mode = 0; /* what mode is sudo to be run in? */
232
unsigned int flags = 0; /* mode flags */
233
unsigned int valid_flags = DEFAULT_VALID_FLAGS;
234
int ch, i;
235
char *cp;
236
debug_decl(parse_args, SUDO_DEBUG_ARGS);
237
238
/* Is someone trying something funny? */
239
if (argc <= 0)
240
usage();
241
242
/* The plugin API includes the program name (either sudo or sudoedit). */
243
progname = getprogname();
244
sudo_settings[ARG_PROGNAME].value = progname;
245
246
/* First, check to see if we were invoked as "sudoedit". */
247
if (strcmp(progname, "sudoedit") == 0) {
248
mode = MODE_EDIT;
249
sudo_settings[ARG_SUDOEDIT].value = "true";
250
valid_flags = EDIT_VALID_FLAGS;
251
short_opts = edit_short_opts;
252
long_opts = edit_long_opts;
253
}
254
255
/* Load local IP addresses and masks. */
256
if (get_net_ifs(&cp) > 0)
257
sudo_settings[ARG_NET_ADDRS].value = cp;
258
259
/* Set max_groups from sudo.conf. */
260
i = sudo_conf_max_groups();
261
if (i != -1) {
262
if (asprintf(&cp, "%d", i) == -1)
263
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
264
sudo_settings[ARG_MAX_GROUPS].value = cp;
265
}
266
267
/* Returns true if the last option string was "-h" */
268
#define got_host_flag (optind > 1 && argv[optind - 1][0] == '-' && \
269
argv[optind - 1][1] == 'h' && argv[optind - 1][2] == '\0')
270
271
/* Returns true if the last option string was "--" */
272
#define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
273
argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
274
275
/* Returns true if next option is an environment variable */
276
#define is_envar (optind < argc && argv[optind][0] != '/' && \
277
argv[optind][0] != '=' && strchr(argv[optind], '=') != NULL)
278
279
/* Space for environment variables is lazy allocated. */
280
memset(&extra_env, 0, sizeof(extra_env));
281
282
for (;;) {
283
/*
284
* Some trickiness is required to allow environment variables
285
* to be interspersed with command line options.
286
*/
287
if ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
288
switch (ch) {
289
case 'A':
290
SET(tgetpass_flags, TGP_ASKPASS);
291
sudo_settings[ARG_ASKPASS].value = "true";
292
break;
293
#ifdef HAVE_BSD_AUTH_H
294
case 'a':
295
assert(optarg != NULL);
296
if (*optarg == '\0')
297
usage();
298
if (sudo_settings[ARG_BSDAUTH_TYPE].value != NULL)
299
usage();
300
sudo_settings[ARG_BSDAUTH_TYPE].value = optarg;
301
break;
302
#endif
303
case 'b':
304
SET(flags, MODE_BACKGROUND);
305
break;
306
case 'B':
307
SET(tgetpass_flags, TGP_BELL);
308
break;
309
case 'C':
310
assert(optarg != NULL);
311
if (sudo_strtonum(optarg, 3, INT_MAX, NULL) == 0) {
312
sudo_warnx("%s",
313
U_("the argument to -C must be a number greater than or equal to 3"));
314
usage();
315
}
316
if (sudo_settings[ARG_CLOSEFROM].value != NULL)
317
usage();
318
sudo_settings[ARG_CLOSEFROM].value = optarg;
319
break;
320
#ifdef HAVE_LOGIN_CAP_H
321
case 'c':
322
assert(optarg != NULL);
323
if (*optarg == '\0')
324
usage();
325
if (sudo_settings[ARG_LOGIN_CLASS].value != NULL)
326
usage();
327
sudo_settings[ARG_LOGIN_CLASS].value = optarg;
328
break;
329
#endif
330
case 'D':
331
assert(optarg != NULL);
332
if (*optarg == '\0')
333
usage();
334
if (sudo_settings[ARG_CWD].value != NULL)
335
usage();
336
sudo_settings[ARG_CWD].value = optarg;
337
break;
338
case 'E':
339
/*
340
* Optional argument is a comma-separated list of
341
* environment variables to preserve.
342
* If not present, preserve everything.
343
*/
344
if (optarg == NULL) {
345
sudo_settings[ARG_PRESERVE_ENVIRONMENT].value = "true";
346
SET(flags, MODE_PRESERVE_ENV);
347
} else {
348
parse_env_list(&extra_env, optarg);
349
}
350
break;
351
case 'e':
352
if (mode && mode != MODE_EDIT)
353
usage_excl();
354
mode = MODE_EDIT;
355
sudo_settings[ARG_SUDOEDIT].value = "true";
356
valid_flags = EDIT_VALID_FLAGS;
357
break;
358
case 'g':
359
assert(optarg != NULL);
360
if (*optarg == '\0')
361
usage();
362
if (sudo_settings[ARG_RUNAS_GROUP].value != NULL)
363
usage();
364
sudo_settings[ARG_RUNAS_GROUP].value = optarg;
365
break;
366
case 'H':
367
sudo_settings[ARG_SET_HOME].value = "true";
368
SET(flags, MODE_RESET_HOME);
369
break;
370
case 'h':
371
if (optarg == NULL) {
372
/*
373
* Optional args support -hhostname, not -h hostname.
374
* If we see a non-option after the -h flag, treat as
375
* remote host and bump optind to skip over it.
376
*/
377
if (got_host_flag && argv[optind] != NULL &&
378
argv[optind][0] != '-' && !is_envar) {
379
if (sudo_settings[ARG_REMOTE_HOST].value != NULL)
380
usage();
381
sudo_settings[ARG_REMOTE_HOST].value = argv[optind++];
382
continue;
383
}
384
if (mode && mode != MODE_HELP) {
385
if (strcmp(progname, "sudoedit") != 0)
386
usage_excl();
387
}
388
mode = MODE_HELP;
389
valid_flags = 0;
390
break;
391
}
392
FALLTHROUGH;
393
case OPT_HOSTNAME:
394
assert(optarg != NULL);
395
if (*optarg == '\0')
396
usage();
397
if (sudo_settings[ARG_REMOTE_HOST].value != NULL)
398
usage();
399
sudo_settings[ARG_REMOTE_HOST].value = optarg;
400
break;
401
case 'i':
402
sudo_settings[ARG_LOGIN_SHELL].value = "true";
403
SET(flags, MODE_LOGIN_SHELL);
404
break;
405
case 'K':
406
if (mode && mode != MODE_KILL)
407
usage_excl();
408
mode = MODE_KILL;
409
valid_flags = 0;
410
FALLTHROUGH;
411
case 'k':
412
if (sudo_settings[ARG_UPDATE_TICKET].value != NULL)
413
usage_excl_ticket();
414
sudo_settings[ARG_IGNORE_TICKET].value = "true";
415
break;
416
case 'l':
417
if (mode) {
418
if (mode == MODE_LIST)
419
SET(flags, MODE_LONG_LIST);
420
else
421
usage_excl();
422
}
423
mode = MODE_LIST;
424
valid_flags = LIST_VALID_FLAGS;
425
break;
426
case 'N':
427
if (sudo_settings[ARG_IGNORE_TICKET].value != NULL)
428
usage_excl_ticket();
429
sudo_settings[ARG_UPDATE_TICKET].value = "false";
430
break;
431
case 'n':
432
SET(flags, MODE_NONINTERACTIVE);
433
sudo_settings[ARG_NONINTERACTIVE].value = "true";
434
break;
435
case 'P':
436
sudo_settings[ARG_PRESERVE_GROUPS].value = "true";
437
SET(flags, MODE_PRESERVE_GROUPS);
438
break;
439
case 'p':
440
/* An empty prompt is allowed. */
441
assert(optarg != NULL);
442
if (sudo_settings[ARG_PROMPT].value != NULL)
443
usage();
444
sudo_settings[ARG_PROMPT].value = optarg;
445
break;
446
case 'R':
447
assert(optarg != NULL);
448
if (*optarg == '\0')
449
usage();
450
if (sudo_settings[ARG_CHROOT].value != NULL)
451
usage();
452
sudo_warnx("%s",
453
U_("the -R option will be removed in a future version of sudo"));
454
sudo_settings[ARG_CHROOT].value = optarg;
455
break;
456
#ifdef HAVE_SELINUX
457
case 'r':
458
assert(optarg != NULL);
459
if (*optarg == '\0')
460
usage();
461
if (sudo_settings[ARG_SELINUX_ROLE].value != NULL)
462
usage();
463
sudo_settings[ARG_SELINUX_ROLE].value = optarg;
464
break;
465
case 't':
466
assert(optarg != NULL);
467
if (*optarg == '\0')
468
usage();
469
if (sudo_settings[ARG_SELINUX_TYPE].value != NULL)
470
usage();
471
sudo_settings[ARG_SELINUX_TYPE].value = optarg;
472
break;
473
#endif
474
case 'T':
475
/* Plugin determines whether empty timeout is allowed. */
476
assert(optarg != NULL);
477
if (sudo_settings[ARG_TIMEOUT].value != NULL)
478
usage();
479
sudo_settings[ARG_TIMEOUT].value = optarg;
480
break;
481
case 'S':
482
SET(tgetpass_flags, TGP_STDIN);
483
break;
484
case 's':
485
sudo_settings[ARG_USER_SHELL].value = "true";
486
SET(flags, MODE_SHELL);
487
break;
488
case 'U':
489
assert(optarg != NULL);
490
if (list_user != NULL || *optarg == '\0')
491
usage();
492
list_user = optarg;
493
break;
494
case 'u':
495
assert(optarg != NULL);
496
if (*optarg == '\0')
497
usage();
498
if (sudo_settings[ARG_RUNAS_USER].value != NULL)
499
usage();
500
sudo_settings[ARG_RUNAS_USER].value = optarg;
501
break;
502
case 'v':
503
if (mode && mode != MODE_VALIDATE)
504
usage_excl();
505
mode = MODE_VALIDATE;
506
valid_flags = VALIDATE_VALID_FLAGS;
507
break;
508
case 'V':
509
if (mode && mode != MODE_VERSION) {
510
if (strcmp(progname, "sudoedit") != 0)
511
usage_excl();
512
}
513
mode = MODE_VERSION;
514
valid_flags = 0;
515
break;
516
default:
517
usage();
518
}
519
} else if (!got_end_of_args && is_envar) {
520
/* Insert key=value pair, crank optind and resume getopt. */
521
env_insert(&extra_env, argv[optind]);
522
optind++;
523
} else {
524
/* Not an option or an environment variable -- we're done. */
525
break;
526
}
527
}
528
529
argc -= optind;
530
argv += optind;
531
*old_optind = optind;
532
533
if (!mode) {
534
/* Defer -k mode setting until we know whether it is a flag or not */
535
if (sudo_settings[ARG_IGNORE_TICKET].value != NULL) {
536
if (argc == 0 && !ISSET(flags, MODE_SHELL|MODE_LOGIN_SHELL)) {
537
mode = MODE_INVALIDATE; /* -k by itself */
538
sudo_settings[ARG_IGNORE_TICKET].value = NULL;
539
valid_flags = 0;
540
}
541
}
542
if (!mode)
543
mode = MODE_RUN; /* running a command */
544
}
545
546
if (argc > 0 && mode == MODE_LIST)
547
mode = MODE_CHECK;
548
549
if (ISSET(flags, MODE_LOGIN_SHELL)) {
550
if (ISSET(flags, MODE_SHELL)) {
551
sudo_warnx("%s",
552
U_("you may not specify both the -i and -s options"));
553
usage();
554
}
555
if (ISSET(flags, MODE_PRESERVE_ENV)) {
556
sudo_warnx("%s",
557
U_("you may not specify both the -i and -E options"));
558
usage();
559
}
560
SET(flags, MODE_SHELL);
561
}
562
if ((flags & valid_flags) != flags)
563
usage();
564
if (mode == MODE_EDIT &&
565
(ISSET(flags, MODE_PRESERVE_ENV) || extra_env.env_len != 0)) {
566
if (ISSET(mode, MODE_PRESERVE_ENV))
567
sudo_warnx("%s", U_("the -E option is not valid in edit mode"));
568
if (extra_env.env_len != 0)
569
sudo_warnx("%s",
570
U_("you may not specify environment variables in edit mode"));
571
usage();
572
}
573
if ((sudo_settings[ARG_RUNAS_USER].value != NULL ||
574
sudo_settings[ARG_RUNAS_GROUP].value != NULL) &&
575
!ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
576
usage();
577
}
578
if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
579
sudo_warnx("%s",
580
U_("the -U option may only be used with the -l option"));
581
usage();
582
}
583
if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
584
sudo_warnx("%s", U_("the -A and -S options may not be used together"));
585
usage();
586
}
587
if ((argc == 0 && mode == MODE_EDIT) ||
588
(argc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
589
usage();
590
if (argc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL)) {
591
SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
592
sudo_settings[ARG_IMPLIED_SHELL].value = "true";
593
}
594
#ifdef ENABLE_SUDO_PLUGIN_API
595
sudo_settings[ARG_PLUGIN_DIR].value = sudo_conf_plugin_dir_path();
596
#endif
597
if (exec_ptrace_intercept_supported())
598
sudo_settings[ARG_INTERCEPT_SETID].value = "true";
599
if (exec_ptrace_subcmds_supported())
600
sudo_settings[ARG_INTERCEPT_PTRACE].value = "true";
601
602
if (mode == MODE_HELP)
603
help();
604
605
/*
606
* For shell mode we need to rewrite argv
607
* TODO: move this to the policy plugin and make escaping configurable
608
*/
609
if (ISSET(flags, MODE_SHELL|MODE_LOGIN_SHELL) && ISSET(mode, MODE_RUN)) {
610
char **av, *cmnd = NULL;
611
int ac = 1;
612
613
if (argc != 0) {
614
/* shell -c "command" */
615
char *src, *dst;
616
size_t size = 0;
617
618
for (av = argv; *av != NULL; av++)
619
size += strlen(*av) + 1;
620
if (size == 0 || (cmnd = reallocarray(NULL, size, 2)) == NULL)
621
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
622
if (!gc_add(GC_PTR, cmnd))
623
exit(EXIT_FAILURE);
624
625
for (dst = cmnd, av = argv; *av != NULL; av++) {
626
for (src = *av; *src != '\0'; src++) {
627
/* quote potential meta characters */
628
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
629
*dst++ = '\\';
630
*dst++ = *src;
631
}
632
*dst++ = ' ';
633
}
634
if (cmnd != dst)
635
dst--; /* replace last space with a NUL */
636
*dst = '\0';
637
638
ac += 2; /* -c cmnd */
639
}
640
641
av = reallocarray(NULL, (size_t)ac + 1, sizeof(char *));
642
if (av == NULL)
643
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
644
if (!gc_add(GC_PTR, av))
645
exit(EXIT_FAILURE);
646
647
av[0] = (char *)shell; /* plugin may override shell */
648
if (cmnd != NULL) {
649
av[1] = (char *)"-c";
650
av[2] = cmnd;
651
}
652
av[ac] = NULL;
653
654
argv = av;
655
argc = ac;
656
}
657
658
/*
659
* For sudoedit we need to rewrite argv
660
*/
661
if (mode == MODE_EDIT) {
662
#if defined(HAVE_SETRESUID) || defined(HAVE_SETREUID) || defined(HAVE_SETEUID)
663
char **av;
664
int ac;
665
666
av = reallocarray(NULL, (size_t)argc + 2, sizeof(char *));
667
if (av == NULL)
668
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
669
if (!gc_add(GC_PTR, av))
670
exit(EXIT_FAILURE);
671
672
/* Must have the command in argv[0]. */
673
av[0] = (char *)"sudoedit";
674
for (ac = 0; argv[ac] != NULL; ac++) {
675
av[ac + 1] = argv[ac];
676
}
677
av[++ac] = NULL;
678
679
argv = av;
680
argc = ac;
681
#else
682
sudo_fatalx("%s", U_("sudoedit is not supported on this platform"));
683
#endif
684
}
685
686
*settingsp = sudo_settings;
687
*env_addp = extra_env.envp;
688
*nargc = argc;
689
*nargv = argv;
690
*list_userp = list_user;
691
debug_return_uint(mode | flags);
692
}
693
694
/*
695
* Display usage message.
696
* The actual usage strings are in sudo_usage.h for configure substitution.
697
*/
698
static void
699
display_usage(FILE *fp)
700
{
701
const char * const **uvecs = sudo_usage;
702
const char * const *uvec;
703
size_t i;
704
int indent;
705
706
/*
707
* Use usage vectors appropriate to the progname.
708
*/
709
if (strcmp(getprogname(), "sudoedit") == 0)
710
uvecs = sudoedit_usage;
711
712
indent = (int)strlen(getprogname()) + 8;
713
while ((uvec = *uvecs) != NULL) {
714
(void)fprintf(fp, "usage: %s %s\n", getprogname(), uvec[0]);
715
for (i = 1; uvec[i] != NULL; i++) {
716
(void)fprintf(fp, "%*s%s\n", indent, "", uvec[i]);
717
}
718
uvecs++;
719
}
720
}
721
722
/*
723
* Display usage message and exit.
724
*/
725
sudo_noreturn void
726
usage(void)
727
{
728
display_usage(stderr);
729
exit(EXIT_FAILURE);
730
}
731
732
/*
733
* Tell which options are mutually exclusive and exit.
734
*/
735
static void
736
usage_excl(void)
737
{
738
debug_decl(usage_excl, SUDO_DEBUG_ARGS);
739
740
sudo_warnx("%s",
741
U_("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified"));
742
usage();
743
}
744
745
/*
746
* Tell which options are mutually exclusive and exit.
747
*/
748
static void
749
usage_excl_ticket(void)
750
{
751
debug_decl(usage_excl_ticket, SUDO_DEBUG_ARGS);
752
753
sudo_warnx("%s",
754
U_("Only one of the -K, -k or -N options may be specified"));
755
usage();
756
}
757
758
static int
759
help_out(const char * restrict buf)
760
{
761
return fputs(buf, stdout);
762
}
763
764
sudo_noreturn static void
765
help(void)
766
{
767
struct sudo_lbuf lbuf;
768
const int indent = 32;
769
const char *pname = getprogname();
770
bool sudoedit = false;
771
debug_decl(help, SUDO_DEBUG_ARGS);
772
773
if (strcmp(pname, "sudoedit") == 0) {
774
sudoedit = true;
775
(void)printf(_("%s - edit files as another user\n\n"), pname);
776
} else {
777
(void)printf(_("%s - execute a command as another user\n\n"), pname);
778
}
779
display_usage(stdout);
780
781
sudo_lbuf_init(&lbuf, help_out, indent, NULL, 80);
782
sudo_lbuf_append(&lbuf, "%s", _("\nOptions:\n"));
783
sudo_lbuf_append(&lbuf, " -A, --askpass %s\n",
784
_("use a helper program for password prompting"));
785
#ifdef HAVE_BSD_AUTH_H
786
sudo_lbuf_append(&lbuf, " -a, --auth-type=type %s\n",
787
_("use specified BSD authentication type"));
788
#endif
789
if (!sudoedit) {
790
sudo_lbuf_append(&lbuf, " -b, --background %s\n",
791
_("run command in the background"));
792
}
793
sudo_lbuf_append(&lbuf, " -B, --bell %s\n",
794
_("ring bell when prompting"));
795
sudo_lbuf_append(&lbuf, " -C, --close-from=num %s\n",
796
_("close all file descriptors >= num"));
797
#ifdef HAVE_LOGIN_CAP_H
798
sudo_lbuf_append(&lbuf, " -c, --login-class=class %s\n",
799
_("run command with the specified BSD login class"));
800
#endif
801
sudo_lbuf_append(&lbuf, " -D, --chdir=directory %s\n",
802
_("change the working directory before running command"));
803
if (!sudoedit) {
804
sudo_lbuf_append(&lbuf, " -E, --preserve-env %s\n",
805
_("preserve user environment when running command"));
806
sudo_lbuf_append(&lbuf, " --preserve-env=list %s\n",
807
_("preserve specific environment variables"));
808
sudo_lbuf_append(&lbuf, " -e, --edit %s\n",
809
_("edit files instead of running a command"));
810
}
811
sudo_lbuf_append(&lbuf, " -g, --group=group %s\n",
812
_("run command as the specified group name or ID"));
813
if (!sudoedit) {
814
sudo_lbuf_append(&lbuf, " -H, --set-home %s\n",
815
_("set HOME variable to target user's home dir"));
816
}
817
sudo_lbuf_append(&lbuf, " -h, --help %s\n",
818
_("display help message and exit"));
819
sudo_lbuf_append(&lbuf, " -h, --host=host %s\n",
820
_("run command on host (if supported by plugin)"));
821
if (!sudoedit) {
822
sudo_lbuf_append(&lbuf, " -i, --login %s\n",
823
_("run login shell as the target user; a command may also be specified"));
824
sudo_lbuf_append(&lbuf, " -K, --remove-timestamp %s\n",
825
_("remove timestamp file completely"));
826
}
827
sudo_lbuf_append(&lbuf, " -k, --reset-timestamp %s\n",
828
_("invalidate timestamp file"));
829
if (!sudoedit) {
830
sudo_lbuf_append(&lbuf, " -l, --list %s\n",
831
_("list user's privileges or check a specific command; use twice for longer format"));
832
}
833
sudo_lbuf_append(&lbuf, " -n, --non-interactive %s\n",
834
_("non-interactive mode, no prompts are used"));
835
if (!sudoedit) {
836
sudo_lbuf_append(&lbuf, " -P, --preserve-groups %s\n",
837
_("preserve group vector instead of setting to target's"));
838
}
839
sudo_lbuf_append(&lbuf, " -p, --prompt=prompt %s\n",
840
_("use the specified password prompt"));
841
sudo_lbuf_append(&lbuf, " -R, --chroot=directory %s\n",
842
_("change the root directory before running command"));
843
#ifdef HAVE_SELINUX
844
sudo_lbuf_append(&lbuf, " -r, --role=role %s\n",
845
_("create SELinux security context with specified role"));
846
#endif
847
sudo_lbuf_append(&lbuf, " -S, --stdin %s\n",
848
_("read password from standard input"));
849
if (!sudoedit) {
850
sudo_lbuf_append(&lbuf, " -s, --shell %s\n",
851
_("run shell as the target user; a command may also be specified"));
852
}
853
#ifdef HAVE_SELINUX
854
sudo_lbuf_append(&lbuf, " -t, --type=type %s\n",
855
_("create SELinux security context with specified type"));
856
#endif
857
sudo_lbuf_append(&lbuf, " -T, --command-timeout=timeout %s\n",
858
_("terminate command after the specified time limit"));
859
if (!sudoedit) {
860
sudo_lbuf_append(&lbuf, " -U, --other-user=user %s\n",
861
_("in list mode, display privileges for user"));
862
}
863
sudo_lbuf_append(&lbuf, " -u, --user=user %s\n",
864
_("run command (or edit file) as specified user name or ID"));
865
sudo_lbuf_append(&lbuf, " -V, --version %s\n",
866
_("display version information and exit"));
867
if (!sudoedit) {
868
sudo_lbuf_append(&lbuf, " -v, --validate %s\n",
869
_("update user's timestamp without running a command"));
870
}
871
sudo_lbuf_append(&lbuf, " -- %s\n",
872
_("stop processing command line arguments"));
873
sudo_lbuf_print(&lbuf);
874
sudo_lbuf_destroy(&lbuf);
875
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, 0);
876
exit(EXIT_SUCCESS);
877
}
878
879