diff --git a/plugins/sudoers/audit.c b/plugins/sudoers/audit.c index 090e9eea1..f72d5b87d 100644 --- a/plugins/sudoers/audit.c +++ b/plugins/sudoers/audit.c @@ -181,7 +181,7 @@ sudoers_audit_open(unsigned int version, sudo_conv_t conversation, info.settings = settings; info.user_info = user_info; info.plugin_args = plugin_options; - ret = sudoers_init(&info, submit_envp); + ret = sudoers_init(&info, log_parse_error, submit_envp); if (ret == true) { /* Unset close function if we don't need it to avoid extra process. */ diff --git a/plugins/sudoers/check_aliases.c b/plugins/sudoers/check_aliases.c index 2edfdd38e..db362c815 100644 --- a/plugins/sudoers/check_aliases.c +++ b/plugins/sudoers/check_aliases.c @@ -38,6 +38,8 @@ struct alias_warned { }; SLIST_HEAD(alias_warned_list, alias_warned); +static bool alias_warnx(const char *file, int line, int column, bool strict, bool quiet, const char *fmt, ...) __printflike(6, 7); + static bool alias_warned(struct alias_warned_list *warned, char *name) { @@ -67,6 +69,41 @@ alias_warned_add(struct alias_warned_list *warned, char *name) debug_return; } +static bool +alias_warnx(const char *file, int line, int column, bool strict, bool quiet, + const char *fmt, ...) +{ + bool ret = true; + va_list ap; + debug_decl(alias_warnx, SUDOERS_DEBUG_ALIAS); + + if (strict && sudoers_error_hook != NULL) + ret = sudoers_error_hook(file, line, column, fmt, ap); + + if (!quiet) { + int oldlocale; + char *errstr; + + sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale); + va_start(ap, fmt); + if (vasprintf(&errstr, _(fmt), ap) == -1) { + errstr = NULL; + ret = false; + } else if (line > 0) { + sudo_printf(SUDO_CONV_ERROR_MSG, _("%s:%d:%d: %s\n"), file, + line, column, errstr); + } else { + sudo_printf(SUDO_CONV_ERROR_MSG, _("%s: %s\n"), file, errstr); + } + va_end(ap); + sudoers_setlocale(oldlocale, NULL); + + free(errstr); + } + + debug_return_bool(ret); +} + static int check_alias(struct sudoers_parse_tree *parse_tree, struct alias_warned_list *warned, char *name, int type, @@ -87,24 +124,14 @@ check_alias(struct sudoers_parse_tree *parse_tree, } alias_put(a); } else { - if (!quiet && !alias_warned(warned, name)) { + if (!alias_warned(warned, name)) { if (errno == ELOOP) { - sudo_printf(SUDO_CONV_ERROR_MSG, strict ? - U_("Error: %s:%d:%d: cycle in %s \"%s\"") : - U_("Warning: %s:%d:%d: cycle in %s \"%s\""), - file, line, column, alias_type_to_string(type), name); + alias_warnx(file, line, column, strict, quiet, + N_("cycle in %s \"%s\""), alias_type_to_string(type), name); } else { - sudo_printf(SUDO_CONV_ERROR_MSG, strict ? - U_("Error: %s:%d:%d: %s \"%s\" referenced but not defined") : - U_("Warning: %s:%d:%d: %s \"%s\" referenced but not defined"), - file, line, column, alias_type_to_string(type), name); - } - sudo_printf(SUDO_CONV_ERROR_MSG, "\n"); - if (strict && errorfile == NULL) { - errorfile = sudo_rcstr_addref(file); - errorlineno = line; - errorcolumn = column; - /* No need to set errorstring, visudo doesn't use it. */ + alias_warnx(file, line, column, strict, quiet, + N_("%s \"%s\" referenced but not defined"), + alias_type_to_string(type), name); } alias_warned_add(warned, name); } @@ -117,6 +144,7 @@ check_alias(struct sudoers_parse_tree *parse_tree, /* * Iterate through the sudoers datastructures looking for undefined * aliases or unused aliases. + * In strict mode, returns the number of errors, else 0. */ int check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet, diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c index a70a4acd4..847c5c750 100644 --- a/plugins/sudoers/cvtsudoers.c +++ b/plugins/sudoers/cvtsudoers.c @@ -753,9 +753,6 @@ parse_sudoers(const char *input_file, struct cvtsudoers_config *conf) if (sudoersparse() && !parse_error) { sudo_warnx(U_("failed to parse %s file, unknown error"), input_file); parse_error = true; - sudo_rcstr_delref(errorfile); - if ((errorfile = sudo_rcstr_dup(input_file)) == NULL) - sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } debug_return_bool(!parse_error); } diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c index 3159a19fa..ad28549b9 100644 --- a/plugins/sudoers/defaults.c +++ b/plugins/sudoers/defaults.c @@ -73,6 +73,7 @@ static bool store_timespec(const char *str, struct sudo_defs_types *def); static bool store_rlimit(const char *str, struct sudo_defs_types *def); static bool list_op(const char *str, size_t, struct list_members *list, enum list_ops op); static bool valid_path(struct sudo_defs_types *def, const char *val, const char *file, int line, int column, bool quiet); +static bool defaults_warnx(const char *file, int line, int column, bool quiet, const char *fmt, ...) __printflike(5, 6); /* * Table describing compile-time and run-time options. @@ -185,17 +186,9 @@ find_default(const char *name, const char *file, int line, int column, bool quie if (strcmp(name, sudo_defs_table[i].name) == 0) debug_return_int(i); } - if (!quiet && !def_ignore_unknown_defaults) { - if (line > 0) { - sudo_warnx(U_("%s:%d:%d: unknown defaults entry \"%s\""), - file, line, column + 1, name); - } else { - sudo_warnx(U_("%s: unknown defaults entry \"%s\""), - file, name); - } - } else { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "%s: unknown defaults entry \"%s\"", __func__, name); + if (!def_ignore_unknown_defaults) { + defaults_warnx(file, line, column, quiet, + N_("unknown defaults entry \"%s\""), name); } debug_return_int(-1); } @@ -237,15 +230,8 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op, break; default: if (!ISSET(def->type, T_BOOL) || op != false) { - if (!quiet) { - if (line > 0) { - sudo_warnx(U_("%s:%d:%d: no value specified for \"%s\""), - file, line, column, def->name); - } else { - sudo_warnx(U_("%s: no value specified for \"%s\""), - file, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("no value specified for \"%s\""), def->name); debug_return_bool(false); } } @@ -253,15 +239,8 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op, /* Only lists support append/remove. */ if ((op == '+' || op == '-') && (def->type & T_MASK) != T_LIST) { - if (!quiet) { - if (line > 0) { - sudo_warnx(U_("%s:%d:%d: invalid operator \"%c=\" for \"%s\""), - file, line, column, op, def->name); - } else { - sudo_warnx(U_("%s: invalid operator \"%c=\" for \"%s\""), - file, op, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("invalid operator \"%c=\" for \"%s\""), op, def->name); debug_return_bool(false); } @@ -292,15 +271,8 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op, break; case T_FLAG: if (val != NULL) { - if (!quiet) { - if (line > 0) { - sudo_warnx(U_("%s:%d:%d: option \"%s\" does not take a value"), - file, line, column, def->name); - } else { - sudo_warnx(U_("%s: option \"%s\" does not take a value"), - file, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("option \"%s\" does not take a value"), def->name); rc = -1; break; } @@ -323,28 +295,15 @@ parse_default_entry(struct sudo_defs_types *def, const char *val, int op, rc = store_rlimit(val, def); break; default: - if (!quiet) { - if (line > 0) { - sudo_warnx(U_("%s:%d:%d: invalid Defaults type 0x%x for option \"%s\""), - file, line, column, def->type, def->name); - } else { - sudo_warnx(U_("%s: invalid Defaults type 0x%x for option \"%s\""), - file, def->type, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("invalid Defaults type 0x%x for option \"%s\""), + def->type, def->name); rc = -1; break; } if (rc == false) { - if (!quiet) { - if (line > 0) { - sudo_warnx(U_("%s:%d:%d: value \"%s\" is invalid for option \"%s\""), - file, line, column, val, def->name); - } else { - sudo_warnx(U_("%s: value \"%s\" is invalid for option \"%s\""), - file, val, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("value \"%s\" is invalid for option \"%s\""), val, def->name); } debug_return_bool(rc == true); @@ -1156,45 +1115,21 @@ valid_path(struct sudo_defs_types *def, const char *val, debug_decl(valid_path, SUDOERS_DEBUG_DEFAULTS); if (strlen(val) >= PATH_MAX) { - if (!quiet) { - if (line > 0) { - sudo_warnx(U_("%s:%d:%d: path name for \"%s\" too long"), - file, line, column, def->name); - } else { - sudo_warnx(U_("%s: path name for \"%s\" too long"), - file, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("path name for \"%s\" too long"), def->name); ret = false; } if (ISSET(def->type, T_CHPATH)) { if (val[0] != '/' && val[0] != '~' && (val[0] != '*' || val[1] != '\0')) { - if (!quiet) { - if (line > 0) { - sudo_warnx( - U_("%s:%d:%d: values for \"%s\" must start with a '/', '~', or '*'"), - file, line, column, def->name); - } else { - sudo_warnx( - U_("%s: values for \"%s\" must start with a '/', '~', or '*'"), - file, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("values for \"%s\" must start with a '/', '~', or '*'"), + def->name); ret = false; } } else { if (val[0] != '/') { - if (!quiet) { - if (line > 0) { - sudo_warnx( - U_("%s:%d:%d: values for \"%s\" must start with a '/'"), - file, line, column, def->name); - } else { - sudo_warnx( - U_("%s: values for \"%s\" must start with a '/'"), - file, def->name); - } - } + defaults_warnx(file, line, column, quiet, + N_("values for \"%s\" must start with a '/'"), def->name); ret = false; } @@ -1293,7 +1228,9 @@ cb_passprompt_regex(const union sudo_defs_val *sd_un, int op) if (op == '+' || op == true) { SLIST_FOREACH(lm, &sd_un->list, entries) { if (!sudo_regex_compile(NULL, lm->value, &errstr)) { - sudo_warnx(U_("invalid regular expression \"%s\": %s"), + /* XXX - need to pass file/file/col/quiet to callbacks */ + defaults_warnx(sudoers, -1, -1, false, + U_("invalid regular expression \"%s\": %s"), lm->value, U_(errstr)); debug_return_bool(false); } @@ -1302,3 +1239,41 @@ cb_passprompt_regex(const union sudo_defs_val *sd_un, int op) debug_return_bool(true); } + +static bool +defaults_warnx(const char *file, int line, int column, bool quiet, + const char *fmt, ...) +{ + bool ret = true; + va_list ap; + debug_decl(defaults_warnx, SUDOERS_DEBUG_DEFAULTS); + + if (sudoers_error_hook != NULL) { + va_start(ap, fmt); + ret = sudoers_error_hook(file, line, column, fmt, ap); + va_end(ap); + } + + if (!quiet) { + int oldlocale; + char *errstr; + + sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale); + va_start(ap, fmt); + if (vasprintf(&errstr, _(fmt), ap) == -1) { + errstr = NULL; + ret = false; + } else if (line > 0) { + sudo_printf(SUDO_CONV_ERROR_MSG, _("%s:%d:%d: %s\n"), file, + line, column, errstr); + } else { + sudo_printf(SUDO_CONV_ERROR_MSG, _("%s: %s\n"), file, errstr); + } + va_end(ap); + sudoers_setlocale(oldlocale, NULL); + + free(errstr); + } + + debug_return_bool(ret); +} diff --git a/plugins/sudoers/file.c b/plugins/sudoers/file.c index e20f545d2..3ccf14136 100644 --- a/plugins/sudoers/file.c +++ b/plugins/sudoers/file.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2004-2005, 2007-2018 Todd C. Miller + * Copyright (c) 2004-2005, 2007-2022 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -102,19 +102,9 @@ sudo_file_parse(struct sudo_nss *nss) sudoersin = handle->fp; error = sudoersparse(); - if (error || parse_error) { - if (errorlineno != -1) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, N_("%s:%d:%d: %s"), - errorfile, errorlineno, errorcolumn, - errorstring ? errorstring : N_("syntax error")); - } else { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, N_("%s: %s"), - errorfile, errorstring ? errorstring : N_("syntax error")); - } - if (error || !sudoers_recovery) { - /* unrecoverable error */ - debug_return_ptr(NULL); - } + if (error || (parse_error && !sudoers_recovery)) { + /* unrecoverable error */ + debug_return_ptr(NULL); } /* Move parsed sudoers policy to nss handle. */ diff --git a/plugins/sudoers/gram.c b/plugins/sudoers/gram.c index b41c47676..454e8d001 100644 --- a/plugins/sudoers/gram.c +++ b/plugins/sudoers/gram.c @@ -85,7 +85,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2021 + * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2022 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -134,10 +134,9 @@ bool sudoers_warnings = true; bool sudoers_strict = false; bool parse_error = false; -int errorlineno = -1; -int errorcolumn = -1; -char *errorfile = NULL; -char *errorstring = NULL; + +/* Optional logging function for parse errors. */ +sudoers_logger_t sudoers_error_hook; static int alias_line, alias_column; @@ -167,7 +166,7 @@ static struct sudo_command *new_command(char *, char *); static struct command_digest *new_digest(int, char *); static void alias_error(const char *name, int errnum); -#line 165 "gram.c" +#line 164 "gram.c" # ifndef YY_CAST # ifdef __cplusplus @@ -328,7 +327,7 @@ extern int sudoersdebug; #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 88 "gram.y" +#line 87 "gram.y" struct cmndspec *cmndspec; struct defaults *defaults; @@ -342,7 +341,7 @@ union YYSTYPE char *string; int tok; -#line 340 "gram.c" +#line 339 "gram.c" }; typedef union YYSTYPE YYSTYPE; @@ -870,22 +869,22 @@ static const yytype_int8 yytranslate[] = /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_int16 yyrline[] = { - 0, 199, 199, 202, 205, 206, 209, 212, 215, 224, - 233, 239, 242, 245, 248, 251, 255, 259, 263, 267, - 273, 276, 282, 285, 291, 292, 299, 308, 317, 327, - 337, 349, 350, 355, 361, 378, 382, 388, 397, 405, - 414, 423, 434, 435, 497, 563, 572, 581, 590, 601, - 602, 609, 612, 626, 630, 636, 652, 668, 673, 677, - 682, 687, 692, 696, 701, 704, 709, 725, 736, 748, - 759, 777, 778, 779, 780, 781, 782, 783, 784, 785, - 786, 789, 795, 798, 803, 808, 817, 826, 838, 845, - 852, 859, 868, 871, 874, 877, 880, 883, 886, 889, - 892, 895, 898, 901, 904, 907, 910, 913, 916, 921, - 935, 944, 967, 968, 971, 971, 983, 986, 987, 994, - 995, 998, 998, 1010, 1013, 1014, 1021, 1022, 1025, 1025, - 1037, 1040, 1041, 1044, 1044, 1056, 1059, 1060, 1067, 1071, - 1077, 1086, 1094, 1103, 1112, 1123, 1124, 1131, 1135, 1141, - 1150, 1158 + 0, 198, 198, 201, 204, 205, 208, 211, 214, 223, + 232, 238, 241, 244, 247, 250, 254, 258, 262, 266, + 272, 275, 281, 284, 290, 291, 298, 307, 316, 326, + 336, 348, 349, 354, 360, 377, 381, 387, 396, 404, + 413, 422, 433, 434, 496, 562, 571, 580, 589, 600, + 601, 608, 611, 625, 629, 635, 651, 667, 672, 676, + 681, 686, 691, 695, 700, 703, 708, 724, 735, 747, + 758, 776, 777, 778, 779, 780, 781, 782, 783, 784, + 785, 788, 794, 797, 802, 807, 816, 825, 837, 844, + 851, 858, 867, 870, 873, 876, 879, 882, 885, 888, + 891, 894, 897, 900, 903, 906, 909, 912, 915, 920, + 934, 943, 966, 967, 970, 970, 982, 985, 986, 993, + 994, 997, 997, 1009, 1012, 1013, 1020, 1021, 1024, 1024, + 1036, 1039, 1040, 1043, 1043, 1055, 1058, 1059, 1066, 1070, + 1076, 1085, 1093, 1102, 1111, 1122, 1123, 1130, 1134, 1140, + 1149, 1157 }; #endif @@ -1637,31 +1636,31 @@ yyreduce: switch (yyn) { case 2: /* file: %empty */ -#line 199 "gram.y" +#line 198 "gram.y" { ; /* empty file */ } -#line 1639 "gram.c" +#line 1638 "gram.c" break; case 6: /* entry: '\n' */ -#line 209 "gram.y" +#line 208 "gram.y" { ; /* blank line */ } -#line 1647 "gram.c" +#line 1646 "gram.c" break; case 7: /* entry: error '\n' */ -#line 212 "gram.y" +#line 211 "gram.y" { yyerrok; } -#line 1655 "gram.c" +#line 1654 "gram.c" break; case 8: /* entry: include */ -#line 215 "gram.y" +#line 214 "gram.y" { if (!push_include((yyvsp[0].string), false)) { parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); @@ -1671,11 +1670,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); free((yyvsp[0].string)); } -#line 1669 "gram.c" +#line 1668 "gram.c" break; case 9: /* entry: includedir */ -#line 224 "gram.y" +#line 223 "gram.y" { if (!push_include((yyvsp[0].string), true)) { parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); @@ -1685,143 +1684,143 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); free((yyvsp[0].string)); } -#line 1683 "gram.c" +#line 1682 "gram.c" break; case 10: /* entry: userlist privileges '\n' */ -#line 233 "gram.y" +#line 232 "gram.y" { if (!add_userspec((yyvsp[-2].member), (yyvsp[-1].privilege))) { sudoerserror(N_("unable to allocate memory")); YYERROR; } } -#line 1694 "gram.c" +#line 1693 "gram.c" break; case 11: /* entry: USERALIAS useraliases '\n' */ -#line 239 "gram.y" +#line 238 "gram.y" { ; } -#line 1702 "gram.c" +#line 1701 "gram.c" break; case 12: /* entry: HOSTALIAS hostaliases '\n' */ -#line 242 "gram.y" +#line 241 "gram.y" { ; } -#line 1710 "gram.c" +#line 1709 "gram.c" break; case 13: /* entry: CMNDALIAS cmndaliases '\n' */ -#line 245 "gram.y" +#line 244 "gram.y" { ; } -#line 1718 "gram.c" +#line 1717 "gram.c" break; case 14: /* entry: RUNASALIAS runasaliases '\n' */ -#line 248 "gram.y" +#line 247 "gram.y" { ; } -#line 1726 "gram.c" +#line 1725 "gram.c" break; case 15: /* entry: DEFAULTS defaults_list '\n' */ -#line 251 "gram.y" +#line 250 "gram.y" { if (!add_defaults(DEFAULTS, NULL, (yyvsp[-1].defaults))) YYERROR; } -#line 1735 "gram.c" +#line 1734 "gram.c" break; case 16: /* entry: DEFAULTS_USER userlist defaults_list '\n' */ -#line 255 "gram.y" +#line 254 "gram.y" { if (!add_defaults(DEFAULTS_USER, (yyvsp[-2].member), (yyvsp[-1].defaults))) YYERROR; } -#line 1744 "gram.c" +#line 1743 "gram.c" break; case 17: /* entry: DEFAULTS_RUNAS userlist defaults_list '\n' */ -#line 259 "gram.y" +#line 258 "gram.y" { if (!add_defaults(DEFAULTS_RUNAS, (yyvsp[-2].member), (yyvsp[-1].defaults))) YYERROR; } -#line 1753 "gram.c" +#line 1752 "gram.c" break; case 18: /* entry: DEFAULTS_HOST hostlist defaults_list '\n' */ -#line 263 "gram.y" +#line 262 "gram.y" { if (!add_defaults(DEFAULTS_HOST, (yyvsp[-2].member), (yyvsp[-1].defaults))) YYERROR; } -#line 1762 "gram.c" +#line 1761 "gram.c" break; case 19: /* entry: DEFAULTS_CMND cmndlist defaults_list '\n' */ -#line 267 "gram.y" +#line 266 "gram.y" { if (!add_defaults(DEFAULTS_CMND, (yyvsp[-2].member), (yyvsp[-1].defaults))) YYERROR; } -#line 1771 "gram.c" +#line 1770 "gram.c" break; case 20: /* include: INCLUDE WORD '\n' */ -#line 273 "gram.y" +#line 272 "gram.y" { (yyval.string) = (yyvsp[-1].string); } -#line 1779 "gram.c" +#line 1778 "gram.c" break; case 21: /* include: INCLUDE WORD error '\n' */ -#line 276 "gram.y" +#line 275 "gram.y" { yyerrok; (yyval.string) = (yyvsp[-2].string); } -#line 1788 "gram.c" +#line 1787 "gram.c" break; case 22: /* includedir: INCLUDEDIR WORD '\n' */ -#line 282 "gram.y" +#line 281 "gram.y" { (yyval.string) = (yyvsp[-1].string); } -#line 1796 "gram.c" +#line 1795 "gram.c" break; case 23: /* includedir: INCLUDEDIR WORD error '\n' */ -#line 285 "gram.y" +#line 284 "gram.y" { yyerrok; (yyval.string) = (yyvsp[-2].string); } -#line 1805 "gram.c" +#line 1804 "gram.c" break; case 25: /* defaults_list: defaults_list ',' defaults_entry */ -#line 292 "gram.y" +#line 291 "gram.y" { parser_leak_remove(LEAK_DEFAULTS, (yyvsp[0].defaults)); HLTQ_CONCAT((yyvsp[-2].defaults), (yyvsp[0].defaults), entries); (yyval.defaults) = (yyvsp[-2].defaults); } -#line 1815 "gram.c" +#line 1814 "gram.c" break; case 26: /* defaults_entry: DEFVAR */ -#line 299 "gram.y" +#line 298 "gram.y" { (yyval.defaults) = new_default((yyvsp[0].string), NULL, true); if ((yyval.defaults) == NULL) { @@ -1831,11 +1830,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DEFAULTS, (yyval.defaults)); } -#line 1829 "gram.c" +#line 1828 "gram.c" break; case 27: /* defaults_entry: '!' DEFVAR */ -#line 308 "gram.y" +#line 307 "gram.y" { (yyval.defaults) = new_default((yyvsp[0].string), NULL, false); if ((yyval.defaults) == NULL) { @@ -1845,11 +1844,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DEFAULTS, (yyval.defaults)); } -#line 1843 "gram.c" +#line 1842 "gram.c" break; case 28: /* defaults_entry: DEFVAR '=' WORD */ -#line 317 "gram.y" +#line 316 "gram.y" { (yyval.defaults) = new_default((yyvsp[-2].string), (yyvsp[0].string), true); if ((yyval.defaults) == NULL) { @@ -1860,11 +1859,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DEFAULTS, (yyval.defaults)); } -#line 1858 "gram.c" +#line 1857 "gram.c" break; case 29: /* defaults_entry: DEFVAR '+' WORD */ -#line 327 "gram.y" +#line 326 "gram.y" { (yyval.defaults) = new_default((yyvsp[-2].string), (yyvsp[0].string), '+'); if ((yyval.defaults) == NULL) { @@ -1875,11 +1874,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DEFAULTS, (yyval.defaults)); } -#line 1873 "gram.c" +#line 1872 "gram.c" break; case 30: /* defaults_entry: DEFVAR '-' WORD */ -#line 337 "gram.y" +#line 336 "gram.y" { (yyval.defaults) = new_default((yyvsp[-2].string), (yyvsp[0].string), '-'); if ((yyval.defaults) == NULL) { @@ -1890,30 +1889,30 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DEFAULTS, (yyval.defaults)); } -#line 1888 "gram.c" +#line 1887 "gram.c" break; case 32: /* privileges: privileges ':' privilege */ -#line 350 "gram.y" +#line 349 "gram.y" { parser_leak_remove(LEAK_PRIVILEGE, (yyvsp[0].privilege)); HLTQ_CONCAT((yyvsp[-2].privilege), (yyvsp[0].privilege), entries); (yyval.privilege) = (yyvsp[-2].privilege); } -#line 1898 "gram.c" +#line 1897 "gram.c" break; case 33: /* privileges: privileges ':' error */ -#line 355 "gram.y" +#line 354 "gram.y" { yyerrok; (yyval.privilege) = (yyvsp[-2].privilege); } -#line 1907 "gram.c" +#line 1906 "gram.c" break; case 34: /* privilege: hostlist '=' cmndspeclist */ -#line 361 "gram.y" +#line 360 "gram.y" { struct privilege *p = calloc(1, sizeof(*p)); if (p == NULL) { @@ -1929,29 +1928,29 @@ yyreduce: HLTQ_INIT(p, entries); (yyval.privilege) = p; } -#line 1927 "gram.c" +#line 1926 "gram.c" break; case 35: /* ophost: host */ -#line 378 "gram.y" +#line 377 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = false; } -#line 1936 "gram.c" +#line 1935 "gram.c" break; case 36: /* ophost: '!' host */ -#line 382 "gram.y" +#line 381 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = true; } -#line 1945 "gram.c" +#line 1944 "gram.c" break; case 37: /* host: ALIAS */ -#line 388 "gram.y" +#line 387 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), ALIAS); if ((yyval.member) == NULL) { @@ -1961,11 +1960,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 1959 "gram.c" +#line 1958 "gram.c" break; case 38: /* host: ALL */ -#line 397 "gram.y" +#line 396 "gram.y" { (yyval.member) = new_member(NULL, ALL); if ((yyval.member) == NULL) { @@ -1974,11 +1973,11 @@ yyreduce: } parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 1972 "gram.c" +#line 1971 "gram.c" break; case 39: /* host: NETGROUP */ -#line 405 "gram.y" +#line 404 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), NETGROUP); if ((yyval.member) == NULL) { @@ -1988,11 +1987,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 1986 "gram.c" +#line 1985 "gram.c" break; case 40: /* host: NTWKADDR */ -#line 414 "gram.y" +#line 413 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), NTWKADDR); if ((yyval.member) == NULL) { @@ -2002,11 +2001,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 2000 "gram.c" +#line 1999 "gram.c" break; case 41: /* host: WORD */ -#line 423 "gram.y" +#line 422 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), WORD); if ((yyval.member) == NULL) { @@ -2016,11 +2015,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 2014 "gram.c" +#line 2013 "gram.c" break; case 43: /* cmndspeclist: cmndspeclist ',' cmndspec */ -#line 435 "gram.y" +#line 434 "gram.y" { struct cmndspec *prev; prev = HLTQ_LAST((yyvsp[-2].cmndspec), cmndspec, entries); @@ -2081,11 +2080,11 @@ yyreduce: } (yyval.cmndspec) = (yyvsp[-2].cmndspec); } -#line 2079 "gram.c" +#line 2078 "gram.c" break; case 44: /* cmndspec: runasspec options cmndtag digcmnd */ -#line 497 "gram.y" +#line 496 "gram.y" { struct cmndspec *cs = calloc(1, sizeof(*cs)); if (cs == NULL) { @@ -2150,11 +2149,11 @@ yyreduce: cs->tags.setenv = IMPLIED; (yyval.cmndspec) = cs; } -#line 2148 "gram.c" +#line 2147 "gram.c" break; case 45: /* digestspec: SHA224_TOK ':' DIGEST */ -#line 563 "gram.y" +#line 562 "gram.y" { (yyval.digest) = new_digest(SUDO_DIGEST_SHA224, (yyvsp[0].string)); if ((yyval.digest) == NULL) { @@ -2164,11 +2163,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DIGEST, (yyval.digest)); } -#line 2162 "gram.c" +#line 2161 "gram.c" break; case 46: /* digestspec: SHA256_TOK ':' DIGEST */ -#line 572 "gram.y" +#line 571 "gram.y" { (yyval.digest) = new_digest(SUDO_DIGEST_SHA256, (yyvsp[0].string)); if ((yyval.digest) == NULL) { @@ -2178,11 +2177,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DIGEST, (yyval.digest)); } -#line 2176 "gram.c" +#line 2175 "gram.c" break; case 47: /* digestspec: SHA384_TOK ':' DIGEST */ -#line 581 "gram.y" +#line 580 "gram.y" { (yyval.digest) = new_digest(SUDO_DIGEST_SHA384, (yyvsp[0].string)); if ((yyval.digest) == NULL) { @@ -2192,11 +2191,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DIGEST, (yyval.digest)); } -#line 2190 "gram.c" +#line 2189 "gram.c" break; case 48: /* digestspec: SHA512_TOK ':' DIGEST */ -#line 590 "gram.y" +#line 589 "gram.y" { (yyval.digest) = new_digest(SUDO_DIGEST_SHA512, (yyvsp[0].string)); if ((yyval.digest) == NULL) { @@ -2206,29 +2205,29 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_DIGEST, (yyval.digest)); } -#line 2204 "gram.c" +#line 2203 "gram.c" break; case 50: /* digestlist: digestlist ',' digestspec */ -#line 602 "gram.y" +#line 601 "gram.y" { parser_leak_remove(LEAK_DIGEST, (yyvsp[0].digest)); HLTQ_CONCAT((yyvsp[-2].digest), (yyvsp[0].digest), entries); (yyval.digest) = (yyvsp[-2].digest); } -#line 2214 "gram.c" +#line 2213 "gram.c" break; case 51: /* digcmnd: opcmnd */ -#line 609 "gram.y" +#line 608 "gram.y" { (yyval.member) = (yyvsp[0].member); } -#line 2222 "gram.c" +#line 2221 "gram.c" break; case 52: /* digcmnd: digestlist opcmnd */ -#line 612 "gram.y" +#line 611 "gram.y" { struct sudo_command *c = (struct sudo_command *) (yyvsp[0].member)->name; @@ -2241,29 +2240,29 @@ yyreduce: HLTQ_TO_TAILQ(&c->digests, (yyvsp[-1].digest), entries); (yyval.member) = (yyvsp[0].member); } -#line 2239 "gram.c" +#line 2238 "gram.c" break; case 53: /* opcmnd: cmnd */ -#line 626 "gram.y" +#line 625 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = false; } -#line 2248 "gram.c" +#line 2247 "gram.c" break; case 54: /* opcmnd: '!' cmnd */ -#line 630 "gram.y" +#line 629 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = true; } -#line 2257 "gram.c" +#line 2256 "gram.c" break; case 55: /* chdirspec: CWD '=' WORD */ -#line 636 "gram.y" +#line 635 "gram.y" { if ((yyvsp[0].string)[0] != '/' && (yyvsp[0].string)[0] != '~') { if (strcmp((yyvsp[0].string), "*") != 0) { @@ -2278,11 +2277,11 @@ yyreduce: } (yyval.string) = (yyvsp[0].string); } -#line 2276 "gram.c" +#line 2275 "gram.c" break; case 56: /* chrootspec: CHROOT '=' WORD */ -#line 652 "gram.y" +#line 651 "gram.y" { if ((yyvsp[0].string)[0] != '/' && (yyvsp[0].string)[0] != '~') { if (strcmp((yyvsp[0].string), "*") != 0) { @@ -2297,83 +2296,83 @@ yyreduce: } (yyval.string) = (yyvsp[0].string); } -#line 2295 "gram.c" +#line 2294 "gram.c" break; case 57: /* timeoutspec: CMND_TIMEOUT '=' WORD */ -#line 668 "gram.y" +#line 667 "gram.y" { (yyval.string) = (yyvsp[0].string); } -#line 2303 "gram.c" +#line 2302 "gram.c" break; case 58: /* notbeforespec: NOTBEFORE '=' WORD */ -#line 673 "gram.y" +#line 672 "gram.y" { (yyval.string) = (yyvsp[0].string); } -#line 2311 "gram.c" +#line 2310 "gram.c" break; case 59: /* notafterspec: NOTAFTER '=' WORD */ -#line 677 "gram.y" +#line 676 "gram.y" { (yyval.string) = (yyvsp[0].string); } -#line 2319 "gram.c" +#line 2318 "gram.c" break; case 60: /* rolespec: ROLE '=' WORD */ -#line 682 "gram.y" +#line 681 "gram.y" { (yyval.string) = (yyvsp[0].string); } -#line 2327 "gram.c" +#line 2326 "gram.c" break; case 61: /* typespec: TYPE '=' WORD */ -#line 687 "gram.y" +#line 686 "gram.y" { (yyval.string) = (yyvsp[0].string); } -#line 2335 "gram.c" +#line 2334 "gram.c" break; case 62: /* privsspec: PRIVS '=' WORD */ -#line 692 "gram.y" +#line 691 "gram.y" { (yyval.string) = (yyvsp[0].string); } -#line 2343 "gram.c" +#line 2342 "gram.c" break; case 63: /* limitprivsspec: LIMITPRIVS '=' WORD */ -#line 696 "gram.y" +#line 695 "gram.y" { (yyval.string) = (yyvsp[0].string); } -#line 2351 "gram.c" +#line 2350 "gram.c" break; case 64: /* runasspec: %empty */ -#line 701 "gram.y" +#line 700 "gram.y" { (yyval.runas) = NULL; } -#line 2359 "gram.c" +#line 2358 "gram.c" break; case 65: /* runasspec: '(' runaslist ')' */ -#line 704 "gram.y" +#line 703 "gram.y" { (yyval.runas) = (yyvsp[-1].runas); } -#line 2367 "gram.c" +#line 2366 "gram.c" break; case 66: /* runaslist: %empty */ -#line 709 "gram.y" +#line 708 "gram.y" { (yyval.runas) = calloc(1, sizeof(struct runascontainer)); if ((yyval.runas) != NULL) { @@ -2390,11 +2389,11 @@ yyreduce: } parser_leak_add(LEAK_RUNAS, (yyval.runas)); } -#line 2388 "gram.c" +#line 2387 "gram.c" break; case 67: /* runaslist: userlist */ -#line 725 "gram.y" +#line 724 "gram.y" { (yyval.runas) = calloc(1, sizeof(struct runascontainer)); if ((yyval.runas) == NULL) { @@ -2406,11 +2405,11 @@ yyreduce: (yyval.runas)->runasusers = (yyvsp[0].member); /* $$->runasgroups = NULL; */ } -#line 2404 "gram.c" +#line 2403 "gram.c" break; case 68: /* runaslist: userlist ':' grouplist */ -#line 736 "gram.y" +#line 735 "gram.y" { (yyval.runas) = calloc(1, sizeof(struct runascontainer)); if ((yyval.runas) == NULL) { @@ -2423,11 +2422,11 @@ yyreduce: (yyval.runas)->runasusers = (yyvsp[-2].member); (yyval.runas)->runasgroups = (yyvsp[0].member); } -#line 2421 "gram.c" +#line 2420 "gram.c" break; case 69: /* runaslist: ':' grouplist */ -#line 748 "gram.y" +#line 747 "gram.y" { (yyval.runas) = calloc(1, sizeof(struct runascontainer)); if ((yyval.runas) == NULL) { @@ -2439,11 +2438,11 @@ yyreduce: /* $$->runasusers = NULL; */ (yyval.runas)->runasgroups = (yyvsp[0].member); } -#line 2437 "gram.c" +#line 2436 "gram.c" break; case 70: /* runaslist: ':' */ -#line 759 "gram.y" +#line 758 "gram.y" { (yyval.runas) = calloc(1, sizeof(struct runascontainer)); if ((yyval.runas) != NULL) { @@ -2460,108 +2459,108 @@ yyreduce: } parser_leak_add(LEAK_RUNAS, (yyval.runas)); } -#line 2458 "gram.c" +#line 2457 "gram.c" break; case 71: /* reserved_word: ALL */ -#line 777 "gram.y" +#line 776 "gram.y" { (yyval.string) = "ALL"; } -#line 2464 "gram.c" +#line 2463 "gram.c" break; case 72: /* reserved_word: CHROOT */ -#line 778 "gram.y" +#line 777 "gram.y" { (yyval.string) = "CHROOT"; } -#line 2470 "gram.c" +#line 2469 "gram.c" break; case 73: /* reserved_word: CWD */ -#line 779 "gram.y" +#line 778 "gram.y" { (yyval.string) = "CWD"; } -#line 2476 "gram.c" +#line 2475 "gram.c" break; case 74: /* reserved_word: CMND_TIMEOUT */ -#line 780 "gram.y" +#line 779 "gram.y" { (yyval.string) = "CMND_TIMEOUT"; } -#line 2482 "gram.c" +#line 2481 "gram.c" break; case 75: /* reserved_word: NOTBEFORE */ -#line 781 "gram.y" +#line 780 "gram.y" { (yyval.string) = "NOTBEFORE"; } -#line 2488 "gram.c" +#line 2487 "gram.c" break; case 76: /* reserved_word: NOTAFTER */ -#line 782 "gram.y" +#line 781 "gram.y" { (yyval.string) = "NOTAFTER"; } -#line 2494 "gram.c" +#line 2493 "gram.c" break; case 77: /* reserved_word: ROLE */ -#line 783 "gram.y" +#line 782 "gram.y" { (yyval.string) = "ROLE"; } -#line 2500 "gram.c" +#line 2499 "gram.c" break; case 78: /* reserved_word: TYPE */ -#line 784 "gram.y" +#line 783 "gram.y" { (yyval.string) = "TYPE"; } -#line 2506 "gram.c" +#line 2505 "gram.c" break; case 79: /* reserved_word: PRIVS */ -#line 785 "gram.y" +#line 784 "gram.y" { (yyval.string) = "PRIVS"; } -#line 2512 "gram.c" +#line 2511 "gram.c" break; case 80: /* reserved_word: LIMITPRIVS */ -#line 786 "gram.y" +#line 785 "gram.y" { (yyval.string) = "LIMITPRIVS"; } -#line 2518 "gram.c" +#line 2517 "gram.c" break; case 81: /* reserved_alias: reserved_word */ -#line 789 "gram.y" +#line 788 "gram.y" { sudoerserrorf(U_("syntax error, reserved word %s used as an alias name"), (yyvsp[0].string)); YYERROR; } -#line 2527 "gram.c" +#line 2526 "gram.c" break; case 82: /* options: %empty */ -#line 795 "gram.y" +#line 794 "gram.y" { init_options(&(yyval.options)); } -#line 2535 "gram.c" +#line 2534 "gram.c" break; case 83: /* options: options chdirspec */ -#line 798 "gram.y" +#line 797 "gram.y" { parser_leak_remove(LEAK_PTR, (yyval.options).runcwd); free((yyval.options).runcwd); (yyval.options).runcwd = (yyvsp[0].string); } -#line 2545 "gram.c" +#line 2544 "gram.c" break; case 84: /* options: options chrootspec */ -#line 803 "gram.y" +#line 802 "gram.y" { parser_leak_remove(LEAK_PTR, (yyval.options).runchroot); free((yyval.options).runchroot); (yyval.options).runchroot = (yyvsp[0].string); } -#line 2555 "gram.c" +#line 2554 "gram.c" break; case 85: /* options: options notbeforespec */ -#line 808 "gram.y" +#line 807 "gram.y" { (yyval.options).notbefore = parse_gentime((yyvsp[0].string)); parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); @@ -2571,11 +2570,11 @@ yyreduce: YYERROR; } } -#line 2569 "gram.c" +#line 2568 "gram.c" break; case 86: /* options: options notafterspec */ -#line 817 "gram.y" +#line 816 "gram.y" { (yyval.options).notafter = parse_gentime((yyvsp[0].string)); parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); @@ -2585,11 +2584,11 @@ yyreduce: YYERROR; } } -#line 2583 "gram.c" +#line 2582 "gram.c" break; case 87: /* options: options timeoutspec */ -#line 826 "gram.y" +#line 825 "gram.y" { (yyval.options).timeout = parse_timeout((yyvsp[0].string)); parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); @@ -2602,11 +2601,11 @@ yyreduce: YYERROR; } } -#line 2600 "gram.c" +#line 2599 "gram.c" break; case 88: /* options: options rolespec */ -#line 838 "gram.y" +#line 837 "gram.y" { #ifdef HAVE_SELINUX parser_leak_remove(LEAK_PTR, (yyval.options).role); @@ -2614,11 +2613,11 @@ yyreduce: (yyval.options).role = (yyvsp[0].string); #endif } -#line 2612 "gram.c" +#line 2611 "gram.c" break; case 89: /* options: options typespec */ -#line 845 "gram.y" +#line 844 "gram.y" { #ifdef HAVE_SELINUX parser_leak_remove(LEAK_PTR, (yyval.options).type); @@ -2626,11 +2625,11 @@ yyreduce: (yyval.options).type = (yyvsp[0].string); #endif } -#line 2624 "gram.c" +#line 2623 "gram.c" break; case 90: /* options: options privsspec */ -#line 852 "gram.y" +#line 851 "gram.y" { #ifdef HAVE_PRIV_SET parser_leak_remove(LEAK_PTR, (yyval.options).privs); @@ -2638,11 +2637,11 @@ yyreduce: (yyval.options).privs = (yyvsp[0].string); #endif } -#line 2636 "gram.c" +#line 2635 "gram.c" break; case 91: /* options: options limitprivsspec */ -#line 859 "gram.y" +#line 858 "gram.y" { #ifdef HAVE_PRIV_SET parser_leak_remove(LEAK_PTR, (yyval.options).limitprivs); @@ -2650,147 +2649,147 @@ yyreduce: (yyval.options).limitprivs = (yyvsp[0].string); #endif } -#line 2648 "gram.c" +#line 2647 "gram.c" break; case 92: /* cmndtag: %empty */ -#line 868 "gram.y" +#line 867 "gram.y" { TAGS_INIT(&(yyval.tag)); } -#line 2656 "gram.c" +#line 2655 "gram.c" break; case 93: /* cmndtag: cmndtag NOPASSWD */ -#line 871 "gram.y" +#line 870 "gram.y" { (yyval.tag).nopasswd = true; } -#line 2664 "gram.c" +#line 2663 "gram.c" break; case 94: /* cmndtag: cmndtag PASSWD */ -#line 874 "gram.y" +#line 873 "gram.y" { (yyval.tag).nopasswd = false; } -#line 2672 "gram.c" +#line 2671 "gram.c" break; case 95: /* cmndtag: cmndtag NOEXEC */ -#line 877 "gram.y" +#line 876 "gram.y" { (yyval.tag).noexec = true; } -#line 2680 "gram.c" +#line 2679 "gram.c" break; case 96: /* cmndtag: cmndtag EXEC */ -#line 880 "gram.y" +#line 879 "gram.y" { (yyval.tag).noexec = false; } -#line 2688 "gram.c" +#line 2687 "gram.c" break; case 97: /* cmndtag: cmndtag INTERCEPT */ -#line 883 "gram.y" +#line 882 "gram.y" { (yyval.tag).intercept = true; } -#line 2696 "gram.c" +#line 2695 "gram.c" break; case 98: /* cmndtag: cmndtag NOINTERCEPT */ -#line 886 "gram.y" +#line 885 "gram.y" { (yyval.tag).intercept = false; } -#line 2704 "gram.c" +#line 2703 "gram.c" break; case 99: /* cmndtag: cmndtag SETENV */ -#line 889 "gram.y" +#line 888 "gram.y" { (yyval.tag).setenv = true; } -#line 2712 "gram.c" +#line 2711 "gram.c" break; case 100: /* cmndtag: cmndtag NOSETENV */ -#line 892 "gram.y" +#line 891 "gram.y" { (yyval.tag).setenv = false; } -#line 2720 "gram.c" +#line 2719 "gram.c" break; case 101: /* cmndtag: cmndtag LOG_INPUT */ -#line 895 "gram.y" +#line 894 "gram.y" { (yyval.tag).log_input = true; } -#line 2728 "gram.c" +#line 2727 "gram.c" break; case 102: /* cmndtag: cmndtag NOLOG_INPUT */ -#line 898 "gram.y" +#line 897 "gram.y" { (yyval.tag).log_input = false; } -#line 2736 "gram.c" +#line 2735 "gram.c" break; case 103: /* cmndtag: cmndtag LOG_OUTPUT */ -#line 901 "gram.y" +#line 900 "gram.y" { (yyval.tag).log_output = true; } -#line 2744 "gram.c" +#line 2743 "gram.c" break; case 104: /* cmndtag: cmndtag NOLOG_OUTPUT */ -#line 904 "gram.y" +#line 903 "gram.y" { (yyval.tag).log_output = false; } -#line 2752 "gram.c" +#line 2751 "gram.c" break; case 105: /* cmndtag: cmndtag FOLLOWLNK */ -#line 907 "gram.y" +#line 906 "gram.y" { (yyval.tag).follow = true; } -#line 2760 "gram.c" +#line 2759 "gram.c" break; case 106: /* cmndtag: cmndtag NOFOLLOWLNK */ -#line 910 "gram.y" +#line 909 "gram.y" { (yyval.tag).follow = false; } -#line 2768 "gram.c" +#line 2767 "gram.c" break; case 107: /* cmndtag: cmndtag MAIL */ -#line 913 "gram.y" +#line 912 "gram.y" { (yyval.tag).send_mail = true; } -#line 2776 "gram.c" +#line 2775 "gram.c" break; case 108: /* cmndtag: cmndtag NOMAIL */ -#line 916 "gram.y" +#line 915 "gram.y" { (yyval.tag).send_mail = false; } -#line 2784 "gram.c" +#line 2783 "gram.c" break; case 109: /* cmnd: ALL */ -#line 921 "gram.y" +#line 920 "gram.y" { struct sudo_command *c; @@ -2805,11 +2804,11 @@ yyreduce: } parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 2803 "gram.c" +#line 2802 "gram.c" break; case 110: /* cmnd: ALIAS */ -#line 935 "gram.y" +#line 934 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), ALIAS); if ((yyval.member) == NULL) { @@ -2819,11 +2818,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 2817 "gram.c" +#line 2816 "gram.c" break; case 111: /* cmnd: COMMAND */ -#line 944 "gram.y" +#line 943 "gram.y" { struct sudo_command *c; @@ -2845,20 +2844,20 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].command).args); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 2843 "gram.c" +#line 2842 "gram.c" break; case 114: /* $@1: %empty */ -#line 971 "gram.y" +#line 970 "gram.y" { alias_line = this_lineno; alias_column = sudolinebuf.toke_start + 1; } -#line 2852 "gram.c" +#line 2851 "gram.c" break; case 115: /* hostalias: ALIAS $@1 '=' hostlist */ -#line 974 "gram.y" +#line 973 "gram.y" { if (!alias_add(&parsed_policy, (yyvsp[-3].string), HOSTALIAS, sudoers, alias_line, alias_column, (yyvsp[0].member))) { @@ -2868,30 +2867,30 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[-3].string)); parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); } -#line 2866 "gram.c" +#line 2865 "gram.c" break; case 118: /* hostlist: hostlist ',' ophost */ -#line 987 "gram.y" +#line 986 "gram.y" { parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); HLTQ_CONCAT((yyvsp[-2].member), (yyvsp[0].member), entries); (yyval.member) = (yyvsp[-2].member); } -#line 2876 "gram.c" +#line 2875 "gram.c" break; case 121: /* $@2: %empty */ -#line 998 "gram.y" +#line 997 "gram.y" { alias_line = this_lineno; alias_column = sudolinebuf.toke_start + 1; } -#line 2885 "gram.c" +#line 2884 "gram.c" break; case 122: /* cmndalias: ALIAS $@2 '=' cmndlist */ -#line 1001 "gram.y" +#line 1000 "gram.y" { if (!alias_add(&parsed_policy, (yyvsp[-3].string), CMNDALIAS, sudoers, alias_line, alias_column, (yyvsp[0].member))) { @@ -2901,30 +2900,30 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[-3].string)); parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); } -#line 2899 "gram.c" +#line 2898 "gram.c" break; case 125: /* cmndlist: cmndlist ',' digcmnd */ -#line 1014 "gram.y" +#line 1013 "gram.y" { parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); HLTQ_CONCAT((yyvsp[-2].member), (yyvsp[0].member), entries); (yyval.member) = (yyvsp[-2].member); } -#line 2909 "gram.c" +#line 2908 "gram.c" break; case 128: /* $@3: %empty */ -#line 1025 "gram.y" +#line 1024 "gram.y" { alias_line = this_lineno; alias_column = sudolinebuf.toke_start + 1; } -#line 2918 "gram.c" +#line 2917 "gram.c" break; case 129: /* runasalias: ALIAS $@3 '=' userlist */ -#line 1028 "gram.y" +#line 1027 "gram.y" { if (!alias_add(&parsed_policy, (yyvsp[-3].string), RUNASALIAS, sudoers, alias_line, alias_column, (yyvsp[0].member))) { @@ -2934,20 +2933,20 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[-3].string)); parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); } -#line 2932 "gram.c" +#line 2931 "gram.c" break; case 133: /* $@4: %empty */ -#line 1044 "gram.y" +#line 1043 "gram.y" { alias_line = this_lineno; alias_column = sudolinebuf.toke_start + 1; } -#line 2941 "gram.c" +#line 2940 "gram.c" break; case 134: /* useralias: ALIAS $@4 '=' userlist */ -#line 1047 "gram.y" +#line 1046 "gram.y" { if (!alias_add(&parsed_policy, (yyvsp[-3].string), USERALIAS, sudoers, alias_line, alias_column, (yyvsp[0].member))) { @@ -2957,39 +2956,39 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[-3].string)); parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); } -#line 2955 "gram.c" +#line 2954 "gram.c" break; case 137: /* userlist: userlist ',' opuser */ -#line 1060 "gram.y" +#line 1059 "gram.y" { parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); HLTQ_CONCAT((yyvsp[-2].member), (yyvsp[0].member), entries); (yyval.member) = (yyvsp[-2].member); } -#line 2965 "gram.c" +#line 2964 "gram.c" break; case 138: /* opuser: user */ -#line 1067 "gram.y" +#line 1066 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = false; } -#line 2974 "gram.c" +#line 2973 "gram.c" break; case 139: /* opuser: '!' user */ -#line 1071 "gram.y" +#line 1070 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = true; } -#line 2983 "gram.c" +#line 2982 "gram.c" break; case 140: /* user: ALIAS */ -#line 1077 "gram.y" +#line 1076 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), ALIAS); if ((yyval.member) == NULL) { @@ -2999,11 +2998,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 2997 "gram.c" +#line 2996 "gram.c" break; case 141: /* user: ALL */ -#line 1086 "gram.y" +#line 1085 "gram.y" { (yyval.member) = new_member(NULL, ALL); if ((yyval.member) == NULL) { @@ -3012,11 +3011,11 @@ yyreduce: } parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 3010 "gram.c" +#line 3009 "gram.c" break; case 142: /* user: NETGROUP */ -#line 1094 "gram.y" +#line 1093 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), NETGROUP); if ((yyval.member) == NULL) { @@ -3026,11 +3025,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 3024 "gram.c" +#line 3023 "gram.c" break; case 143: /* user: USERGROUP */ -#line 1103 "gram.y" +#line 1102 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), USERGROUP); if ((yyval.member) == NULL) { @@ -3040,11 +3039,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 3038 "gram.c" +#line 3037 "gram.c" break; case 144: /* user: WORD */ -#line 1112 "gram.y" +#line 1111 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), WORD); if ((yyval.member) == NULL) { @@ -3054,39 +3053,39 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 3052 "gram.c" +#line 3051 "gram.c" break; case 146: /* grouplist: grouplist ',' opgroup */ -#line 1124 "gram.y" +#line 1123 "gram.y" { parser_leak_remove(LEAK_MEMBER, (yyvsp[0].member)); HLTQ_CONCAT((yyvsp[-2].member), (yyvsp[0].member), entries); (yyval.member) = (yyvsp[-2].member); } -#line 3062 "gram.c" +#line 3061 "gram.c" break; case 147: /* opgroup: group */ -#line 1131 "gram.y" +#line 1130 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = false; } -#line 3071 "gram.c" +#line 3070 "gram.c" break; case 148: /* opgroup: '!' group */ -#line 1135 "gram.y" +#line 1134 "gram.y" { (yyval.member) = (yyvsp[0].member); (yyval.member)->negated = true; } -#line 3080 "gram.c" +#line 3079 "gram.c" break; case 149: /* group: ALIAS */ -#line 1141 "gram.y" +#line 1140 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), ALIAS); if ((yyval.member) == NULL) { @@ -3096,11 +3095,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 3094 "gram.c" +#line 3093 "gram.c" break; case 150: /* group: ALL */ -#line 1150 "gram.y" +#line 1149 "gram.y" { (yyval.member) = new_member(NULL, ALL); if ((yyval.member) == NULL) { @@ -3109,11 +3108,11 @@ yyreduce: } parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 3107 "gram.c" +#line 3106 "gram.c" break; case 151: /* group: WORD */ -#line 1158 "gram.y" +#line 1157 "gram.y" { (yyval.member) = new_member((yyvsp[0].string), WORD); if ((yyval.member) == NULL) { @@ -3123,11 +3122,11 @@ yyreduce: parser_leak_remove(LEAK_PTR, (yyvsp[0].string)); parser_leak_add(LEAK_MEMBER, (yyval.member)); } -#line 3121 "gram.c" +#line 3120 "gram.c" break; -#line 3125 "gram.c" +#line 3124 "gram.c" default: break; } @@ -3320,32 +3319,20 @@ yyreturnlab: return yyresult; } -#line 1168 "gram.y" +#line 1167 "gram.y" /* Like yyerror() but takes a printf-style format string. */ void sudoerserrorf(const char *fmt, ...) { + const int column = sudolinebuf.toke_start + 1; va_list ap; debug_decl(sudoerserrorf, SUDOERS_DEBUG_PARSER); - /* Save the line the first error occurred on. */ - if (errorlineno == -1) { - sudo_rcstr_delref(errorfile); - errorfile = sudo_rcstr_addref(sudoers); - errorlineno = this_lineno; - errorcolumn = sudolinebuf.toke_start + 1; - if (fmt != NULL) { - va_start(ap, fmt); - if (strcmp(fmt, "%s") == 0) { - /* Optimize common case, a single string. */ - errorstring = strdup(_(va_arg(ap, char *))); - } else { - if (vasprintf(&errorstring, fmt, ap) == -1) - errorstring = NULL; - } - va_end(ap); - } + if (sudoers_error_hook != NULL) { + va_start(ap, fmt); + sudoers_error_hook(sudoers, this_lineno, column, fmt, ap); + va_end(ap); } if (sudoers_warnings && fmt != NULL) { LEXTRACE("<*> "); @@ -3362,7 +3349,7 @@ sudoerserrorf(const char *fmt, ...) /* Optimize common case, a single string. */ s = _(va_arg(ap, char *)); } else { - if (vasprintf(&s, fmt, ap) != -1) + if (vasprintf(&s, _(fmt), ap) != -1) tofree = s; else s = _("syntax error"); @@ -3923,12 +3910,6 @@ init_parser(const char *path, bool quiet, bool strict) } parse_error = false; - errorlineno = -1; - errorcolumn = -1; - free(errorstring); - errorstring = NULL; - sudo_rcstr_delref(errorfile); - errorfile = NULL; sudoers_warnings = !quiet; sudoers_strict = strict; diff --git a/plugins/sudoers/gram.h b/plugins/sudoers/gram.h index 339fd0050..481d5cbef 100644 --- a/plugins/sudoers/gram.h +++ b/plugins/sudoers/gram.h @@ -171,7 +171,7 @@ extern int sudoersdebug; #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { -#line 88 "gram.y" +#line 87 "gram.y" struct cmndspec *cmndspec; struct defaults *defaults; diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y index d827be7fd..add599bcb 100644 --- a/plugins/sudoers/gram.y +++ b/plugins/sudoers/gram.y @@ -2,7 +2,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2021 + * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2022 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -51,10 +51,9 @@ bool sudoers_warnings = true; bool sudoers_strict = false; bool parse_error = false; -int errorlineno = -1; -int errorcolumn = -1; -char *errorfile = NULL; -char *errorstring = NULL; + +/* Optional logging function for parse errors. */ +sudoers_logger_t sudoers_error_hook; static int alias_line, alias_column; @@ -1170,26 +1169,14 @@ group : ALIAS { void sudoerserrorf(const char *fmt, ...) { + const int column = sudolinebuf.toke_start + 1; va_list ap; debug_decl(sudoerserrorf, SUDOERS_DEBUG_PARSER); - /* Save the line the first error occurred on. */ - if (errorlineno == -1) { - sudo_rcstr_delref(errorfile); - errorfile = sudo_rcstr_addref(sudoers); - errorlineno = this_lineno; - errorcolumn = sudolinebuf.toke_start + 1; - if (fmt != NULL) { - va_start(ap, fmt); - if (strcmp(fmt, "%s") == 0) { - /* Optimize common case, a single string. */ - errorstring = strdup(_(va_arg(ap, char *))); - } else { - if (vasprintf(&errorstring, fmt, ap) == -1) - errorstring = NULL; - } - va_end(ap); - } + if (sudoers_error_hook != NULL) { + va_start(ap, fmt); + sudoers_error_hook(sudoers, this_lineno, column, fmt, ap); + va_end(ap); } if (sudoers_warnings && fmt != NULL) { LEXTRACE("<*> "); @@ -1206,7 +1193,7 @@ sudoerserrorf(const char *fmt, ...) /* Optimize common case, a single string. */ s = _(va_arg(ap, char *)); } else { - if (vasprintf(&s, fmt, ap) != -1) + if (vasprintf(&s, _(fmt), ap) != -1) tofree = s; else s = _("syntax error"); @@ -1767,12 +1754,6 @@ init_parser(const char *path, bool quiet, bool strict) } parse_error = false; - errorlineno = -1; - errorcolumn = -1; - free(errorstring); - errorstring = NULL; - sudo_rcstr_delref(errorfile); - errorfile = NULL; sudoers_warnings = !quiet; sudoers_strict = strict; diff --git a/plugins/sudoers/logging.c b/plugins/sudoers/logging.c index 4fabaaad3..4518fcb55 100644 --- a/plugins/sudoers/logging.c +++ b/plugins/sudoers/logging.c @@ -769,6 +769,37 @@ gai_log_warning(int flags, int errnum, const char *fmt, ...) debug_return_bool(ret); } +/* + * Log and mail a parse error using log_warningx(). + * Does not write the message to stderr. + */ +bool +log_parse_error(const char *file, int line, int column, const char *fmt, + va_list args) +{ + const int flags = SLOG_SEND_MAIL|SLOG_NO_STDERR; + char *errstr, *tofree = NULL; + bool ret; + debug_decl(log_parse_error, SUDOERS_DEBUG_LOGGING); + + if (strcmp(fmt, "%s") == 0) { + /* Optimize common case, a single string. */ + errstr = _(va_arg(args, char *)); + } else { + if (vasprintf(&errstr, _(fmt), args) == -1) + debug_return_bool(false); + tofree = errstr; + } + + if (line > 0) + ret = log_warningx(flags, _("%s:%d:%d: %s"), file, line, column, errstr); + else + ret = log_warningx(flags, _("%s: %s"), file, errstr); + free(tofree); + + debug_return_bool(ret); +} + /* * Determine whether we should send mail based on "status" and defaults options. */ diff --git a/plugins/sudoers/logging.h b/plugins/sudoers/logging.h index f61de455b..cc12f87cc 100644 --- a/plugins/sudoers/logging.h +++ b/plugins/sudoers/logging.h @@ -57,6 +57,8 @@ struct log_details { #define SLOG_NO_LOG 0x20 /* do not log via file or syslog */ #define SLOG_AUDIT 0x40 /* send message to audit as well */ +typedef bool (*sudoers_logger_t)(const char *file, int line, int column, const char *fmt, va_list args); + /* XXX - needed for auditing */ extern int NewArgc; extern char **NewArgv; @@ -86,5 +88,6 @@ bool sudoers_locale_callback(const union sudo_defs_val *sd_un, int op); void sudoers_to_eventlog(struct eventlog *evlog, char * const argv[], char *const envp[], const char *uuid_str); void init_eventlog_config(void); bool init_log_details(struct log_details *details, struct eventlog *evlog); +bool log_parse_error(const char *file, int line, int column, const char *fmt, va_list ap) __printflike(4, 0); #endif /* SUDOERS_LOGGING_H */ diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index 0375b54be..ed15a3440 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2000, 2004, 2007-2021 + * Copyright (c) 1996, 1998-2000, 2004, 2007-2022 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -361,8 +361,9 @@ void alias_put(struct alias *a); /* check_aliases.c */ int check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet, int (*cb_unused)(struct sudoers_parse_tree *, struct alias *, void *)); -/* gram.c */ +/* 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 quiet, bool strict); void free_member(struct member *m); void free_members(struct member_list *members); diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index c2aee5130..c25fc3e4c 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -1000,7 +1000,7 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation, info.settings = settings; info.user_info = user_info; info.plugin_args = args; - ret = sudoers_init(&info, envp); + ret = sudoers_init(&info, log_parse_error, envp); /* The audit functions set audit_msg on failure. */ if (ret != 1 && audit_msg != NULL) { diff --git a/plugins/sudoers/regress/fuzz/fuzz_policy.c b/plugins/sudoers/regress/fuzz/fuzz_policy.c index d8feec60f..1d4d70e0a 100644 --- a/plugins/sudoers/regress/fuzz/fuzz_policy.c +++ b/plugins/sudoers/regress/fuzz/fuzz_policy.c @@ -742,6 +742,14 @@ log_exit_status(int exit_status) return true; } +/* STUB */ +bool +log_parse_error(const char *file, int line, int column, const char *fmt, + va_list args) +{ + return true; +} + /* STUB */ int audit_failure(char *const argv[], char const *const fmt, ...) diff --git a/plugins/sudoers/regress/sudoers/test18.toke.ok b/plugins/sudoers/regress/sudoers/test18.toke.ok index 78e9ba6fa..7c800a8ee 100644 --- a/plugins/sudoers/regress/sudoers/test18.toke.ok +++ b/plugins/sudoers/regress/sudoers/test18.toke.ok @@ -6,5 +6,5 @@ WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND WORD(6) ALL = CMND_TIMEOUT = WORD(6) <*> COMMAND -testsudoers: sudoers:2:26: value "2d8h10m59ss" is invalid for option "command_timeout" -testsudoers: sudoers:3:31: value "15f" is invalid for option "command_timeout" +sudoers:2:26: value "2d8h10m59ss" is invalid for option "command_timeout" +sudoers:3:31: value "15f" is invalid for option "command_timeout" diff --git a/plugins/sudoers/regress/visudo/test2.err.ok b/plugins/sudoers/regress/visudo/test2.err.ok index e6a275df1..d4b356f9d 100644 --- a/plugins/sudoers/regress/visudo/test2.err.ok +++ b/plugins/sudoers/regress/visudo/test2.err.ok @@ -1 +1 @@ -Error: stdin:1:12: cycle in User_Alias "FOO" +stdin:1:12: cycle in User_Alias "FOO" diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index 854dde7da..062e8ad27 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -162,8 +162,6 @@ sudoers_reinit_defaults(void) if (!update_defaults(NULL, &initial_defaults, SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, - N_("problem with defaults entries")); debug_return_bool(false); } @@ -172,19 +170,16 @@ sudoers_reinit_defaults(void) log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, N_("unable to get defaults from %s"), nss->source); } - if (!update_defaults(nss->parse_tree, NULL, - SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, - N_("problem with defaults entries")); - /* not a fatal error */ - } + /* Not a fatal error. */ + (void)update_defaults(nss->parse_tree, NULL, + SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false); } debug_return_int(true); } int -sudoers_init(void *info, char * const envp[]) +sudoers_init(void *info, sudoers_logger_t logger, char * const envp[]) { struct sudo_nss *nss, *nss_next; int oldlocale, sources = 0; @@ -197,6 +192,9 @@ sudoers_init(void *info, char * const envp[]) bindtextdomain("sudoers", LOCALEDIR); + /* Hook up logging function for parse errors. */ + sudoers_error_hook = logger; + /* Register fatal/fatalx callback. */ sudo_fatal_callback_register(sudoers_cleanup); @@ -228,8 +226,6 @@ sudoers_init(void *info, char * const envp[]) /* Update defaults set by front-end. */ if (!update_defaults(NULL, &initial_defaults, SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, - N_("problem with defaults entries")); debug_return_int(-1); } @@ -251,11 +247,9 @@ sudoers_init(void *info, char * const envp[]) log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, N_("unable to get defaults from %s"), nss->source); } - if (!update_defaults(nss->parse_tree, NULL, - SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false)) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, - N_("problem with defaults entries")); - } + /* Not a fatal error. */ + (void)update_defaults(nss->parse_tree, NULL, + SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER|SETDEF_RUNAS, false); } if (sources == 0) { sudo_warnx("%s", U_("no valid sudoers sources found, quitting")); @@ -1052,10 +1046,8 @@ set_cmnd(void) } TAILQ_FOREACH(nss, snl, entries) { - if (!update_defaults(nss->parse_tree, NULL, SETDEF_CMND, false)) { - log_warningx(SLOG_SEND_MAIL|SLOG_NO_STDERR, - N_("problem with defaults entries")); - } + /* Not a fatal error. */ + (void)update_defaults(nss->parse_tree, NULL, SETDEF_CMND, false); } debug_return_int(ret); diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 255fe4953..aca5bf680 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -310,10 +310,6 @@ int pam_prep_user(struct passwd *); /* gram.y */ int sudoersparse(void); extern char *login_style; -extern char *errorfile; -extern int errorlineno; -extern int errorcolumn; -extern char *errorstring; extern bool parse_error; extern bool sudoers_warnings; extern bool sudoers_recovery; @@ -413,7 +409,7 @@ bool matches_env_pattern(const char *pattern, const char *var, bool *full_match) /* sudoers.c */ FILE *open_sudoers(const char *, bool, bool *); int set_cmnd_path(const char *runchroot); -int sudoers_init(void *info, char * const envp[]); +int sudoers_init(void *info, sudoers_logger_t logger, char * const envp[]); int sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], bool verbose, void *closure); void sudoers_cleanup(void); void sudo_user_free(void); diff --git a/plugins/sudoers/visudo.c b/plugins/sudoers/visudo.c index 738fb1306..a26da077c 100644 --- a/plugins/sudoers/visudo.c +++ b/plugins/sudoers/visudo.c @@ -77,6 +77,7 @@ struct sudoersfile { bool modified; bool doedit; int fd; + int errorline; }; TAILQ_HEAD(sudoersfile_list, sudoersfile); @@ -89,6 +90,7 @@ static char *get_editor(int *editor_argc, char ***editor_argv); static bool check_syntax(const char *, bool, bool, bool, bool); static bool edit_sudoers(struct sudoersfile *, char *, int, char **, int); static bool install_sudoers(struct sudoersfile *, bool, bool); +static bool visudo_track_error(const char *file, int line, int column, const char *fmt, va_list args); static int print_unused(struct sudoers_parse_tree *, struct alias *, void *); static bool reparse_sudoers(char *, int, char **, bool, bool); static int run_command(char *, char **); @@ -107,6 +109,7 @@ struct sudo_user sudo_user; struct passwd *list_pw; static struct sudoersfile_list sudoerslist = TAILQ_HEAD_INITIALIZER(sudoerslist); static bool checkonly; +static unsigned int errors; static const char short_opts[] = "cf:hOPqsVx:"; static struct option long_opts[] = { { "check", no_argument, NULL, 'c' }, @@ -256,6 +259,9 @@ main(int argc, char *argv[]) } get_hostname(); + /* Hook the sudoers parser to track files with parse errors. */ + sudoers_error_hook = visudo_track_error; + /* Setup defaults data structures. */ if (!init_defaults()) sudo_fatalx("%s", U_("unable to initialize sudoers default values")); @@ -320,6 +326,28 @@ done: exit(exitcode); } +static bool +visudo_track_error(const char *file, int line, int column, const char *fmt, + va_list args) +{ + struct sudoersfile *sp; + debug_decl(visudo_track_error, SUDOERS_DEBUG_UTIL); + + TAILQ_FOREACH(sp, &sudoerslist, entries) { + if (sp->errorline > 0) + continue; /* preserve the first error */ + + if (strcmp(file, sp->path) == 0 || + (sp->tpath != NULL && strcmp(file, sp->tpath) == 0)) { + sp->errorline = line; + break; + } + } + errors++; + + debug_return_bool(true); +} + static char * get_editor(int *editor_argc, char ***editor_argv) { @@ -564,7 +592,7 @@ done: /* * Check Defaults and Alias entries. - * Sets parse_error on error and error{file,lineno,column} if possible. + * On error, visudo_track_error() will set the line number in sudoerslist. */ static void check_defaults_and_aliases(bool strict, bool quiet) @@ -572,21 +600,6 @@ check_defaults_and_aliases(bool strict, bool quiet) debug_decl(check_defaults_and_aliases, SUDOERS_DEBUG_UTIL); if (!check_defaults(&parsed_policy, quiet)) { - struct defaults *d; - sudo_rcstr_delref(errorfile); - errorfile = NULL; - errorlineno = -1; - errorcolumn = -1; - /* XXX - should edit all files with errors */ - TAILQ_FOREACH(d, &parsed_policy.defaults, entries) { - if (d->error && errorlineno == -1) { - /* Defaults parse error, set error{file,lineno,column}. */ - errorfile = sudo_rcstr_addref(d->file); - errorlineno = d->line; - errorcolumn = d->column; - break; - } - } parse_error = true; } if (check_aliases(&parsed_policy, strict, quiet, print_unused) != 0) { @@ -610,6 +623,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv, /* * Parse the edited sudoers files. */ + errors = 0; while ((sp = TAILQ_FIRST(&sudoerslist)) != NULL) { last = TAILQ_LAST(&sudoerslist, sudoersfile_list); fp = fopen(sp->tpath, "r+"); @@ -621,6 +635,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv, if (!init_defaults()) sudo_fatalx("%s", U_("unable to initialize sudoers default values")); init_parser(sp->path, quiet, true); + sp->errorline = -1; /* Parse the sudoers temp file(s) */ sudoersrestart(fp); @@ -629,9 +644,6 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv, sudo_warnx(U_("unable to parse temporary file (%s), unknown error"), sp->tpath); parse_error = true; - sudo_rcstr_delref(errorfile); - if ((errorfile = sudo_rcstr_dup(sp->path)) == NULL) - sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } fclose(sudoersin); if (!parse_error) { @@ -656,17 +668,11 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv, default: /* Edit file with the parse error */ TAILQ_FOREACH(sp, &sudoerslist, entries) { - if (errorfile == NULL || strcmp(sp->path, errorfile) == 0) { + if (errors == 0 || sp->errorline > 0) { edit_sudoers(sp, editor, editor_argc, editor_argv, - errorlineno); - if (errorfile != NULL) - break; + sp->errorline); } } - if (errorfile != NULL && sp == NULL) { - sudo_fatalx(U_("internal error, unable to find %s in list!"), - sudoers); - } break; } } @@ -959,9 +965,6 @@ check_syntax(const char *file, bool quiet, bool strict, bool check_owner, if (!quiet) sudo_warnx(U_("failed to parse %s file, unknown error"), file); parse_error = true; - sudo_rcstr_delref(errorfile); - if ((errorfile = sudo_rcstr_dup(file)) == NULL) - sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } if (!parse_error) { (void) update_defaults(&parsed_policy, NULL, @@ -1186,13 +1189,9 @@ visudo_cleanup(void) static void quit(int signo) { - struct sudoersfile *sp; struct iovec iov[4]; - TAILQ_FOREACH(sp, &sudoerslist, entries) { - if (sp->tpath != NULL) - (void) unlink(sp->tpath); - } + visudo_cleanup(); #define emsg " exiting due to signal: " iov[0].iov_base = (char *)getprogname();