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.
This commit is contained in:
Todd C. Miller
2021-11-24 06:52:51 -07:00
parent ce9c6d17c5
commit 003f9550f1
6 changed files with 70 additions and 8 deletions

View File

@@ -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.

View File

@@ -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 .

View File

@@ -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 },

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -25,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
@@ -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);