summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <Todd.Miller@sudo.ws>2023-05-02 10:47:11 -0600
committerTodd C. Miller <Todd.Miller@sudo.ws>2023-05-02 10:47:11 -0600
commita4811f21f880af67461db113377b289461c3fc23 (patch)
tree81c50231e84e40807926de2260582a3f950b922b
parent9588d46fae9d6ad6cfb25b74ace786ed392dd15c (diff)
downloadsudo-a4811f21f880af67461db113377b289461c3fc23.tar.gz
Support adminconfdir for relative include paths in sudoers.
-rw-r--r--plugins/sudoers/gram.c20
-rw-r--r--plugins/sudoers/gram.y20
-rw-r--r--plugins/sudoers/parse.h4
-rw-r--r--plugins/sudoers/regress/fuzz/fuzz_sudoers.c2
-rw-r--r--plugins/sudoers/sudoers.h1
-rw-r--r--plugins/sudoers/testsudoers.c2
-rw-r--r--plugins/sudoers/toke.c398
-rw-r--r--plugins/sudoers/toke.l208
-rw-r--r--plugins/sudoers/visudo.c8
9 files changed, 424 insertions, 239 deletions
diff --git a/plugins/sudoers/gram.c b/plugins/sudoers/gram.c
index c606408f6..5cbdaca34 100644
--- a/plugins/sudoers/gram.c
+++ b/plugins/sudoers/gram.c
@@ -3965,7 +3965,7 @@ free_parse_tree(struct sudoers_parse_tree *parse_tree)
* the current sudoers file to path.
*/
bool
-init_parser_ext(const char *path, bool strict, int verbose)
+init_parser_ext(const char *file, const char *path, bool strict, int verbose)
{
bool ret = true;
debug_decl(init_parser, SUDOERS_DEBUG_PARSER);
@@ -3975,8 +3975,8 @@ init_parser_ext(const char *path, bool strict, int verbose)
init_lexer();
sudo_rcstr_delref(sudoers);
- if (path != NULL) {
- if ((sudoers = sudo_rcstr_dup(path)) == NULL) {
+ if (file != NULL) {
+ if ((sudoers = sudo_rcstr_dup(file)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
ret = false;
}
@@ -3984,6 +3984,16 @@ init_parser_ext(const char *path, bool strict, int verbose)
sudoers = NULL;
}
+ sudo_rcstr_delref(sudoers_search_path);
+ if (path != NULL) {
+ if ((sudoers_search_path = sudo_rcstr_dup(path)) == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ ret = false;
+ }
+ } else {
+ sudoers_search_path = NULL;
+ }
+
parse_error = false;
sudoers_strict = strict;
sudoers_verbose = verbose;
@@ -3992,9 +4002,9 @@ init_parser_ext(const char *path, bool strict, int verbose)
}
bool
-init_parser(const char *path)
+init_parser(const char *file)
{
- return init_parser_ext(path, false, 1);
+ return init_parser_ext(file, NULL, false, 1);
}
/*
diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y
index ee20174f3..54b9d84e9 100644
--- a/plugins/sudoers/gram.y
+++ b/plugins/sudoers/gram.y
@@ -1782,7 +1782,7 @@ free_parse_tree(struct sudoers_parse_tree *parse_tree)
* the current sudoers file to path.
*/
bool
-init_parser_ext(const char *path, bool strict, int verbose)
+init_parser_ext(const char *file, const char *path, bool strict, int verbose)
{
bool ret = true;
debug_decl(init_parser, SUDOERS_DEBUG_PARSER);
@@ -1792,8 +1792,8 @@ init_parser_ext(const char *path, bool strict, int verbose)
init_lexer();
sudo_rcstr_delref(sudoers);
- if (path != NULL) {
- if ((sudoers = sudo_rcstr_dup(path)) == NULL) {
+ if (file != NULL) {
+ if ((sudoers = sudo_rcstr_dup(file)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
ret = false;
}
@@ -1801,6 +1801,16 @@ init_parser_ext(const char *path, bool strict, int verbose)
sudoers = NULL;
}
+ sudo_rcstr_delref(sudoers_search_path);
+ if (path != NULL) {
+ if ((sudoers_search_path = sudo_rcstr_dup(path)) == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ ret = false;
+ }
+ } else {
+ sudoers_search_path = NULL;
+ }
+
parse_error = false;
sudoers_strict = strict;
sudoers_verbose = verbose;
@@ -1809,9 +1819,9 @@ init_parser_ext(const char *path, bool strict, int verbose)
}
bool
-init_parser(const char *path)
+init_parser(const char *file)
{
- return init_parser_ext(path, false, 1);
+ return init_parser_ext(file, NULL, false, 1);
}
/*
diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h
index c894c9467..36902b061 100644
--- a/plugins/sudoers/parse.h
+++ b/plugins/sudoers/parse.h
@@ -372,8 +372,8 @@ int check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet
/* gram.y */
extern struct sudoers_parse_tree parsed_policy;
extern bool (*sudoers_error_hook)(const char *file, int line, int column, const char *fmt, va_list args);
-bool init_parser(const char *path);
-bool init_parser_ext(const char *path, bool strict, int verbose);
+bool init_parser(const char *file);
+bool init_parser_ext(const char *file, const char *path, bool strict, int verbose);
void free_member(struct member *m);
void free_members(struct member_list *members);
void free_cmndspec(struct cmndspec *cs, struct cmndspec_list *csl);
diff --git a/plugins/sudoers/regress/fuzz/fuzz_sudoers.c b/plugins/sudoers/regress/fuzz/fuzz_sudoers.c
index d2b89ff23..602db6111 100644
--- a/plugins/sudoers/regress/fuzz/fuzz_sudoers.c
+++ b/plugins/sudoers/regress/fuzz/fuzz_sudoers.c
@@ -312,7 +312,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
/* Initialize defaults and parse sudoers. */
init_defaults();
- init_parser_ext("sudoers", true, 1);
+ init_parser_ext("sudoers", NULL, true, 1);
sudoersrestart(fp);
sudoersparse();
reparent_parse_tree(&parse_tree);
diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h
index 8858f3e3f..621d228f1 100644
--- a/plugins/sudoers/sudoers.h
+++ b/plugins/sudoers/sudoers.h
@@ -335,6 +335,7 @@ void sudoersrestart(FILE *);
extern FILE *sudoersin;
extern const char *sudoers_file;
extern char *sudoers;
+extern char *sudoers_search_path;
extern mode_t sudoers_mode;
extern uid_t sudoers_uid;
extern gid_t sudoers_gid;
diff --git a/plugins/sudoers/testsudoers.c b/plugins/sudoers/testsudoers.c
index c3121daf0..673046183 100644
--- a/plugins/sudoers/testsudoers.c
+++ b/plugins/sudoers/testsudoers.c
@@ -274,7 +274,7 @@ main(int argc, char *argv[])
}
/* Initialize the parser and set sudoers filename to "sudoers". */
- init_parser_ext("sudoers", true, 2);
+ init_parser_ext("sudoers", NULL, true, 2);
/*
* Set runas passwd/group entries based on command line or sudoers.
diff --git a/plugins/sudoers/toke.c b/plugins/sudoers/toke.c
index 76585d0e4..750178fba 100644
--- a/plugins/sudoers/toke.c
+++ b/plugins/sudoers/toke.c
@@ -3089,6 +3089,7 @@ char *sudoerstext;
int sudolineno; /* current sudoers line number. */
char *sudoers; /* sudoers file being parsed. */
+char *sudoers_search_path; /* colon-separated path of sudoers files. */
const char *sudoers_errstr; /* description of last error from lexer. */
struct sudolinebuf sudolinebuf; /* sudoers line being parsed. */
@@ -3135,7 +3136,7 @@ int (*trace_print)(const char *msg) = sudoers_trace_print;
-#line 3133 "toke.c"
+#line 3134 "toke.c"
#define INITIAL 0
#define GOTDEFS 1
@@ -3356,9 +3357,9 @@ YY_DECL
}
{
-#line 124 "toke.l"
+#line 125 "toke.l"
-#line 3356 "toke.c"
+#line 3357 "toke.c"
while ( 1 ) /* loops until end-of-file is reached */
{
@@ -3418,7 +3419,7 @@ do_action: /* This label is used only to access EOF actions. */
case 1:
YY_RULE_SETUP
-#line 125 "toke.l"
+#line 126 "toke.l"
{
LEXTRACE(", ");
return ',';
@@ -3426,12 +3427,12 @@ YY_RULE_SETUP
YY_BREAK
case 2:
YY_RULE_SETUP
-#line 130 "toke.l"
+#line 131 "toke.l"
BEGIN STARTDEFS;
YY_BREAK
case 3:
YY_RULE_SETUP
-#line 132 "toke.l"
+#line 133 "toke.l"
{
BEGIN INDEFS;
LEXTRACE("DEFVAR ");
@@ -3443,7 +3444,7 @@ YY_RULE_SETUP
case 4:
YY_RULE_SETUP
-#line 141 "toke.l"
+#line 142 "toke.l"
{
BEGIN STARTDEFS;
LEXTRACE(", ");
@@ -3452,7 +3453,7 @@ YY_RULE_SETUP
YY_BREAK
case 5:
YY_RULE_SETUP
-#line 147 "toke.l"
+#line 148 "toke.l"
{
LEXTRACE("= ");
return '=';
@@ -3460,7 +3461,7 @@ YY_RULE_SETUP
YY_BREAK
case 6:
YY_RULE_SETUP
-#line 152 "toke.l"
+#line 153 "toke.l"
{
LEXTRACE("+= ");
return '+';
@@ -3468,7 +3469,7 @@ YY_RULE_SETUP
YY_BREAK
case 7:
YY_RULE_SETUP
-#line 157 "toke.l"
+#line 158 "toke.l"
{
LEXTRACE("-= ");
return '-';
@@ -3476,7 +3477,7 @@ YY_RULE_SETUP
YY_BREAK
case 8:
YY_RULE_SETUP
-#line 162 "toke.l"
+#line 163 "toke.l"
{
LEXTRACE("BEGINSTR ");
sudoerslval.string = NULL;
@@ -3486,7 +3487,7 @@ YY_RULE_SETUP
YY_BREAK
case 9:
YY_RULE_SETUP
-#line 169 "toke.l"
+#line 170 "toke.l"
{
LEXTRACE("WORD(2) ");
if (!fill(sudoerstext, sudoersleng))
@@ -3499,7 +3500,7 @@ YY_RULE_SETUP
case 10:
/* rule 10 can match eol */
YY_RULE_SETUP
-#line 178 "toke.l"
+#line 179 "toke.l"
{
/* Line continuation char followed by newline. */
sudolineno++;
@@ -3508,7 +3509,7 @@ YY_RULE_SETUP
YY_BREAK
case 11:
YY_RULE_SETUP
-#line 184 "toke.l"
+#line 185 "toke.l"
{
LEXTRACE("ENDSTR ");
BEGIN prev_state;
@@ -3550,7 +3551,7 @@ YY_RULE_SETUP
YY_BREAK
case 12:
YY_RULE_SETUP
-#line 223 "toke.l"
+#line 224 "toke.l"
{
LEXTRACE("BACKSLASH ");
if (!append(sudoerstext, sudoersleng))
@@ -3559,7 +3560,7 @@ YY_RULE_SETUP
YY_BREAK
case 13:
YY_RULE_SETUP
-#line 229 "toke.l"
+#line 230 "toke.l"
{
LEXTRACE("STRBODY ");
if (!append(sudoerstext, sudoersleng))
@@ -3570,7 +3571,7 @@ YY_RULE_SETUP
case 14:
YY_RULE_SETUP
-#line 237 "toke.l"
+#line 238 "toke.l"
{
/* quoted fnmatch glob char, pass verbatim */
LEXTRACE("QUOTEDCHAR ");
@@ -3581,7 +3582,7 @@ YY_RULE_SETUP
YY_BREAK
case 15:
YY_RULE_SETUP
-#line 245 "toke.l"
+#line 246 "toke.l"
{
/* quoted sudoers special char, strip backslash */
LEXTRACE("QUOTEDCHAR ");
@@ -3593,7 +3594,7 @@ YY_RULE_SETUP
case 16:
/* rule 16 can match eol */
YY_RULE_SETUP
-#line 253 "toke.l"
+#line 254 "toke.l"
{
BEGIN INITIAL;
sudoersless(0);
@@ -3603,7 +3604,7 @@ YY_RULE_SETUP
YY_BREAK
case 17:
YY_RULE_SETUP
-#line 260 "toke.l"
+#line 261 "toke.l"
{
if (sudoerslval.command.args == NULL && sudoerstext[0] == '^') {
LEXTRACE("ARG REGEX ");
@@ -3622,7 +3623,7 @@ YY_RULE_SETUP
case 18:
YY_RULE_SETUP
-#line 276 "toke.l"
+#line 277 "toke.l"
{
/* quoted character, pass verbatim */
LEXTRACE("QUOTEDCHAR ");
@@ -3633,7 +3634,7 @@ YY_RULE_SETUP
case 19:
/* rule 19 can match eol */
YY_RULE_SETUP
-#line 283 "toke.l"
+#line 284 "toke.l"
{
/* Let the parser attempt to recover. */
sudoersless(0);
@@ -3647,7 +3648,7 @@ YY_RULE_SETUP
YY_BREAK
case 20:
YY_RULE_SETUP
-#line 294 "toke.l"
+#line 295 "toke.l"
{
if (!fill_args("$", 1, false))
yyterminate();
@@ -3664,7 +3665,7 @@ YY_RULE_SETUP
YY_BREAK
case 21:
YY_RULE_SETUP
-#line 308 "toke.l"
+#line 309 "toke.l"
{
if (continued) {
/* remove whitespace after line continuation */
@@ -3683,7 +3684,7 @@ YY_RULE_SETUP
case 22:
YY_RULE_SETUP
-#line 324 "toke.l"
+#line 325 "toke.l"
{
/* Only return DIGEST if the length is correct. */
yy_size_t digest_len =
@@ -3701,7 +3702,7 @@ YY_RULE_SETUP
YY_BREAK
case 23:
YY_RULE_SETUP
-#line 339 "toke.l"
+#line 340 "toke.l"
{
/* Only return DIGEST if the length is correct. */
yy_size_t len, digest_len =
@@ -3726,7 +3727,7 @@ YY_RULE_SETUP
YY_BREAK
case 24:
YY_RULE_SETUP
-#line 361 "toke.l"
+#line 362 "toke.l"
{
if (continued) {
sudoers_errstr = N_("invalid line continuation");
@@ -3741,7 +3742,7 @@ YY_RULE_SETUP
YY_BREAK
case 25:
YY_RULE_SETUP
-#line 373 "toke.l"
+#line 374 "toke.l"
{
if (continued) {
sudoers_errstr = N_("invalid line continuation");
@@ -3757,7 +3758,7 @@ YY_RULE_SETUP
case 26:
/* rule 26 can match eol */
YY_RULE_SETUP
-#line 385 "toke.l"
+#line 386 "toke.l"
{
if (continued) {
sudoers_errstr = N_("invalid line continuation");
@@ -3777,7 +3778,7 @@ YY_RULE_SETUP
case 27:
/* rule 27 can match eol */
YY_RULE_SETUP
-#line 401 "toke.l"
+#line 402 "toke.l"
{
if (continued) {
sudoers_errstr = N_("invalid line continuation");
@@ -3796,7 +3797,7 @@ YY_RULE_SETUP
YY_BREAK
case 28:
YY_RULE_SETUP
-#line 417 "toke.l"
+#line 418 "toke.l"
{
char deftype;
int n;
@@ -3840,7 +3841,7 @@ YY_RULE_SETUP
YY_BREAK
case 29:
YY_RULE_SETUP
-#line 458 "toke.l"
+#line 459 "toke.l"
{
int n;
@@ -3870,7 +3871,7 @@ YY_RULE_SETUP
YY_BREAK
case 30:
YY_RULE_SETUP
-#line 485 "toke.l"
+#line 486 "toke.l"
{
/* cmnd does not require passwd for this user */
LEXTRACE("NOPASSWD ");
@@ -3879,7 +3880,7 @@ YY_RULE_SETUP
YY_BREAK
case 31:
YY_RULE_SETUP
-#line 491 "toke.l"
+#line 492 "toke.l"
{
/* cmnd requires passwd for this user */
LEXTRACE("PASSWD ");
@@ -3888,7 +3889,7 @@ YY_RULE_SETUP
YY_BREAK
case 32:
YY_RULE_SETUP
-#line 497 "toke.l"
+#line 498 "toke.l"
{
LEXTRACE("NOEXEC ");
return NOEXEC;
@@ -3896,7 +3897,7 @@ YY_RULE_SETUP
YY_BREAK
case 33:
YY_RULE_SETUP
-#line 502 "toke.l"
+#line 503 "toke.l"
{
LEXTRACE("EXEC ");
return EXEC;
@@ -3904,7 +3905,7 @@ YY_RULE_SETUP
YY_BREAK
case 34:
YY_RULE_SETUP
-#line 507 "toke.l"
+#line 508 "toke.l"
{
LEXTRACE("INTERCEPT ");
return INTERCEPT;
@@ -3912,7 +3913,7 @@ YY_RULE_SETUP
YY_BREAK
case 35:
YY_RULE_SETUP
-#line 512 "toke.l"
+#line 513 "toke.l"
{
LEXTRACE("NOINTERCEPT ");
return NOINTERCEPT;
@@ -3920,7 +3921,7 @@ YY_RULE_SETUP
YY_BREAK
case 36:
YY_RULE_SETUP
-#line 517 "toke.l"
+#line 518 "toke.l"
{
LEXTRACE("SETENV ");
return SETENV;
@@ -3928,7 +3929,7 @@ YY_RULE_SETUP
YY_BREAK
case 37:
YY_RULE_SETUP
-#line 522 "toke.l"
+#line 523 "toke.l"
{
LEXTRACE("NOSETENV ");
return NOSETENV;
@@ -3936,7 +3937,7 @@ YY_RULE_SETUP
YY_BREAK
case 38:
YY_RULE_SETUP
-#line 527 "toke.l"
+#line 528 "toke.l"
{
LEXTRACE("LOG_OUTPUT ");
return LOG_OUTPUT;
@@ -3944,7 +3945,7 @@ YY_RULE_SETUP
YY_BREAK
case 39:
YY_RULE_SETUP
-#line 532 "toke.l"
+#line 533 "toke.l"
{
LEXTRACE("NOLOG_OUTPUT ");
return NOLOG_OUTPUT;
@@ -3952,7 +3953,7 @@ YY_RULE_SETUP
YY_BREAK
case 40:
YY_RULE_SETUP
-#line 537 "toke.l"
+#line 538 "toke.l"
{
LEXTRACE("LOG_INPUT ");
return LOG_INPUT;
@@ -3960,7 +3961,7 @@ YY_RULE_SETUP
YY_BREAK
case 41:
YY_RULE_SETUP
-#line 542 "toke.l"
+#line 543 "toke.l"
{
LEXTRACE("NOLOG_INPUT ");
return NOLOG_INPUT;
@@ -3968,7 +3969,7 @@ YY_RULE_SETUP
YY_BREAK
case 42:
YY_RULE_SETUP
-#line 547 "toke.l"
+#line 548 "toke.l"
{
LEXTRACE("MAIL ");
return MAIL;
@@ -3976,7 +3977,7 @@ YY_RULE_SETUP
YY_BREAK
case 43:
YY_RULE_SETUP
-#line 552 "toke.l"
+#line 553 "toke.l"
{
LEXTRACE("NOMAIL ");
return NOMAIL;
@@ -3984,7 +3985,7 @@ YY_RULE_SETUP
YY_BREAK
case 44:
YY_RULE_SETUP
-#line 557 "toke.l"
+#line 558 "toke.l"
{
LEXTRACE("FOLLOW ");
return FOLLOWLNK;
@@ -3992,7 +3993,7 @@ YY_RULE_SETUP
YY_BREAK
case 45:
YY_RULE_SETUP
-#line 562 "toke.l"
+#line 563 "toke.l"
{
LEXTRACE("NOFOLLOW ");
return NOFOLLOWLNK;
@@ -4000,7 +4001,7 @@ YY_RULE_SETUP
YY_BREAK
case 46:
YY_RULE_SETUP
-#line 567 "toke.l"
+#line 568 "toke.l"
{
if (sudoerstext[0] == '+')
sudoers_errstr = N_("empty netgroup");
@@ -4012,7 +4013,7 @@ YY_RULE_SETUP
YY_BREAK
case 47:
YY_RULE_SETUP
-#line 576 "toke.l"
+#line 577 "toke.l"
{
/* netgroup */
if (!fill(sudoerstext, sudoersleng))
@@ -4023,7 +4024,7 @@ YY_RULE_SETUP
YY_BREAK
case 48:
YY_RULE_SETUP
-#line 584 "toke.l"
+#line 585 "toke.l"
{
/* group */
if (!fill(sudoerstext, sudoersleng))
@@ -4034,7 +4035,7 @@ YY_RULE_SETUP
YY_BREAK
case 49:
YY_RULE_SETUP
-#line 592 "toke.l"
+#line 593 "toke.l"
{
if (!fill(sudoerstext, sudoersleng))
yyterminate();
@@ -4044,7 +4045,7 @@ YY_RULE_SETUP
YY_BREAK
case 50:
YY_RULE_SETUP
-#line 599 "toke.l"
+#line 600 "toke.l"
{
if (!fill(sudoerstext, sudoersleng))
yyterminate();
@@ -4054,7 +4055,7 @@ YY_RULE_SETUP
YY_BREAK
case 51:
YY_RULE_SETUP
-#line 606 "toke.l"
+#line 607 "toke.l"
{
if (!ipv6_valid(sudoerstext)) {
sudoers_errstr = N_("invalid IPv6 address");
@@ -4069,7 +4070,7 @@ YY_RULE_SETUP
YY_BREAK
case 52:
YY_RULE_SETUP
-#line 618 "toke.l"
+#line 619 "toke.l"
{
if (!ipv6_valid(sudoerstext)) {
sudoers_errstr = N_("invalid IPv6 address");
@@ -4084,7 +4085,7 @@ YY_RULE_SETUP
YY_BREAK
case 53:
YY_RULE_SETUP
-#line 630 "toke.l"
+#line 631 "toke.l"
{
LEXTRACE("ALL ");
return ALL;
@@ -4093,7 +4094,7 @@ YY_RULE_SETUP
YY_BREAK
case 54:
YY_RULE_SETUP
-#line 636 "toke.l"
+#line 637 "toke.l"
{
LEXTRACE("CMND_TIMEOUT ");
return CMND_TIMEOUT;
@@ -4101,7 +4102,7 @@ YY_RULE_SETUP
YY_BREAK
case 55:
YY_RULE_SETUP
-#line 641 "toke.l"
+#line 642 "toke.l"
{
LEXTRACE("NOTBEFORE ");
return NOTBEFORE;
@@ -4109,7 +4110,7 @@ YY_RULE_SETUP
YY_BREAK
case 56:
YY_RULE_SETUP
-#line 646 "toke.l"
+#line 647 "toke.l"
{
LEXTRACE("NOTAFTER ");
return NOTAFTER;
@@ -4117,7 +4118,7 @@ YY_RULE_SETUP
YY_BREAK
case 57:
YY_RULE_SETUP
-#line 651 "toke.l"
+#line 652 "toke.l"
{
LEXTRACE("CWD ");
prev_state = YY_START;
@@ -4127,7 +4128,7 @@ YY_RULE_SETUP
YY_BREAK
case 58:
YY_RULE_SETUP
-#line 658 "toke.l"
+#line 659 "toke.l"
{
LEXTRACE("CHROOT ");
prev_state = YY_START;
@@ -4137,7 +4138,7 @@ YY_RULE_SETUP
YY_BREAK
case 59:
YY_RULE_SETUP
-#line 665 "toke.l"
+#line 666 "toke.l"
{
#ifdef HAVE_SELINUX
LEXTRACE("ROLE ");
@@ -4149,7 +4150,7 @@ YY_RULE_SETUP
YY_BREAK
case 60:
YY_RULE_SETUP
-#line 674 "toke.l"
+#line 675 "toke.l"
{
#ifdef HAVE_SELINUX
LEXTRACE("TYPE ");
@@ -4161,7 +4162,7 @@ YY_RULE_SETUP
YY_BREAK
case 61:
YY_RULE_SETUP
-#line 682 "toke.l"
+#line 683 "toke.l"
{
#ifdef HAVE_APPARMOR
LEXTRACE("APPARMOR_PROFILE ");
@@ -4173,7 +4174,7 @@ YY_RULE_SETUP
YY_BREAK
case 62:
YY_RULE_SETUP
-#line 690 "toke.l"
+#line 691 "toke.l"
{
#ifdef HAVE_PRIV_SET
LEXTRACE("PRIVS ");
@@ -4185,7 +4186,7 @@ YY_RULE_SETUP
YY_BREAK
case 63:
YY_RULE_SETUP
-#line 699 "toke.l"
+#line 700 "toke.l"
{
#ifdef HAVE_PRIV_SET
LEXTRACE("LIMITPRIVS ");
@@ -4197,7 +4198,7 @@ YY_RULE_SETUP
YY_BREAK
case 64:
YY_RULE_SETUP
-#line 708 "toke.l"
+#line 709 "toke.l"
{
got_alias:
if (!fill(sudoerstext, sudoersleng))
@@ -4208,7 +4209,7 @@ YY_RULE_SETUP
YY_BREAK
case 65:
YY_RULE_SETUP
-#line 716 "toke.l"
+#line 717 "toke.l"
{
/* XXX - no way to specify digest for command */
/* no command args allowed for Defaults!/path */
@@ -4220,7 +4221,7 @@ YY_RULE_SETUP
YY_BREAK
case 66:
YY_RULE_SETUP
-#line 725 "toke.l"
+#line 726 "toke.l"
{
digest_type = SUDO_DIGEST_SHA224;
BEGIN WANTDIGEST;
@@ -4230,7 +4231,7 @@ YY_RULE_SETUP
YY_BREAK
case 67:
YY_RULE_SETUP
-#line 732 "toke.l"
+#line 733 "toke.l"
{
digest_type = SUDO_DIGEST_SHA256;
BEGIN WANTDIGEST;
@@ -4240,7 +4241,7 @@ YY_RULE_SETUP
YY_BREAK
case 68:
YY_RULE_SETUP
-#line 739 "toke.l"
+#line 740 "toke.l"
{
digest_type = SUDO_DIGEST_SHA384;
BEGIN WANTDIGEST;
@@ -4250,7 +4251,7 @@ YY_RULE_SETUP
YY_BREAK
case 69:
YY_RULE_SETUP
-#line 746 "toke.l"
+#line 747 "toke.l"
{
digest_type = SUDO_DIGEST_SHA512;
BEGIN WANTDIGEST;
@@ -4260,7 +4261,7 @@ YY_RULE_SETUP
YY_BREAK
case 70:
YY_RULE_SETUP
-#line 753 "toke.l"
+#line 754 "toke.l"
{
BEGIN GOTCMND;
LEXTRACE("COMMAND ");
@@ -4270,7 +4271,7 @@ YY_RULE_SETUP
YY_BREAK
case 71:
YY_RULE_SETUP
-#line 760 "toke.l"
+#line 761 "toke.l"
{
BEGIN prev_state;
if (!fill(sudoerstext, sudoersleng))
@@ -4281,7 +4282,7 @@ YY_RULE_SETUP
YY_BREAK
case 72:
YY_RULE_SETUP
-#line 768 "toke.l"
+#line 769 "toke.l"
{
/* directories can't have args... */
if (sudoerstext[sudoersleng - 1] == '/') {
@@ -4298,7 +4299,7 @@ YY_RULE_SETUP
YY_BREAK
case 73:
YY_RULE_SETUP
-#line 782 "toke.l"
+#line 783 "toke.l"
{
if (sudoers_strict) {
if (!sudo_regex_compile(NULL, sudoerstext, &sudoers_errstr)) {
@@ -4314,7 +4315,7 @@ YY_RULE_SETUP
YY_BREAK
case 74:
YY_RULE_SETUP
-#line 795 "toke.l"
+#line 796 "toke.l"
{
LEXTRACE("BEGINSTR ");
sudoerslval.string = NULL;
@@ -4324,7 +4325,7 @@ YY_RULE_SETUP
YY_BREAK
case 75:
YY_RULE_SETUP
-#line 802 "toke.l"
+#line 803 "toke.l"
{
/* a word */
if (!fill(sudoerstext, sudoersleng))
@@ -4336,7 +4337,7 @@ YY_RULE_SETUP
case 76:
YY_RULE_SETUP
-#line 811 "toke.l"
+#line 812 "toke.l"
{
/* include file/directory */
if (!fill(sudoerstext, sudoersleng))
@@ -4348,7 +4349,7 @@ YY_RULE_SETUP
YY_BREAK
case 77:
YY_RULE_SETUP
-#line 820 "toke.l"
+#line 821 "toke.l"
{
LEXTRACE("BEGINSTR ");
sudoerslval.string = NULL;
@@ -4359,7 +4360,7 @@ YY_RULE_SETUP
case 78:
YY_RULE_SETUP
-#line 828 "toke.l"
+#line 829 "toke.l"
{
LEXTRACE("( ");
return '(';
@@ -4367,7 +4368,7 @@ YY_RULE_SETUP
YY_BREAK
case 79:
YY_RULE_SETUP
-#line 833 "toke.l"
+#line 834 "toke.l"
{
LEXTRACE(") ");
return ')';
@@ -4375,7 +4376,7 @@ YY_RULE_SETUP
YY_BREAK
case 80:
YY_RULE_SETUP
-#line 838 "toke.l"
+#line 839 "toke.l"
{
LEXTRACE(", ");
return ',';
@@ -4383,7 +4384,7 @@ YY_RULE_SETUP
YY_BREAK
case 81:
YY_RULE_SETUP
-#line 843 "toke.l"
+#line 844 "toke.l"
{
LEXTRACE("= ");
return '=';
@@ -4391,7 +4392,7 @@ YY_RULE_SETUP
YY_BREAK
case 82:
YY_RULE_SETUP
-#line 848 "toke.l"
+#line 849 "toke.l"
{
LEXTRACE(": ");
return ':';
@@ -4399,7 +4400,7 @@ YY_RULE_SETUP
YY_BREAK
case 83:
YY_RULE_SETUP
-#line 853 "toke.l"
+#line 854 "toke.l"
{
if (sudoersleng & 1) {
LEXTRACE("!");
@@ -4410,7 +4411,7 @@ YY_RULE_SETUP
case 84:
/* rule 84 can match eol */
YY_RULE_SETUP
-#line 860 "toke.l"
+#line 861 "toke.l"
{
if (YY_START == INSTR) {
/* throw away old string */
@@ -4432,7 +4433,7 @@ YY_RULE_SETUP
YY_BREAK
case 85:
YY_RULE_SETUP
-#line 879 "toke.l"
+#line 880 "toke.l"
{ /* throw away space/tabs */
sawspace = true; /* but remember for fill_args */
}
@@ -4440,7 +4441,7 @@ YY_RULE_SETUP
case 86:
/* rule 86 can match eol */
YY_RULE_SETUP
-#line 883 "toke.l"
+#line 884 "toke.l"
{
sawspace = true; /* remember for fill_args */
sudolineno++;
@@ -4450,7 +4451,7 @@ YY_RULE_SETUP
case 87:
/* rule 87 can match eol */
YY_RULE_SETUP
-#line 889 "toke.l"
+#line 890 "toke.l"
{
if (sudoerstext[sudoersleng - 1] == '\n') {
/* comment ending in a newline */
@@ -4468,7 +4469,7 @@ YY_RULE_SETUP
YY_BREAK
case 88:
YY_RULE_SETUP
-#line 904 "toke.l"
+#line 905 "toke.l"
{
LEXTRACE("NOMATCH ");
return NOMATCH;
@@ -4484,7 +4485,7 @@ case YY_STATE_EOF(INSTR):
case YY_STATE_EOF(WANTDIGEST):
case YY_STATE_EOF(GOTINC):
case YY_STATE_EOF(EXPECTPATH):
-#line 909 "toke.l"
+#line 910 "toke.l"
{
if (!pop_include())
yyterminate();
@@ -4492,10 +4493,10 @@ case YY_STATE_EOF(EXPECTPATH):
YY_BREAK
case 89:
YY_RULE_SETUP
-#line 914 "toke.l"
+#line 915 "toke.l"
ECHO;
YY_BREAK
-#line 4493 "toke.c"
+#line 4494 "toke.c"
case YY_END_OF_BUFFER:
{
@@ -5456,7 +5457,7 @@ void sudoersfree (void * ptr )
#define YYTABLES_NAME "yytables"
-#line 914 "toke.l"
+#line 915 "toke.l"
struct path_list {
@@ -5469,7 +5470,8 @@ SLIST_HEAD(path_list_head, path_list);
struct include_stack {
struct sudolinebuf line;
YY_BUFFER_STATE bs;
- char *path;
+ char *path; /* search path */
+ char *file;
struct path_list_head more; /* more files in case of includedir */
int lineno;
bool keepopen;
@@ -5504,6 +5506,7 @@ read_dir_files(const char *dirpath, struct path_list ***pathsp, int verbose)
const size_t dirlen = strlen(dirpath);
debug_decl(read_dir_files, SUDOERS_DEBUG_PARSER);
+ /* XXX - fdopendir */
dir = opendir(dirpath);
if (dir == NULL) {
if (errno == ENOENT)
@@ -5665,88 +5668,149 @@ init_lexer(void)
}
/*
+ * Like strlcpy() but expand %h escapes to user_shost.
+ */
+static size_t
+strlcpy_expand_host(char *dst, const char *src, size_t size)
+{
+ size_t len = 0;
+ char ch;
+ debug_decl(strlcpy_expand_host, SUDOERS_DEBUG_PARSER);
+
+ while ((ch = *src++) != '\0') {
+ if (ch == '%' && *src == 'h') {
+ size_t n = strlcpy(dst, user_shost, size);
+ len += n;
+ if (n >= size) {
+ /* truncated */
+ n = size ? size - 1 : 0;
+ }
+ dst += n;
+ size -= n;
+ src++;
+ continue;
+ }
+ if (size > 1) {
+ *dst++ = ch;
+ size--;
+ len++;
+ }
+ }
+ if (size > 0)
+ *dst = '\0';
+
+ debug_return_size_t(len);
+}
+
+/*
* Expand any embedded %h (host) escapes in the given path and makes
* a relative path fully-qualified based on the current sudoers file.
- * Returns a reference-counted string.
+ * Returns a reference-counted string on success or NULL on failure.
*/
static char *
-expand_include(const char *opath)
+expand_include(const char *src)
{
+ const char *path = sudoers_search_path ? sudoers_search_path : sudoers;
+ const char *path_end = path + strlen(path);
const char *cp, *ep;
- char *path, *pp;
- size_t len, olen, dirlen = 0;
- bool subst = false;
+ char *dst0, *dst;
+ size_t dst_size, src_len;
+ unsigned int nhost = 0;
debug_decl(expand_include, SUDOERS_DEBUG_PARSER);
/* Strip double quotes if present. */
- olen = strlen(opath);
- if (olen > 1 && opath[0] == '"' && opath[olen - 1] == '"') {
- opath++;
- olen -= 2;
+ src_len = strlen(src);
+ if (src_len > 1 && src[0] == '"' && src[src_len - 1] == '"') {
+ src++;
+ src_len -= 2;
}
- if (olen == 0)
+ if (src_len == 0)
debug_return_ptr(NULL);
- /* Relative paths are located in the same dir as the sudoers file. */
- if (*opath != '/') {
- char *dirend = strrchr(sudoers, '/');
- if (dirend != NULL)
- dirlen = (size_t)(dirend - sudoers) + 1;
- }
-
- cp = opath;
- ep = opath + olen;
- len = olen;
+ /* Check for %h escapes in src. */
+ cp = src;
+ ep = src + src_len;
while (cp < ep) {
if (cp[0] == '%' && cp[1] == 'h') {
- subst = true;
- len += strlen(user_shost);
+ nhost++;
cp += 2;
continue;
}
cp++;
}
+ if (*src == '/') {
+ /* Fully-qualified path, make a copy and expand %h escapes. */
+ dst_size = src_len + (nhost * strlen(user_shost)) - (nhost * 2) + 1;
+ dst0 = sudo_rcstr_alloc(dst_size - 1);
+ if (dst0 == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ sudoerserror(NULL);
+ debug_return_str(NULL);
+ }
+ if (strlcpy_expand_host(dst0, src, dst_size) >= dst_size)
+ goto oflow;
+ debug_return_str(dst0);
+ }
+
+ /*
+ * Relative paths are located in the same dir as the sudoers file.
+ * If the current sudoers file was opened via a colon-separated path,
+ * use the same path when opening src.
+ */
+ dst_size = 0;
+ for (cp = sudo_strsplit(path, path_end, ":", &ep); cp != NULL;
+ cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
+ char *dirend = memrchr(cp, '/', ep - cp);
+ if (dirend != NULL) {
+ dst_size += (size_t)(dirend - cp) + 1;
+ }
+ /* Includes space for ':' separator and NUL terminator. */
+ dst_size += src_len + (nhost * strlen(user_shost)) - (nhost * 2) + 1;
+ }
+
/* Make a copy of the fully-qualified path and return it. */
- path = pp = sudo_rcstr_alloc(dirlen + len);
- if (path == NULL) {
+ dst = dst0 = sudo_rcstr_alloc(dst_size - 1);
+ if (dst0 == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
sudoerserror(NULL);
debug_return_str(NULL);
}
- if (dirlen) {
- memcpy(path, sudoers, dirlen);
- pp += dirlen;
- }
- if (subst) {
- /* substitute for %h */
- cp = opath;
- while (cp < ep) {
- if (cp[0] == '%' && cp[1] == 'h') {
- size_t n = strlcpy(pp, user_shost, len + 1);
- if (n >= len + 1)
- goto oflow;
- cp += 2;
- pp += n;
- len -= n;
- continue;
- }
- if (len < 1)
+ for (cp = sudo_strsplit(path, path_end, ":", &ep); cp != NULL;
+ cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
+ size_t len;
+ char *dirend;
+
+ if (cp != path) {
+ if (dst_size < 2)
goto oflow;
- *pp++ = *cp++;
- len--;
+ *dst++ = ':';
+ dst_size--;
}
- *pp = '\0';
- } else {
- memcpy(pp, opath, len);
- pp[len] = '\0';
+
+ dirend = memrchr(cp, '/', ep - cp);
+ if (dirend != NULL) {
+ len = (size_t)(dirend - cp) + 1;
+ if (len >= dst_size)
+ goto oflow;
+ memcpy(dst, cp, len);
+ dst += len;
+ dst_size -= len;
+ }
+
+ len = strlcpy_expand_host(dst, src, dst_size);
+ if (len >= dst_size)
+ goto oflow;
+ dst += len;
+ dst_size -= len;
}
+ *dst = '\0';
- debug_return_str(path);
+ debug_return_str(dst0);
oflow:
sudo_warnx(U_("internal error, %s overflow"), __func__);
sudoerserror(NULL);
- sudo_rcstr_delref(path);
+ free(dst0);
debug_return_str(NULL);
}
@@ -5760,7 +5824,7 @@ static bool
push_include_int(const char *opath, bool isdir, int verbose)
{
struct path_list *pl;
- char *path;
+ char *file = NULL, *path;
FILE *fp;
debug_decl(push_include, SUDOERS_DEBUG_PARSER);
@@ -5773,7 +5837,8 @@ push_include_int(const char *opath, bool isdir, int verbose)
if (idepth > MAX_SUDOERS_DEPTH) {
if (verbose > 0) {
- fprintf(stderr, U_("%s: %s"), path, U_("too many levels of includes"));
+ fprintf(stderr, U_("%s: %s"), path,
+ U_("too many levels of includes"));
fputc('\n', stderr);
}
sudoerserror(NULL);
@@ -5793,9 +5858,12 @@ push_include_int(const char *opath, bool isdir, int verbose)
SLIST_INIT(&istack[idepth].more);
if (isdir) {
struct stat sb;
- int count, status;
+ char dname[PATH_MAX];
+ int count, fd, status;
- status = sudo_secure_dir(path, sudoers_uid, sudoers_gid, &sb);
+ fd = sudo_open_conf_path(path, dname, sizeof(dname), NULL);
+ status = sudo_secure_fd(fd, S_IFDIR, sudoers_uid, sudoers_gid, &sb);
+ close(fd); /* XXX use in read_dir_files? */
if (status != SUDO_PATH_SECURE) {
if (verbose > 0) {
switch (status) {
@@ -5824,7 +5892,7 @@ push_include_int(const char *opath, bool isdir, int verbose)
sudo_rcstr_delref(path);
debug_return_bool(true);
}
- count = switch_dir(&istack[idepth], path, verbose);
+ count = switch_dir(&istack[idepth], dname, verbose);
if (count <= 0) {
/* switch_dir() called sudoerserror() for us */
sudo_rcstr_delref(path);
@@ -5833,6 +5901,7 @@ push_include_int(const char *opath, bool isdir, int verbose)
/* Parse the first dir entry we can open, leave the rest for later. */
do {
+ sudo_rcstr_delref(file);
sudo_rcstr_delref(path);
if ((pl = SLIST_FIRST(&istack[idepth].more)) == NULL) {
/* Unable to open any files in include dir, not an error. */
@@ -5841,24 +5910,32 @@ push_include_int(const char *opath, bool isdir, int verbose)
SLIST_REMOVE_HEAD(&istack[idepth].more, entries);
path = pl->path;
free(pl);
- } while ((fp = open_sudoers(path, NULL, false, &keepopen)) == NULL);
+ /* The file and path and the same for sudoers.d files. */
+ file = path;
+ sudo_rcstr_addref(file);
+ } while ((fp = open_sudoers(file, NULL, false, &keepopen)) == NULL);
} else {
- if ((fp = open_sudoers(path, NULL, true, &keepopen)) == NULL) {
+ if ((fp = open_sudoers(path, &file, true, &keepopen)) == NULL) {
/* The error was already printed by open_sudoers() */
sudoerserror(NULL);
sudo_rcstr_delref(path);
debug_return_bool(false);
}
}
- /* Push the old (current) file and open the new one. */
- istack[idepth].path = sudoers; /* push old path (and its ref) */
+ /*
+ * Push the old (current) file and open the new one.
+ * We use the existing refs of sudoers and sudoers_search_path.
+ */
+ istack[idepth].file = sudoers;
+ istack[idepth].path = sudoers_search_path;
istack[idepth].line = sudolinebuf;
istack[idepth].bs = YY_CURRENT_BUFFER;
istack[idepth].lineno = sudolineno;
istack[idepth].keepopen = keepopen;
idepth++;
sudolineno = 1;
- sudoers = path;
+ sudoers = file;
+ sudoers_search_path = path;
sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
memset(&sudolinebuf, 0, sizeof(sudolinebuf));
@@ -5903,7 +5980,10 @@ pop_include(void)
sudolinebuf.len = sudolinebuf.off = 0;
sudolinebuf.toke_start = sudolinebuf.toke_end = 0;
sudo_rcstr_delref(sudoers);
- sudoers = pl->path;
+ sudo_rcstr_delref(sudoers_search_path);
+ sudoers_search_path = pl->path;
+ sudoers = sudoers_search_path;
+ sudo_rcstr_addref(sudoers);
sudolineno = 1;
sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
free(pl);
@@ -5920,7 +6000,9 @@ pop_include(void)
free(sudolinebuf.buf);
sudolinebuf = istack[idepth].line;
sudo_rcstr_delref(sudoers);
- sudoers = istack[idepth].path;
+ sudoers = istack[idepth].file;
+ sudo_rcstr_delref(sudoers_search_path);
+ sudoers_search_path = istack[idepth].path;
sudolineno = istack[idepth].lineno;
keepopen = istack[idepth].keepopen;
}
diff --git a/plugins/sudoers/toke.l b/plugins/sudoers/toke.l
index a9111a2f6..3a9990e05 100644
--- a/plugins/sudoers/toke.l
+++ b/plugins/sudoers/toke.l
@@ -55,6 +55,7 @@
int sudolineno; /* current sudoers line number. */
char *sudoers; /* sudoers file being parsed. */
+char *sudoers_search_path; /* colon-separated path of sudoers files. */
const char *sudoers_errstr; /* description of last error from lexer. */
struct sudolinebuf sudolinebuf; /* sudoers line being parsed. */
@@ -922,7 +923,8 @@ SLIST_HEAD(path_list_head, path_list);
struct include_stack {
struct sudolinebuf line;
YY_BUFFER_STATE bs;
- char *path;
+ char *path; /* search path */
+ char *file;
struct path_list_head more; /* more files in case of includedir */
int lineno;
bool keepopen;
@@ -957,6 +959,7 @@ read_dir_files(const char *dirpath, struct path_list ***pathsp, int verbose)
const size_t dirlen = strlen(dirpath);
debug_decl(read_dir_files, SUDOERS_DEBUG_PARSER);
+ /* XXX - fdopendir */
dir = opendir(dirpath);
if (dir == NULL) {
if (errno == ENOENT)
@@ -1118,88 +1121,149 @@ init_lexer(void)
}
/*
+ * Like strlcpy() but expand %h escapes to user_shost.
+ */
+static size_t
+strlcpy_expand_host(char *dst, const char *src, size_t size)
+{
+ size_t len = 0;
+ char ch;
+ debug_decl(strlcpy_expand_host, SUDOERS_DEBUG_PARSER);
+
+ while ((ch = *src++) != '\0') {
+ if (ch == '%' && *src == 'h') {
+ size_t n = strlcpy(dst, user_shost, size);
+ len += n;
+ if (n >= size) {
+ /* truncated */
+ n = size ? size - 1 : 0;
+ }
+ dst += n;
+ size -= n;
+ src++;
+ continue;
+ }
+ if (size > 1) {
+ *dst++ = ch;
+ size--;
+ len++;
+ }
+ }
+ if (size > 0)
+ *dst = '\0';
+
+ debug_return_size_t(len);
+}
+
+/*
* Expand any embedded %h (host) escapes in the given path and makes
* a relative path fully-qualified based on the current sudoers file.
- * Returns a reference-counted string.
+ * Returns a reference-counted string on success or NULL on failure.
*/
static char *
-expand_include(const char *opath)
+expand_include(const char *src)
{
+ const char *path = sudoers_search_path ? sudoers_search_path : sudoers;
+ const char *path_end = path + strlen(path);
const char *cp, *ep;
- char *path, *pp;
- size_t len, olen, dirlen = 0;
- bool subst = false;
+ char *dst0, *dst;
+ size_t dst_size, src_len;
+ unsigned int nhost = 0;
debug_decl(expand_include, SUDOERS_DEBUG_PARSER);
/* Strip double quotes if present. */
- olen = strlen(opath);
- if (olen > 1 && opath[0] == '"' && opath[olen - 1] == '"') {
- opath++;
- olen -= 2;
+ src_len = strlen(src);
+ if (src_len > 1 && src[0] == '"' && src[src_len - 1] == '"') {
+ src++;
+ src_len -= 2;
}
- if (olen == 0)
+ if (src_len == 0)
debug_return_ptr(NULL);
- /* Relative paths are located in the same dir as the sudoers file. */
- if (*opath != '/') {
- char *dirend = strrchr(sudoers, '/');
- if (dirend != NULL)
- dirlen = (size_t)(dirend - sudoers) + 1;
- }
-
- cp = opath;
- ep = opath + olen;
- len = olen;
+ /* Check for %h escapes in src. */
+ cp = src;
+ ep = src + src_len;
while (cp < ep) {
if (cp[0] == '%' && cp[1] == 'h') {
- subst = true;
- len += strlen(user_shost);
+ nhost++;
cp += 2;
continue;
}
cp++;
}
+ if (*src == '/') {
+ /* Fully-qualified path, make a copy and expand %h escapes. */
+ dst_size = src_len + (nhost * strlen(user_shost)) - (nhost * 2) + 1;
+ dst0 = sudo_rcstr_alloc(dst_size - 1);
+ if (dst0 == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ sudoerserror(NULL);
+ debug_return_str(NULL);
+ }
+ if (strlcpy_expand_host(dst0, src, dst_size) >= dst_size)
+ goto oflow;
+ debug_return_str(dst0);
+ }
+
+ /*
+ * Relative paths are located in the same dir as the sudoers file.
+ * If the current sudoers file was opened via a colon-separated path,
+ * use the same path when opening src.
+ */
+ dst_size = 0;
+ for (cp = sudo_strsplit(path, path_end, ":", &ep); cp != NULL;
+ cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
+ char *dirend = memrchr(cp, '/', ep - cp);
+ if (dirend != NULL) {
+ dst_size += (size_t)(dirend - cp) + 1;
+ }
+ /* Includes space for ':' separator and NUL terminator. */
+ dst_size += src_len + (nhost * strlen(user_shost)) - (nhost * 2) + 1;
+ }
+
/* Make a copy of the fully-qualified path and return it. */
- path = pp = sudo_rcstr_alloc(dirlen + len);
- if (path == NULL) {
+ dst = dst0 = sudo_rcstr_alloc(dst_size - 1);
+ if (dst0 == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
sudoerserror(NULL);
debug_return_str(NULL);
}
- if (dirlen) {
- memcpy(path, sudoers, dirlen);
- pp += dirlen;
- }
- if (subst) {
- /* substitute for %h */
- cp = opath;
- while (cp < ep) {
- if (cp[0] == '%' && cp[1] == 'h') {
- size_t n = strlcpy(pp, user_shost, len + 1);
- if (n >= len + 1)
- goto oflow;
- cp += 2;
- pp += n;
- len -= n;
- continue;
- }
- if (len < 1)
+ for (cp = sudo_strsplit(path, path_end, ":", &ep); cp != NULL;
+ cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
+ size_t len;
+ char *dirend;
+
+ if (cp != path) {
+ if (dst_size < 2)
goto oflow;
- *pp++ = *cp++;
- len--;
+ *dst++ = ':';
+ dst_size--;
}
- *pp = '\0';
- } else {
- memcpy(pp, opath, len);
- pp[len] = '\0';
+
+ dirend = memrchr(cp, '/', ep - cp);
+ if (dirend != NULL) {
+ len = (size_t)(dirend - cp) + 1;
+ if (len >= dst_size)
+ goto oflow;
+ memcpy(dst, cp, len);
+ dst += len;
+ dst_size -= len;
+ }
+
+ len = strlcpy_expand_host(dst, src, dst_size);
+ if (len >= dst_size)
+ goto oflow;
+ dst += len;
+ dst_size -= len;
}
+ *dst = '\0';
- debug_return_str(path);
+ debug_return_str(dst0);
oflow:
sudo_warnx(U_("internal error, %s overflow"), __func__);
sudoerserror(NULL);
- sudo_rcstr_delref(path);
+ free(dst0);
debug_return_str(NULL);
}
@@ -1213,7 +1277,7 @@ static bool
push_include_int(const char *opath, bool isdir, int verbose)
{
struct path_list *pl;
- char *path;
+ char *file = NULL, *path;
FILE *fp;
debug_decl(push_include, SUDOERS_DEBUG_PARSER);
@@ -1226,7 +1290,8 @@ push_include_int(const char *opath, bool isdir, int verbose)
if (idepth > MAX_SUDOERS_DEPTH) {
if (verbose > 0) {
- fprintf(stderr, U_("%s: %s"), path, U_("too many levels of includes"));
+ fprintf(stderr, U_("%s: %s"), path,
+ U_("too many levels of includes"));
fputc('\n', stderr);
}
sudoerserror(NULL);
@@ -1246,9 +1311,12 @@ push_include_int(const char *opath, bool isdir, int verbose)
SLIST_INIT(&istack[idepth].more);
if (isdir) {
struct stat sb;
- int count, status;
+ char dname[PATH_MAX];
+ int count, fd, status;
- status = sudo_secure_dir(path, sudoers_uid, sudoers_gid, &sb);
+ fd = sudo_open_conf_path(path, dname, sizeof(dname), NULL);
+ status = sudo_secure_fd(fd, S_IFDIR, sudoers_uid, sudoers_gid, &sb);
+ close(fd); /* XXX use in read_dir_files? */
if (status != SUDO_PATH_SECURE) {
if (verbose > 0) {
switch (status) {
@@ -1277,7 +1345,7 @@ push_include_int(const char *opath, bool isdir, int verbose)
sudo_rcstr_delref(path);
debug_return_bool(true);
}
- count = switch_dir(&istack[idepth], path, verbose);
+ count = switch_dir(&istack[idepth], dname, verbose);
if (count <= 0) {
/* switch_dir() called sudoerserror() for us */
sudo_rcstr_delref(path);
@@ -1286,6 +1354,7 @@ push_include_int(const char *opath, bool isdir, int verbose)
/* Parse the first dir entry we can open, leave the rest for later. */
do {
+ sudo_rcstr_delref(file);
sudo_rcstr_delref(path);
if ((pl = SLIST_FIRST(&istack[idepth].more)) == NULL) {
/* Unable to open any files in include dir, not an error. */
@@ -1294,24 +1363,32 @@ push_include_int(const char *opath, bool isdir, int verbose)
SLIST_REMOVE_HEAD(&istack[idepth].more, entries);
path = pl->path;
free(pl);
- } while ((fp = open_sudoers(path, NULL, false, &keepopen)) == NULL);
+ /* The file and path and the same for sudoers.d files. */
+ file = path;
+ sudo_rcstr_addref(file);
+ } while ((fp = open_sudoers(file, NULL, false, &keepopen)) == NULL);
} else {
- if ((fp = open_sudoers(path, NULL, true, &keepopen)) == NULL) {
+ if ((fp = open_sudoers(path, &file, true, &keepopen)) == NULL) {
/* The error was already printed by open_sudoers() */
sudoerserror(NULL);
sudo_rcstr_delref(path);
debug_return_bool(false);
}
}
- /* Push the old (current) file and open the new one. */
- istack[idepth].path = sudoers; /* push old path (and its ref) */
+ /*
+ * Push the old (current) file and open the new one.
+ * We use the existing refs of sudoers and sudoers_search_path.
+ */
+ istack[idepth].file = sudoers;
+ istack[idepth].path = sudoers_search_path;
istack[idepth].line = sudolinebuf;
istack[idepth].bs = YY_CURRENT_BUFFER;
istack[idepth].lineno = sudolineno;
istack[idepth].keepopen = keepopen;
idepth++;
sudolineno = 1;
- sudoers = path;
+ sudoers = file;
+ sudoers_search_path = path;
sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
memset(&sudolinebuf, 0, sizeof(sudolinebuf));
@@ -1356,7 +1433,10 @@ pop_include(void)
sudolinebuf.len = sudolinebuf.off = 0;
sudolinebuf.toke_start = sudolinebuf.toke_end = 0;
sudo_rcstr_delref(sudoers);
- sudoers = pl->path;
+ sudo_rcstr_delref(sudoers_search_path);
+ sudoers_search_path = pl->path;
+ sudoers = sudoers_search_path;
+ sudo_rcstr_addref(sudoers);
sudolineno = 1;
sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
free(pl);
@@ -1373,7 +1453,9 @@ pop_include(void)
free(sudolinebuf.buf);
sudolinebuf = istack[idepth].line;
sudo_rcstr_delref(sudoers);
- sudoers = istack[idepth].path;
+ sudoers = istack[idepth].file;
+ sudo_rcstr_delref(sudoers_search_path);
+ sudoers_search_path = istack[idepth].path;
sudolineno = istack[idepth].lineno;
keepopen = istack[idepth].keepopen;
}
diff --git a/plugins/sudoers/visudo.c b/plugins/sudoers/visudo.c
index 05af2c8cd..5c17a2365 100644
--- a/plugins/sudoers/visudo.c
+++ b/plugins/sudoers/visudo.c
@@ -287,7 +287,7 @@ main(int argc, char *argv[])
* Parse the existing sudoers file(s) to highlight any existing
* errors and to pull in editor and env_editor conf values.
*/
- init_parser_ext(NULL, true, quiet ? 0 : 2);
+ init_parser_ext(NULL, sudoers_file, true, quiet ? 0 : 2);
if ((sudoersin = open_sudoers(sudoers_file, &sudoers, true, NULL)) == NULL)
exit(EXIT_FAILURE);
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
@@ -650,7 +650,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
/* Clean slate for each parse */
if (!init_defaults())
sudo_fatalx("%s", U_("unable to initialize sudoers default values"));
- init_parser_ext(sp->opath, true, quiet ? 0 : 2);
+ init_parser_ext(sp->opath, sudoers_file, true, quiet ? 0 : 2);
sp->errorline = -1;
/* Parse the sudoers temp file(s) */
@@ -1067,7 +1067,7 @@ check_syntax(const char *path, bool quiet, bool strict, bool check_owner,
goto done;
}
}
- init_parser_ext(fname, true, quiet ? 0 : 2);
+ init_parser_ext(fname, path, true, quiet ? 0 : 2);
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
if (sudoersparse() && !parse_error) {
if (!quiet)
@@ -1228,7 +1228,7 @@ open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen)
/* Check for existing entry using the first file in path. */
len = strcspn(path, ":");
TAILQ_FOREACH(entry, &sudoerslist, entries) {
- if (strncmp(path, entry->opath, len) == 0 && entry->opath[len] == '\0')
+ if (strncmp(path, entry->dpath, len) == 0 && entry->dpath[len] == '\0')
break;
}
if (entry == NULL) {