From 7524c231cc548eb56f7ec04fafcb39e4e77740f8 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Sat, 12 Nov 2016 19:22:32 -0700 Subject: [PATCH] Store the file/lineno for alias and userspec entries so we can provide that info if there is an error. --- doc/visudo.cat | 39 ++-- doc/visudo.man.in | 42 +++- doc/visudo.mdoc.in | 41 +++- plugins/sudoers/alias.c | 8 +- plugins/sudoers/gram.c | 233 ++++++++++---------- plugins/sudoers/gram.y | 29 ++- plugins/sudoers/parse.h | 8 +- plugins/sudoers/regress/visudo/test2.err.ok | 2 +- plugins/sudoers/regress/visudo/test3.err.ok | 4 +- plugins/sudoers/visudo.c | 40 ++-- 10 files changed, 267 insertions(+), 179 deletions(-) diff --git a/doc/visudo.cat b/doc/visudo.cat index 2eb83ce97..98fd31aab 100644 --- a/doc/visudo.cat +++ b/doc/visudo.cat @@ -135,34 +135,47 @@ FFIILLEESS _/_e_t_c_/_s_u_d_o_e_r_s_._t_m_p Lock file for visudo DDIIAAGGNNOOSSTTIICCSS + In addition to reporting _s_u_d_o_e_r_s parse errors, vviissuuddoo may produce the + following messages: + sudoers file busy, try again later. Someone else is currently editing the _s_u_d_o_e_r_s file. /etc/sudoers.tmp: Permission denied You didn't run vviissuuddoo as root. - Can't find you in the passwd database - Your user ID does not appear in the system passwd file. + you do not exist in the passwd database + Your user ID does not appear in the system passwd database. - Warning: {User,Runas,Host,Cmnd}_Alias referenced but not defined + {User,Runas,Host,Cmnd}_Alias referenced but not defined Either you are trying to use an undeclared {User,Runas,Host,Cmnd}_Alias or you have a user or host name listed that consists solely of uppercase letters, digits, and the underscore (`_') character. In the latter case, you can ignore the - warnings (ssuuddoo will not complain). In --ss (strict) mode these are - errors, not warnings. + warnings (ssuuddoo will not complain). The message is prefixed with + the path name of the _s_u_d_o_e_r_s file and the line number where the + undefined alias was used. In --ss (strict) mode these are errors, + not warnings. - Warning: unused {User,Runas,Host,Cmnd}_Alias + unused {User,Runas,Host,Cmnd}_Alias The specified {User,Runas,Host,Cmnd}_Alias was defined but never - used. You may wish to comment out or remove the unused alias. + used. The message is prefixed with the path name of the _s_u_d_o_e_r_s + file and the line number where the unused alias was defined. You + may wish to comment out or remove the unused alias. - Warning: cycle in {User,Runas,Host,Cmnd}_Alias + cycle in {User,Runas,Host,Cmnd}_Alias The specified {User,Runas,Host,Cmnd}_Alias includes a reference to - itself, either directly or through an alias it includes. This is - only a warning by default as ssuuddoo will ignore cycles when parsing - the _s_u_d_o_e_r_s file. + itself, either directly or through an alias it includes. The + message is prefixed with the path name of the _s_u_d_o_e_r_s file and the + line number where the cycle was detected. This is only a warning + unless vviissuuddoo is run in --ss (strict) mode as ssuuddoo will ignore cycles + when parsing the _s_u_d_o_e_r_s file. + + unknown defaults entry "name" + The _s_u_d_o_e_r_s file contains a Defaults variable not recognized by + vviissuuddoo. - visudo: /etc/sudoers: input and output files must be different + /etc/sudoers: input and output files must be different The --xx flag was used and the specified _o_u_t_p_u_t___f_i_l_e has the same path name as the _s_u_d_o_e_r_s file to export. @@ -199,4 +212,4 @@ DDIISSCCLLAAIIMMEERR file distributed with ssuuddoo or https://www.sudo.ws/license.html for complete details. -Sudo 1.8.18 November 20, 2015 Sudo 1.8.18 +Sudo 1.8.19 November 12, 2016 Sudo 1.8.19 diff --git a/doc/visudo.man.in b/doc/visudo.man.in index 86d19a096..eb9a64e2e 100644 --- a/doc/visudo.man.in +++ b/doc/visudo.man.in @@ -21,7 +21,7 @@ .\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" -.TH "VISUDO" "8" "November 20, 2015" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" +.TH "VISUDO" "8" "November 12, 2016" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" .nh .if n .ad l .SH "NAME" @@ -324,6 +324,11 @@ List of who can run what \fI@sysconfdir@/sudoers.tmp\fR Lock file for visudo .SH "DIAGNOSTICS" +In addition to reporting +\fIsudoers\fR +parse errors, +\fBvisudo\fR +may produce the following messages: .TP 6n \fRsudoers file busy, try again later.\fR Someone else is currently editing the @@ -335,10 +340,10 @@ You didn't run \fBvisudo\fR as root. .TP 6n -\fRCan't find you in the passwd database\fR -Your user ID does not appear in the system passwd file. +\fRyou do not exist in the passwd database\fR +Your user ID does not appear in the system passwd database. .TP 6n -\fRWarning: {User,Runas,Host,Cmnd}_Alias referenced but not defined\fR +\fR{User,Runas,Host,Cmnd}_Alias referenced but not defined\fR Either you are trying to use an undeclared {User,Runas,Host,Cmnd}_Alias or you have a user or host name listed that consists solely of uppercase letters, digits, and the underscore @@ -348,26 +353,47 @@ In the latter case, you can ignore the warnings (\fBsudo\fR will not complain) \&. +The message is prefixed with the path name of the +\fIsudoers\fR +file and the line number where the undefined alias was used. In \fB\-s\fR (strict) mode these are errors, not warnings. .TP 6n -\fRWarning: unused {User,Runas,Host,Cmnd}_Alias\fR +\fRunused {User,Runas,Host,Cmnd}_Alias\fR The specified {User,Runas,Host,Cmnd}_Alias was defined but never used. +The message is prefixed with the path name of the +\fIsudoers\fR +file and the line number where the unused alias was defined. You may wish to comment out or remove the unused alias. .TP 6n -\fRWarning: cycle in {User,Runas,Host,Cmnd}_Alias\fR +\fRcycle in {User,Runas,Host,Cmnd}_Alias\fR The specified {User,Runas,Host,Cmnd}_Alias includes a reference to itself, either directly or through an alias it includes. -This is only a warning by default as +The message is prefixed with the path name of the +\fIsudoers\fR +file and the line number where the cycle was detected. +This is only a warning unless +\fBvisudo\fR +is run in +\fB\-s\fR +(strict) mode as \fBsudo\fR will ignore cycles when parsing the \fIsudoers\fR file. .TP 6n -\fRvisudo: @sysconfdir@/sudoers: input and output files must be different\fR +\fRunknown defaults entry \&"name\&"\fR +The +\fIsudoers\fR +file contains a +\fRDefaults\fR +variable not recognized by +\fBvisudo\fR. +.TP 6n +\fR@sysconfdir@/sudoers: input and output files must be different\fR The \fB\-x\fR flag was used and the specified diff --git a/doc/visudo.mdoc.in b/doc/visudo.mdoc.in index e271a84b4..6f9bb28cf 100644 --- a/doc/visudo.mdoc.in +++ b/doc/visudo.mdoc.in @@ -19,7 +19,7 @@ .\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" -.Dd November 20, 2015 +.Dd November 12, 2016 .Dt VISUDO @mansectsu@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -310,6 +310,11 @@ List of who can run what Lock file for visudo .El .Sh DIAGNOSTICS +In addition to reporting +.Em sudoers +parse errors, +.Nm +may produce the following messages: .Bl -tag -width 4n .It Li sudoers file busy, try again later. Someone else is currently editing the @@ -319,9 +324,9 @@ file. You didn't run .Nm as root. -.It Li Can't find you in the passwd database -Your user ID does not appear in the system passwd file. -.It Li Warning: {User,Runas,Host,Cmnd}_Alias referenced but not defined +.It Li you do not exist in the passwd database +Your user ID does not appear in the system passwd database. +.It Li {User,Runas,Host,Cmnd}_Alias referenced but not defined Either you are trying to use an undeclared {User,Runas,Host,Cmnd}_Alias or you have a user or host name listed that consists solely of uppercase letters, digits, and the underscore @@ -332,23 +337,43 @@ In the latter case, you can ignore the warnings .Nm sudo will not complain .Pc . +The message is prefixed with the path name of the +.Em sudoers +file and the line number where the undefined alias was used. In .Fl s (strict) mode these are errors, not warnings. -.It Li Warning: unused {User,Runas,Host,Cmnd}_Alias +.It Li unused {User,Runas,Host,Cmnd}_Alias The specified {User,Runas,Host,Cmnd}_Alias was defined but never used. +The message is prefixed with the path name of the +.Em sudoers +file and the line number where the unused alias was defined. You may wish to comment out or remove the unused alias. -.It Li Warning: cycle in {User,Runas,Host,Cmnd}_Alias +.It Li cycle in {User,Runas,Host,Cmnd}_Alias The specified {User,Runas,Host,Cmnd}_Alias includes a reference to itself, either directly or through an alias it includes. -This is only a warning by default as +The message is prefixed with the path name of the +.Em sudoers +file and the line number where the cycle was detected. +This is only a warning unless +.Nm +is run in +.Fl s +(strict) mode as .Nm sudo will ignore cycles when parsing the .Em sudoers file. -.It Li visudo: @sysconfdir@/sudoers: input and output files must be different +.It Li unknown defaults entry \&"name\&" +The +.Em sudoers +file contains a +.Li Defaults +variable not recognized by +.Nm . +.It Li @sysconfdir@/sudoers: input and output files must be different The .Fl x flag was used and the specified diff --git a/plugins/sudoers/alias.c b/plugins/sudoers/alias.c index b0615376a..afc462476 100644 --- a/plugins/sudoers/alias.c +++ b/plugins/sudoers/alias.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005, 2007-2015 + * Copyright (c) 2004-2005, 2007-2016 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -109,10 +109,11 @@ alias_put(struct alias *a) /* * Add an alias to the aliases redblack tree. + * Note that "file" must be a reference-counted string. * Returns NULL on success and an error string on failure. */ const char * -alias_add(char *name, int type, struct member *members) +alias_add(char *name, int type, char *file, int lineno, struct member *members) { static char errbuf[512]; struct alias *a; @@ -126,6 +127,8 @@ alias_add(char *name, int type, struct member *members) a->name = name; a->type = type; /* a->used = false; */ + a->file = rcstr_addref(file); + a->lineno = lineno; HLTQ_TO_TAILQ(&a->members, members, entries); switch (rbinsert(aliases, a, NULL)) { case 1: @@ -173,6 +176,7 @@ alias_free(void *v) debug_decl(alias_free, SUDOERS_DEBUG_ALIAS) free(a->name); + rcstr_delref(a->file); free_members(&a->members); free(a); diff --git a/plugins/sudoers/gram.c b/plugins/sudoers/gram.c index 896cb0b0d..e622bdd37 100644 --- a/plugins/sudoers/gram.c +++ b/plugins/sudoers/gram.c @@ -87,6 +87,9 @@ #include "parse.h" #include "toke.h" +/* If we last saw a newline the entry is on the preceding line. */ +#define this_lineno (last_token == COMMENT ? sudolineno - 1 : sudolineno) + /* * Globals */ @@ -106,7 +109,7 @@ static bool add_userspec(struct member *, struct privilege *); static struct defaults *new_default(char *, char *, short); static struct member *new_member(char *, int); static struct sudo_digest *new_digest(int, const char *); -#line 73 "gram.y" +#line 76 "gram.y" #ifndef YYSTYPE_DEFINED #define YYSTYPE_DEFINED typedef union { @@ -124,7 +127,7 @@ typedef union { int tok; } YYSTYPE; #endif /* YYSTYPE_DEFINED */ -#line 127 "gram.c" +#line 130 "gram.c" #define COMMAND 257 #define ALIAS 258 #define DEFVAR 259 @@ -689,19 +692,15 @@ short *yysslim; YYSTYPE *yyvs; unsigned int yystacksize; int yyparse(void); -#line 849 "gram.y" +#line 856 "gram.y" void sudoerserror(const char *s) { debug_decl(sudoerserror, SUDOERS_DEBUG_PARSER) - /* If we last saw a newline the error is on the preceding line. */ - if (last_token == COMMENT) - sudolineno--; - /* Save the line the first error occurred on. */ if (errorlineno == -1) { - errorlineno = sudolineno; + errorlineno = this_lineno; rcstr_delref(errorfile); errorfile = rcstr_addref(sudoers); } @@ -714,7 +713,7 @@ sudoerserror(const char *s) /* Warnings are displayed in the user's locale. */ sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale); - sudo_printf(SUDO_CONV_ERROR_MSG, _(fmt), sudoers, _(s), sudolineno); + sudo_printf(SUDO_CONV_ERROR_MSG, _(fmt), sudoers, _(s), this_lineno); sudoers_setlocale(oldlocale, NULL); } #endif @@ -740,7 +739,7 @@ new_default(char *var, char *val, short op) /* d->type = 0; */ d->op = op; /* d->binding = NULL */ - d->lineno = last_token == COMMENT ? sudolineno - 1 : sudolineno; + d->lineno = this_lineno; d->file = rcstr_addref(sudoers); HLTQ_INIT(d, entries); @@ -847,6 +846,8 @@ add_userspec(struct member *members, struct privilege *privs) "unable to allocate memory"); debug_return_bool(false); } + u->lineno = this_lineno; + u->file = rcstr_addref(sudoers); HLTQ_TO_TAILQ(&u->users, members, entries); HLTQ_TO_TAILQ(&u->privileges, privs, entries); TAILQ_INSERT_TAIL(&userspecs, u, entries); @@ -887,6 +888,7 @@ init_parser(const char *path, bool quiet) bool ret = true; debug_decl(init_parser, SUDOERS_DEBUG_PARSER) + /* XXX - move into a free function */ TAILQ_FOREACH_SAFE(us, &userspecs, entries, us_next) { struct member *m, *m_next; struct privilege *priv, *priv_next; @@ -961,6 +963,7 @@ init_parser(const char *path, bool quiet) } free(priv); } + rcstr_delref(us->file); free(us); } TAILQ_INIT(&userspecs); @@ -1004,7 +1007,7 @@ init_parser(const char *path, bool quiet) debug_return_bool(ret); } -#line 955 "gram.c" +#line 958 "gram.c" /* allocate initial stack or double stack size, up to YYMAXDEPTH */ #if defined(__cplusplus) || defined(__STDC__) static int yygrowstack(void) @@ -1213,23 +1216,23 @@ yyreduce: switch (yyn) { case 1: -#line 167 "gram.y" +#line 170 "gram.y" { ; } break; case 5: -#line 175 "gram.y" +#line 178 "gram.y" { ; } break; case 6: -#line 178 "gram.y" +#line 181 "gram.y" { yyerrok; } break; case 7: -#line 181 "gram.y" +#line 184 "gram.y" { if (!add_userspec(yyvsp[-1].member, yyvsp[0].privilege)) { sudoerserror(N_("unable to allocate memory")); @@ -1238,73 +1241,73 @@ case 7: } break; case 8: -#line 187 "gram.y" +#line 190 "gram.y" { ; } break; case 9: -#line 190 "gram.y" +#line 193 "gram.y" { ; } break; case 10: -#line 193 "gram.y" +#line 196 "gram.y" { ; } break; case 11: -#line 196 "gram.y" +#line 199 "gram.y" { ; } break; case 12: -#line 199 "gram.y" +#line 202 "gram.y" { if (!add_defaults(DEFAULTS, NULL, yyvsp[0].defaults)) YYERROR; } break; case 13: -#line 203 "gram.y" +#line 206 "gram.y" { if (!add_defaults(DEFAULTS_USER, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 14: -#line 207 "gram.y" +#line 210 "gram.y" { if (!add_defaults(DEFAULTS_RUNAS, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 15: -#line 211 "gram.y" +#line 214 "gram.y" { if (!add_defaults(DEFAULTS_HOST, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 16: -#line 215 "gram.y" +#line 218 "gram.y" { if (!add_defaults(DEFAULTS_CMND, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 18: -#line 222 "gram.y" +#line 225 "gram.y" { HLTQ_CONCAT(yyvsp[-2].defaults, yyvsp[0].defaults, entries); yyval.defaults = yyvsp[-2].defaults; } break; case 19: -#line 228 "gram.y" +#line 231 "gram.y" { yyval.defaults = new_default(yyvsp[0].string, NULL, true); if (yyval.defaults == NULL) { @@ -1314,7 +1317,7 @@ case 19: } break; case 20: -#line 235 "gram.y" +#line 238 "gram.y" { yyval.defaults = new_default(yyvsp[0].string, NULL, false); if (yyval.defaults == NULL) { @@ -1324,7 +1327,7 @@ case 20: } break; case 21: -#line 242 "gram.y" +#line 245 "gram.y" { yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, true); if (yyval.defaults == NULL) { @@ -1334,7 +1337,7 @@ case 21: } break; case 22: -#line 249 "gram.y" +#line 252 "gram.y" { yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '+'); if (yyval.defaults == NULL) { @@ -1344,7 +1347,7 @@ case 22: } break; case 23: -#line 256 "gram.y" +#line 259 "gram.y" { yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '-'); if (yyval.defaults == NULL) { @@ -1354,14 +1357,14 @@ case 23: } break; case 25: -#line 266 "gram.y" +#line 269 "gram.y" { HLTQ_CONCAT(yyvsp[-2].privilege, yyvsp[0].privilege, entries); yyval.privilege = yyvsp[-2].privilege; } break; case 26: -#line 272 "gram.y" +#line 275 "gram.y" { struct privilege *p = calloc(1, sizeof(*p)); if (p == NULL) { @@ -1375,21 +1378,21 @@ case 26: } break; case 27: -#line 285 "gram.y" +#line 288 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = false; } break; case 28: -#line 289 "gram.y" +#line 292 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = true; } break; case 29: -#line 295 "gram.y" +#line 298 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -1399,7 +1402,7 @@ case 29: } break; case 30: -#line 302 "gram.y" +#line 305 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -1409,7 +1412,7 @@ case 30: } break; case 31: -#line 309 "gram.y" +#line 312 "gram.y" { yyval.member = new_member(yyvsp[0].string, NETGROUP); if (yyval.member == NULL) { @@ -1419,7 +1422,7 @@ case 31: } break; case 32: -#line 316 "gram.y" +#line 319 "gram.y" { yyval.member = new_member(yyvsp[0].string, NTWKADDR); if (yyval.member == NULL) { @@ -1429,7 +1432,7 @@ case 32: } break; case 33: -#line 323 "gram.y" +#line 326 "gram.y" { yyval.member = new_member(yyvsp[0].string, WORD); if (yyval.member == NULL) { @@ -1439,7 +1442,7 @@ case 33: } break; case 35: -#line 333 "gram.y" +#line 336 "gram.y" { struct cmndspec *prev; prev = HLTQ_LAST(yyvsp[-2].cmndspec, cmndspec, entries); @@ -1485,7 +1488,7 @@ case 35: } break; case 36: -#line 378 "gram.y" +#line 381 "gram.y" { struct cmndspec *cs = calloc(1, sizeof(*cs)); if (cs == NULL) { @@ -1534,7 +1537,7 @@ case 36: } break; case 37: -#line 426 "gram.y" +#line 429 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA224, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1544,7 +1547,7 @@ case 37: } break; case 38: -#line 433 "gram.y" +#line 436 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA256, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1554,7 +1557,7 @@ case 38: } break; case 39: -#line 440 "gram.y" +#line 443 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA384, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1564,7 +1567,7 @@ case 39: } break; case 40: -#line 447 "gram.y" +#line 450 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA512, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1574,13 +1577,13 @@ case 40: } break; case 41: -#line 456 "gram.y" +#line 459 "gram.y" { yyval.member = yyvsp[0].member; } break; case 42: -#line 459 "gram.y" +#line 462 "gram.y" { if (yyvsp[0].member->type != COMMAND) { sudoerserror(N_("a digest requires a path name")); @@ -1592,127 +1595,127 @@ case 42: } break; case 43: -#line 470 "gram.y" +#line 473 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = false; } break; case 44: -#line 474 "gram.y" +#line 477 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = true; } break; case 45: -#line 480 "gram.y" +#line 483 "gram.y" { yyval.string = yyvsp[0].string; } break; case 46: -#line 485 "gram.y" +#line 488 "gram.y" { yyval.string = yyvsp[0].string; } break; case 47: -#line 490 "gram.y" +#line 493 "gram.y" { yyval.seinfo.role = NULL; yyval.seinfo.type = NULL; } break; case 48: -#line 494 "gram.y" +#line 497 "gram.y" { yyval.seinfo.role = yyvsp[0].string; yyval.seinfo.type = NULL; } break; case 49: -#line 498 "gram.y" +#line 501 "gram.y" { yyval.seinfo.type = yyvsp[0].string; yyval.seinfo.role = NULL; } break; case 50: -#line 502 "gram.y" +#line 505 "gram.y" { yyval.seinfo.role = yyvsp[-1].string; yyval.seinfo.type = yyvsp[0].string; } break; case 51: -#line 506 "gram.y" +#line 509 "gram.y" { yyval.seinfo.type = yyvsp[-1].string; yyval.seinfo.role = yyvsp[0].string; } break; case 52: -#line 512 "gram.y" +#line 515 "gram.y" { yyval.string = yyvsp[0].string; } break; case 53: -#line 516 "gram.y" +#line 519 "gram.y" { yyval.string = yyvsp[0].string; } break; case 54: -#line 521 "gram.y" +#line 524 "gram.y" { yyval.privinfo.privs = NULL; yyval.privinfo.limitprivs = NULL; } break; case 55: -#line 525 "gram.y" +#line 528 "gram.y" { yyval.privinfo.privs = yyvsp[0].string; yyval.privinfo.limitprivs = NULL; } break; case 56: -#line 529 "gram.y" +#line 532 "gram.y" { yyval.privinfo.privs = NULL; yyval.privinfo.limitprivs = yyvsp[0].string; } break; case 57: -#line 533 "gram.y" +#line 536 "gram.y" { yyval.privinfo.privs = yyvsp[-1].string; yyval.privinfo.limitprivs = yyvsp[0].string; } break; case 58: -#line 537 "gram.y" +#line 540 "gram.y" { yyval.privinfo.limitprivs = yyvsp[-1].string; yyval.privinfo.privs = yyvsp[0].string; } break; case 59: -#line 543 "gram.y" +#line 546 "gram.y" { yyval.runas = NULL; } break; case 60: -#line 546 "gram.y" +#line 549 "gram.y" { yyval.runas = yyvsp[-1].runas; } break; case 61: -#line 551 "gram.y" +#line 554 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas != NULL) { @@ -1730,7 +1733,7 @@ case 61: } break; case 62: -#line 566 "gram.y" +#line 569 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas == NULL) { @@ -1742,7 +1745,7 @@ case 62: } break; case 63: -#line 575 "gram.y" +#line 578 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas == NULL) { @@ -1754,7 +1757,7 @@ case 63: } break; case 64: -#line 584 "gram.y" +#line 587 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas == NULL) { @@ -1766,7 +1769,7 @@ case 64: } break; case 65: -#line 593 "gram.y" +#line 596 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas != NULL) { @@ -1784,97 +1787,97 @@ case 65: } break; case 66: -#line 610 "gram.y" +#line 613 "gram.y" { TAGS_INIT(yyval.tag); } break; case 67: -#line 613 "gram.y" +#line 616 "gram.y" { yyval.tag.nopasswd = true; } break; case 68: -#line 616 "gram.y" +#line 619 "gram.y" { yyval.tag.nopasswd = false; } break; case 69: -#line 619 "gram.y" +#line 622 "gram.y" { yyval.tag.noexec = true; } break; case 70: -#line 622 "gram.y" +#line 625 "gram.y" { yyval.tag.noexec = false; } break; case 71: -#line 625 "gram.y" +#line 628 "gram.y" { yyval.tag.setenv = true; } break; case 72: -#line 628 "gram.y" +#line 631 "gram.y" { yyval.tag.setenv = false; } break; case 73: -#line 631 "gram.y" +#line 634 "gram.y" { yyval.tag.log_input = true; } break; case 74: -#line 634 "gram.y" +#line 637 "gram.y" { yyval.tag.log_input = false; } break; case 75: -#line 637 "gram.y" +#line 640 "gram.y" { yyval.tag.log_output = true; } break; case 76: -#line 640 "gram.y" +#line 643 "gram.y" { yyval.tag.log_output = false; } break; case 77: -#line 643 "gram.y" +#line 646 "gram.y" { yyval.tag.follow = true; } break; case 78: -#line 646 "gram.y" +#line 649 "gram.y" { yyval.tag.follow = false; } break; case 79: -#line 649 "gram.y" +#line 652 "gram.y" { yyval.tag.send_mail = true; } break; case 80: -#line 652 "gram.y" +#line 655 "gram.y" { yyval.tag.send_mail = false; } break; case 81: -#line 657 "gram.y" +#line 660 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -1884,7 +1887,7 @@ case 81: } break; case 82: -#line 664 "gram.y" +#line 667 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -1894,7 +1897,7 @@ case 82: } break; case 83: -#line 671 "gram.y" +#line 674 "gram.y" { struct sudo_command *c = calloc(1, sizeof(*c)); if (c == NULL) { @@ -1912,82 +1915,86 @@ case 83: } break; case 86: -#line 692 "gram.y" +#line 695 "gram.y" { const char *s; - if ((s = alias_add(yyvsp[-2].string, HOSTALIAS, yyvsp[0].member)) != NULL) { + s = alias_add(yyvsp[-2].string, HOSTALIAS, sudoers, this_lineno, yyvsp[0].member); + if (s != NULL) { sudoerserror(s); YYERROR; } } break; case 88: -#line 702 "gram.y" +#line 706 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; case 91: -#line 712 "gram.y" +#line 716 "gram.y" { const char *s; - if ((s = alias_add(yyvsp[-2].string, CMNDALIAS, yyvsp[0].member)) != NULL) { + s = alias_add(yyvsp[-2].string, CMNDALIAS, sudoers, this_lineno, yyvsp[0].member); + if (s != NULL) { sudoerserror(s); YYERROR; } } break; case 93: -#line 722 "gram.y" +#line 727 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; case 96: -#line 732 "gram.y" +#line 737 "gram.y" { const char *s; - if ((s = alias_add(yyvsp[-2].string, RUNASALIAS, yyvsp[0].member)) != NULL) { + s = alias_add(yyvsp[-2].string, RUNASALIAS, sudoers, this_lineno, yyvsp[0].member); + if (s != NULL) { sudoerserror(s); YYERROR; } } break; case 99: -#line 745 "gram.y" +#line 751 "gram.y" { const char *s; - if ((s = alias_add(yyvsp[-2].string, USERALIAS, yyvsp[0].member)) != NULL) { + s = alias_add(yyvsp[-2].string, USERALIAS, sudoers, this_lineno, yyvsp[0].member); + if (s != NULL) { sudoerserror(s); YYERROR; } } break; case 101: -#line 755 "gram.y" +#line 762 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; case 102: -#line 761 "gram.y" +#line 768 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = false; } break; case 103: -#line 765 "gram.y" +#line 772 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = true; } break; case 104: -#line 771 "gram.y" +#line 778 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -1997,7 +2004,7 @@ case 104: } break; case 105: -#line 778 "gram.y" +#line 785 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -2007,7 +2014,7 @@ case 105: } break; case 106: -#line 785 "gram.y" +#line 792 "gram.y" { yyval.member = new_member(yyvsp[0].string, NETGROUP); if (yyval.member == NULL) { @@ -2017,7 +2024,7 @@ case 106: } break; case 107: -#line 792 "gram.y" +#line 799 "gram.y" { yyval.member = new_member(yyvsp[0].string, USERGROUP); if (yyval.member == NULL) { @@ -2027,7 +2034,7 @@ case 107: } break; case 108: -#line 799 "gram.y" +#line 806 "gram.y" { yyval.member = new_member(yyvsp[0].string, WORD); if (yyval.member == NULL) { @@ -2037,28 +2044,28 @@ case 108: } break; case 110: -#line 809 "gram.y" +#line 816 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; case 111: -#line 815 "gram.y" +#line 822 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = false; } break; case 112: -#line 819 "gram.y" +#line 826 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = true; } break; case 113: -#line 825 "gram.y" +#line 832 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -2068,7 +2075,7 @@ case 113: } break; case 114: -#line 832 "gram.y" +#line 839 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -2078,7 +2085,7 @@ case 114: } break; case 115: -#line 839 "gram.y" +#line 846 "gram.y" { yyval.member = new_member(yyvsp[0].string, WORD); if (yyval.member == NULL) { @@ -2087,7 +2094,7 @@ case 115: } } break; -#line 2038 "gram.c" +#line 2045 "gram.c" } yyssp -= yym; yystate = *yyssp; diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y index c58aeeb09..2f68a4254 100644 --- a/plugins/sudoers/gram.y +++ b/plugins/sudoers/gram.y @@ -49,6 +49,9 @@ #include "parse.h" #include "toke.h" +/* If we last saw a newline the entry is on the preceding line. */ +#define this_lineno (last_token == COMMENT ? sudolineno - 1 : sudolineno) + /* * Globals */ @@ -691,7 +694,8 @@ hostaliases : hostalias hostalias : ALIAS '=' hostlist { const char *s; - if ((s = alias_add($1, HOSTALIAS, $3)) != NULL) { + s = alias_add($1, HOSTALIAS, sudoers, this_lineno, $3); + if (s != NULL) { sudoerserror(s); YYERROR; } @@ -711,7 +715,8 @@ cmndaliases : cmndalias cmndalias : ALIAS '=' cmndlist { const char *s; - if ((s = alias_add($1, CMNDALIAS, $3)) != NULL) { + s = alias_add($1, CMNDALIAS, sudoers, this_lineno, $3); + if (s != NULL) { sudoerserror(s); YYERROR; } @@ -731,7 +736,8 @@ runasaliases : runasalias runasalias : ALIAS '=' userlist { const char *s; - if ((s = alias_add($1, RUNASALIAS, $3)) != NULL) { + s = alias_add($1, RUNASALIAS, sudoers, this_lineno, $3); + if (s != NULL) { sudoerserror(s); YYERROR; } @@ -744,7 +750,8 @@ useraliases : useralias useralias : ALIAS '=' userlist { const char *s; - if ((s = alias_add($1, USERALIAS, $3)) != NULL) { + s = alias_add($1, USERALIAS, sudoers, this_lineno, $3); + if (s != NULL) { sudoerserror(s); YYERROR; } @@ -851,13 +858,9 @@ sudoerserror(const char *s) { debug_decl(sudoerserror, SUDOERS_DEBUG_PARSER) - /* If we last saw a newline the error is on the preceding line. */ - if (last_token == COMMENT) - sudolineno--; - /* Save the line the first error occurred on. */ if (errorlineno == -1) { - errorlineno = sudolineno; + errorlineno = this_lineno; rcstr_delref(errorfile); errorfile = rcstr_addref(sudoers); } @@ -870,7 +873,7 @@ sudoerserror(const char *s) /* Warnings are displayed in the user's locale. */ sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale); - sudo_printf(SUDO_CONV_ERROR_MSG, _(fmt), sudoers, _(s), sudolineno); + sudo_printf(SUDO_CONV_ERROR_MSG, _(fmt), sudoers, _(s), this_lineno); sudoers_setlocale(oldlocale, NULL); } #endif @@ -896,7 +899,7 @@ new_default(char *var, char *val, short op) /* d->type = 0; */ d->op = op; /* d->binding = NULL */ - d->lineno = last_token == COMMENT ? sudolineno - 1 : sudolineno; + d->lineno = this_lineno; d->file = rcstr_addref(sudoers); HLTQ_INIT(d, entries); @@ -1003,6 +1006,8 @@ add_userspec(struct member *members, struct privilege *privs) "unable to allocate memory"); debug_return_bool(false); } + u->lineno = this_lineno; + u->file = rcstr_addref(sudoers); HLTQ_TO_TAILQ(&u->users, members, entries); HLTQ_TO_TAILQ(&u->privileges, privs, entries); TAILQ_INSERT_TAIL(&userspecs, u, entries); @@ -1043,6 +1048,7 @@ init_parser(const char *path, bool quiet) bool ret = true; debug_decl(init_parser, SUDOERS_DEBUG_PARSER) + /* XXX - move into a free function */ TAILQ_FOREACH_SAFE(us, &userspecs, entries, us_next) { struct member *m, *m_next; struct privilege *priv, *priv_next; @@ -1117,6 +1123,7 @@ init_parser(const char *path, bool quiet) } free(priv); } + rcstr_delref(us->file); free(us); } TAILQ_INIT(&userspecs); diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index 684dfeedf..64881fd4d 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -159,6 +159,8 @@ struct userspec { TAILQ_ENTRY(userspec) entries; struct member_list users; /* list of users */ struct privilege_list privileges; /* list of privileges */ + int lineno; + char *file; }; /* @@ -209,7 +211,9 @@ struct runascontainer { struct alias { char *name; /* alias name */ unsigned short type; /* {USER,HOST,RUNAS,CMND}ALIAS */ - bool used; /* "used" flag for cycle detection */ + short used; /* "used" flag for cycle detection */ + int lineno; /* line number of alias entry */ + char *file; /* file the alias entry was in */ struct member_list members; /* list of alias members */ }; @@ -236,7 +240,7 @@ extern struct defaults_list defaults; /* alias.c */ bool no_aliases(void); -const char *alias_add(char *name, int type, struct member *members); +const char *alias_add(char *name, int type, char *file, int lineno, struct member *members); int alias_compare(const void *a1, const void *a2); struct alias *alias_get(char *name, int type); struct alias *alias_remove(char *name, int type); diff --git a/plugins/sudoers/regress/visudo/test2.err.ok b/plugins/sudoers/regress/visudo/test2.err.ok index e036dd048..e2b191ced 100644 --- a/plugins/sudoers/regress/visudo/test2.err.ok +++ b/plugins/sudoers/regress/visudo/test2.err.ok @@ -1 +1 @@ -visudo: Error: cycle in User_Alias "FOO" +visudo: stdin:1 cycle in User_Alias "FOO" diff --git a/plugins/sudoers/regress/visudo/test3.err.ok b/plugins/sudoers/regress/visudo/test3.err.ok index 4209fa242..0e5a24ad5 100644 --- a/plugins/sudoers/regress/visudo/test3.err.ok +++ b/plugins/sudoers/regress/visudo/test3.err.ok @@ -1,2 +1,2 @@ -visudo: Warning: unused User_Alias "A" -visudo: Warning: unused User_Alias "B" +visudo: stdin:1 unused User_Alias "A" +visudo: stdin:2 unused User_Alias "B" diff --git a/plugins/sudoers/visudo.c b/plugins/sudoers/visudo.c index c7bf710fd..9f1c4e91d 100644 --- a/plugins/sudoers/visudo.c +++ b/plugins/sudoers/visudo.c @@ -89,7 +89,7 @@ TAILQ_HEAD(sudoersfile_list, sudoersfile); static void quit(int); static void get_hostname(void); static int whatnow(void); -static int check_aliases(bool, bool); +static int check_aliases(bool strict, bool quiet); static char *get_editor(int *editor_argc, char ***editor_argv); static bool check_syntax(const char *, bool, bool, bool); static bool edit_sudoers(struct sudoersfile *, char *, int, char **, int); @@ -1114,7 +1114,7 @@ alias_type_to_string(int alias_type) } static int -check_alias(char *name, int type, int strict, int quiet) +check_alias(char *name, int type, char *file, int lineno, bool quiet) { struct member *m; struct alias *a; @@ -1124,22 +1124,19 @@ check_alias(char *name, int type, int strict, int quiet) if ((a = alias_get(name, type)) != NULL) { /* check alias contents */ TAILQ_FOREACH(m, &a->members, entries) { - if (m->type == ALIAS) - errors += check_alias(m->name, type, strict, quiet); + if (m->type != ALIAS) + continue; + errors += check_alias(m->name, type, a->file, a->lineno, quiet); } alias_put(a); } else { if (!quiet) { if (errno == ELOOP) { - sudo_warnx(strict ? - U_("Error: cycle in %s \"%s\"") : - U_("Warning: cycle in %s \"%s\""), - alias_type_to_string(type), name); + sudo_warnx(U_("%s:%d cycle in %s \"%s\""), + file, lineno, alias_type_to_string(type), name); } else { - sudo_warnx(strict ? - U_("Error: %s \"%s\" referenced but not defined") : - U_("Warning: %s \"%s\" referenced but not defined"), - alias_type_to_string(type), name); + sudo_warnx(U_("%s:%d %s \"%s\" referenced but not defined"), + file, lineno, alias_type_to_string(type), name); } } errors++; @@ -1173,32 +1170,37 @@ check_aliases(bool strict, bool quiet) TAILQ_FOREACH(us, &userspecs, entries) { TAILQ_FOREACH(m, &us->users, entries) { if (m->type == ALIAS) { - errors += check_alias(m->name, USERALIAS, strict, quiet); + errors += check_alias(m->name, USERALIAS, + us->file, us->lineno, quiet); } } TAILQ_FOREACH(priv, &us->privileges, entries) { TAILQ_FOREACH(m, &priv->hostlist, entries) { if (m->type == ALIAS) { - errors += check_alias(m->name, HOSTALIAS, strict, quiet); + errors += check_alias(m->name, HOSTALIAS, + us->file, us->lineno, quiet); } } TAILQ_FOREACH(cs, &priv->cmndlist, entries) { if (cs->runasuserlist != NULL) { TAILQ_FOREACH(m, cs->runasuserlist, entries) { if (m->type == ALIAS) { - errors += check_alias(m->name, RUNASALIAS, strict, quiet); + errors += check_alias(m->name, RUNASALIAS, + us->file, us->lineno, quiet); } } } if (cs->runasgrouplist != NULL) { TAILQ_FOREACH(m, cs->runasgrouplist, entries) { if (m->type == ALIAS) { - errors += check_alias(m->name, RUNASALIAS, strict, quiet); + errors += check_alias(m->name, RUNASALIAS, + us->file, us->lineno, quiet); } } } if ((m = cs->cmnd)->type == ALIAS) { - errors += check_alias(m->name, CMNDALIAS, strict, quiet); + errors += check_alias(m->name, CMNDALIAS, + us->file, us->lineno, quiet); } } } @@ -1281,8 +1283,8 @@ print_unused(void *v1, void *v2) { struct alias *a = (struct alias *)v1; - sudo_warnx_nodebug(U_("Warning: unused %s \"%s\""), - alias_type_to_string(a->type), a->name); + sudo_warnx_nodebug(U_("%s:%d unused %s \"%s\""), + a->file, a->lineno, alias_type_to_string(a->type), a->name); return 0; } -- 2.40.0