From 003f9550f175f0dff7ca437ee59b5c5bc1f4b36c Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Wed, 24 Nov 2021 06:52:51 -0700 Subject: [PATCH] cvtsudoers: add -l option to log merge actions The "-l logfile" option can be used to store a log of what actions cvtsudoers took when merging multiple files. For example, which aliases were renamed, which entries were overriden or removed as duplicated. --- docs/cvtsudoers.man.in | 11 ++++++++++- docs/cvtsudoers.mdoc.in | 10 +++++++++- plugins/sudoers/cvtsudoers.c | 29 ++++++++++++++++++++++++++++- plugins/sudoers/cvtsudoers.h | 2 ++ plugins/sudoers/cvtsudoers_json.c | 9 ++++++--- plugins/sudoers/cvtsudoers_merge.c | 17 +++++++++++++++-- 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/docs/cvtsudoers.man.in b/docs/cvtsudoers.man.in index eadde0c80..8701a5d9a 100644 --- a/docs/cvtsudoers.man.in +++ b/docs/cvtsudoers.man.in @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.TH "CVTSUDOERS" "1" "November 18, 2021" "Sudo @PACKAGE_VERSION@" "General Commands Manual" +.TH "CVTSUDOERS" "1" "November 23, 2021" "Sudo @PACKAGE_VERSION@" "General Commands Manual" .nh .if n .ad l .SH "NAME" @@ -32,6 +32,7 @@ [\fB\-f\fR\ \fIoutput_format\fR] [\fB\-i\fR\ \fIinput_format\fR] [\fB\-I\fR\ \fIincrement\fR] +[\fB\-l\fR\ \fIlog_file\fR] [\fB\-m\fR\ \fIfilter\fR] [\fB\-o\fR\ \fIoutput_file\fR] [\fB\-O\fR\ \fIstart_point\fR] @@ -233,6 +234,14 @@ When generating LDIF output, increment each sudoOrder attribute by the specified number. Defaults to an increment of 1. .TP 12n +\fB\-l\fR \fIlog_file\fR, \fB\--logfile\fR=\fIlog_file\fR +Log conversion warnings to +\fIlog_file\fR +instead of to the standard error. +This is particularly useful when merging multiple +\fIsudoers\fR +files, which can generate a large number of warnings. +.TP 12n \fB\-m\fR \fIfilter\fR, \fB\--match\fR=\fIfilter\fR Only output rules that match the specified \fIfilter\fR. diff --git a/docs/cvtsudoers.mdoc.in b/docs/cvtsudoers.mdoc.in index 075854472..003dd268f 100644 --- a/docs/cvtsudoers.mdoc.in +++ b/docs/cvtsudoers.mdoc.in @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd November 18, 2021 +.Dd November 23, 2021 .Dt CVTSUDOERS 1 .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -30,6 +30,7 @@ .Op Fl f Ar output_format .Op Fl i Ar input_format .Op Fl I Ar increment +.Op Fl l Ar log_file .Op Fl m Ar filter .Op Fl o Ar output_file .Op Fl O Ar start_point @@ -187,6 +188,13 @@ This is the default input format. When generating LDIF output, increment each sudoOrder attribute by the specified number. Defaults to an increment of 1. +.It Fl l Ar log_file , Fl -logfile Ns = Ns Ar log_file +Log conversion warnings to +.Ar log_file +instead of to the standard error. +This is particularly useful when merging multiple +.Em sudoers +files, which can generate a large number of warnings. .It Fl m Ar filter , Fl -match Ns = Ns Ar filter Only output rules that match the specified .Ar filter . diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c index d8860b518..e01f2410c 100644 --- a/plugins/sudoers/cvtsudoers.c +++ b/plugins/sudoers/cvtsudoers.c @@ -61,7 +61,8 @@ struct cvtsudoers_filter *filters; struct sudo_user sudo_user; struct passwd *list_pw; -static const char short_opts[] = "b:c:d:ef:hi:I:m:Mo:O:pP:s:V"; +static FILE *logfp; +static const char short_opts[] = "b:c:d:ef:hi:I:l:m:Mo:O:pP:s:V"; static struct option long_opts[] = { { "base", required_argument, NULL, 'b' }, { "config", required_argument, NULL, 'c' }, @@ -71,6 +72,7 @@ static struct option long_opts[] = { { "help", no_argument, NULL, 'h' }, { "input-format", required_argument, NULL, 'i' }, { "increment", required_argument, NULL, 'I' }, + { "logfile", required_argument, NULL, 'l' }, { "match", required_argument, NULL, 'm' }, { "match-local", no_argument, NULL, 'M' }, { "prune-matches", no_argument, NULL, 'p' }, @@ -208,6 +210,9 @@ main(int argc, char *argv[]) usage(1); } break; + case 'l': + conf->logfile = optarg; + break; case 'm': conf->filter = optarg; break; @@ -257,6 +262,12 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; + if (conf->logfile != NULL) { + logfp = fopen(conf->logfile, "w"); + if (logfp == NULL) + sudo_fatalx(U_("unable to open log file %s"), conf->logfile); + } + if (conf->input_format != NULL) { if (strcasecmp(conf->input_format, "ldif") == 0) { input_format = format_ldif; @@ -450,6 +461,21 @@ done: return exitcode; } +void +log_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (logfp != NULL) { + vfprintf(logfp, fmt, ap); + fputc('\n', logfp); + } else { + sudo_vwarnx_nodebug(fmt, ap); + } + va_end(ap); +} + /* * cvtsudoers configuration data. */ @@ -462,6 +488,7 @@ static struct cvtsudoers_conf_table cvtsudoers_conf_vars[] = { { "input_format", CONF_STR, &cvtsudoers_config.input_format }, { "output_format", CONF_STR, &cvtsudoers_config.output_format }, { "match", CONF_STR, &cvtsudoers_config.filter }, + { "logfile", CONF_STR, &cvtsudoers_config.logfile }, { "defaults", CONF_STR, &cvtsudoers_config.defstr }, { "suppress", CONF_STR, &cvtsudoers_config.supstr }, { "expand_aliases", CONF_BOOL, &cvtsudoers_config.expand_aliases }, diff --git a/plugins/sudoers/cvtsudoers.h b/plugins/sudoers/cvtsudoers.h index e759c21c7..726f6dc8b 100644 --- a/plugins/sudoers/cvtsudoers.h +++ b/plugins/sudoers/cvtsudoers.h @@ -57,6 +57,7 @@ struct cvtsudoers_config { char *input_format; char *output_format; char *filter; + char *logfile; char *defstr; char *supstr; }; @@ -83,6 +84,7 @@ struct cvtsudoers_filter { /* cvtsudoers.c */ extern struct cvtsudoers_filter *filters; +void log_warnx(const char *fmt, ...) __printflike(1, 2); /* cvtsudoers_csv.c */ bool convert_sudoers_csv(struct sudoers_parse_tree *parse_tree, const char *output_file, struct cvtsudoers_config *conf); diff --git a/plugins/sudoers/cvtsudoers_json.c b/plugins/sudoers/cvtsudoers_json.c index dfd143d2e..10386617e 100644 --- a/plugins/sudoers/cvtsudoers_json.c +++ b/plugins/sudoers/cvtsudoers_json.c @@ -477,7 +477,8 @@ print_defaults_json(struct json_container *jsonc, TAILQ_FOREACH_SAFE(def, &parse_tree->defaults, entries, next) { type = get_defaults_type(def); if (type == -1) { - sudo_warnx(U_("unknown defaults entry \"%s\""), def->var); + log_warnx(U_("%s:%d:%d: unknown defaults entry \"%s\""), + def->file, def->line, def->column, def->var); /* XXX - just pass it through as a string anyway? */ continue; } @@ -511,7 +512,8 @@ print_defaults_json(struct json_container *jsonc, def = next; type = get_defaults_type(def); if (type == -1) { - sudo_warnx(U_("unknown defaults entry \"%s\""), def->var); + log_warnx(U_("%s:%d:%d: unknown defaults entry \"%s\""), + def->file, def->line, def->column, def->var); /* XXX - just pass it through as a string anyway? */ break; } @@ -720,7 +722,8 @@ print_cmndspec_json(struct json_container *jsonc, TAILQ_FOREACH(def, options, entries) { int type = get_defaults_type(def); if (type == -1) { - sudo_warnx(U_("unknown defaults entry \"%s\""), def->var); + log_warnx(U_("%s:%d:%d: unknown defaults entry \"%s\""), + def->file, def->line, def->column, def->var); /* XXX - just pass it through as a string anyway? */ continue; } diff --git a/plugins/sudoers/cvtsudoers_merge.c b/plugins/sudoers/cvtsudoers_merge.c index 82cfd2033..b2bbc9ea7 100644 --- a/plugins/sudoers/cvtsudoers_merge.c +++ b/plugins/sudoers/cvtsudoers_merge.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -356,6 +357,8 @@ alias_rename(const char *old_name, const char *new_name, int alias_type, sudo_warnx(U_("unable to find alias %s"), old_name); debug_return_bool(false); } + log_warnx(U_("%s:%d:%d: renaming alias %s to %s"), + a->file, a->line, a->column, a->name, new_name); free(a->name); a->name = strdup(new_name); if (a->name == NULL) @@ -411,6 +414,8 @@ alias_resolve_conflicts(struct sudoers_parse_tree *parse_tree0, struct alias *a, sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "removing duplicate alias %s from %p", a->name, parse_tree); b = alias_remove(parse_tree, a->name, a->type); + log_warnx(U_("%s:%d:%d: removing duplicate alias %s"), + b->file, b->line, b->column, b->name); alias_free(b); continue; } @@ -569,7 +574,7 @@ defaults_has_conflict(struct defaults *def, TAILQ_FOREACH(d, &parse_tree->defaults, entries) { if (defaults_var_matches(def, d)) { if (!defaults_val_matches(def, d)) { - sudo_warnx(U_("%s:%d:%d: conflicting Defaults entry \"%s\" host-specific in %s:%d:%d"), + log_warnx(U_("%s:%d:%d: conflicting Defaults entry \"%s\" host-specific in %s:%d:%d"), def->file, def->line, def->column, def->var, d->file, d->line, d->column); } @@ -609,6 +614,9 @@ merge_defaults(struct sudoers_parse_tree_list *parse_trees, sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } + log_warnx(U_("%s:%d:%d: made Defaults \"%s\" specific to host %s"), + def->file, def->line, def->column, def->var, + parse_tree->lhost); m->name = strdup(parse_tree->lhost); if (m->name == NULL) { sudo_fatalx(U_("%s: %s"), __func__, @@ -626,10 +634,13 @@ merge_defaults(struct sudoers_parse_tree_list *parse_trees, * Only add Defaults entry if not overridden by subsequent sudoers. */ if (defaults_has_conflict(def, parse_tree)) { + log_warnx(U_("%s:%d:%d: removing Defaults \"%s\" overridden by subsequent entries"), + def->file, def->line, def->column, def->var); free_default(def); } else { if (def->type != DEFAULTS_HOST) { - sudo_warnx(U_("%s:%d:%d: unable to make Defaults \"%s\" host-specific"), def->file, def->line, def->column, def->var); + log_warnx(U_("%s:%d:%d: unable to make Defaults \"%s\" host-specific"), + def->file, def->line, def->column, def->var); } TAILQ_INSERT_TAIL(&merged_tree->defaults, def, entries); } @@ -822,6 +833,8 @@ merge_userspecs(struct sudoers_parse_tree_list *parse_trees, * XXX - do this at the privilege/cmndspec level instead. */ if (userspec_is_duplicate(us, parse_tree)) { + log_warnx(U_("%s:%d:%d: removing userspec overridden by subsequent entries"), + us->file, us->line, us->column); free_userspec(us); } else { TAILQ_INSERT_TAIL(&merged_tree->userspecs, us, entries);