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:
@@ -16,7 +16,7 @@
|
|||||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
.\" 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
|
.nh
|
||||||
.if n .ad l
|
.if n .ad l
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
[\fB\-f\fR\ \fIoutput_format\fR]
|
[\fB\-f\fR\ \fIoutput_format\fR]
|
||||||
[\fB\-i\fR\ \fIinput_format\fR]
|
[\fB\-i\fR\ \fIinput_format\fR]
|
||||||
[\fB\-I\fR\ \fIincrement\fR]
|
[\fB\-I\fR\ \fIincrement\fR]
|
||||||
|
[\fB\-l\fR\ \fIlog_file\fR]
|
||||||
[\fB\-m\fR\ \fIfilter\fR]
|
[\fB\-m\fR\ \fIfilter\fR]
|
||||||
[\fB\-o\fR\ \fIoutput_file\fR]
|
[\fB\-o\fR\ \fIoutput_file\fR]
|
||||||
[\fB\-O\fR\ \fIstart_point\fR]
|
[\fB\-O\fR\ \fIstart_point\fR]
|
||||||
@@ -233,6 +234,14 @@ When generating LDIF output, increment each sudoOrder attribute by
|
|||||||
the specified number.
|
the specified number.
|
||||||
Defaults to an increment of 1.
|
Defaults to an increment of 1.
|
||||||
.TP 12n
|
.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
|
\fB\-m\fR \fIfilter\fR, \fB\--match\fR=\fIfilter\fR
|
||||||
Only output rules that match the specified
|
Only output rules that match the specified
|
||||||
\fIfilter\fR.
|
\fIfilter\fR.
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
.\"
|
.\"
|
||||||
.Dd November 18, 2021
|
.Dd November 23, 2021
|
||||||
.Dt CVTSUDOERS 1
|
.Dt CVTSUDOERS 1
|
||||||
.Os Sudo @PACKAGE_VERSION@
|
.Os Sudo @PACKAGE_VERSION@
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
.Op Fl f Ar output_format
|
.Op Fl f Ar output_format
|
||||||
.Op Fl i Ar input_format
|
.Op Fl i Ar input_format
|
||||||
.Op Fl I Ar increment
|
.Op Fl I Ar increment
|
||||||
|
.Op Fl l Ar log_file
|
||||||
.Op Fl m Ar filter
|
.Op Fl m Ar filter
|
||||||
.Op Fl o Ar output_file
|
.Op Fl o Ar output_file
|
||||||
.Op Fl O Ar start_point
|
.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
|
When generating LDIF output, increment each sudoOrder attribute by
|
||||||
the specified number.
|
the specified number.
|
||||||
Defaults to an increment of 1.
|
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
|
.It Fl m Ar filter , Fl -match Ns = Ns Ar filter
|
||||||
Only output rules that match the specified
|
Only output rules that match the specified
|
||||||
.Ar filter .
|
.Ar filter .
|
||||||
|
@@ -61,7 +61,8 @@
|
|||||||
struct cvtsudoers_filter *filters;
|
struct cvtsudoers_filter *filters;
|
||||||
struct sudo_user sudo_user;
|
struct sudo_user sudo_user;
|
||||||
struct passwd *list_pw;
|
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[] = {
|
static struct option long_opts[] = {
|
||||||
{ "base", required_argument, NULL, 'b' },
|
{ "base", required_argument, NULL, 'b' },
|
||||||
{ "config", required_argument, NULL, 'c' },
|
{ "config", required_argument, NULL, 'c' },
|
||||||
@@ -71,6 +72,7 @@ static struct option long_opts[] = {
|
|||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "input-format", required_argument, NULL, 'i' },
|
{ "input-format", required_argument, NULL, 'i' },
|
||||||
{ "increment", required_argument, NULL, 'I' },
|
{ "increment", required_argument, NULL, 'I' },
|
||||||
|
{ "logfile", required_argument, NULL, 'l' },
|
||||||
{ "match", required_argument, NULL, 'm' },
|
{ "match", required_argument, NULL, 'm' },
|
||||||
{ "match-local", no_argument, NULL, 'M' },
|
{ "match-local", no_argument, NULL, 'M' },
|
||||||
{ "prune-matches", no_argument, NULL, 'p' },
|
{ "prune-matches", no_argument, NULL, 'p' },
|
||||||
@@ -208,6 +210,9 @@ main(int argc, char *argv[])
|
|||||||
usage(1);
|
usage(1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
conf->logfile = optarg;
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
conf->filter = optarg;
|
conf->filter = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -257,6 +262,12 @@ main(int argc, char *argv[])
|
|||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += 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 (conf->input_format != NULL) {
|
||||||
if (strcasecmp(conf->input_format, "ldif") == 0) {
|
if (strcasecmp(conf->input_format, "ldif") == 0) {
|
||||||
input_format = format_ldif;
|
input_format = format_ldif;
|
||||||
@@ -450,6 +461,21 @@ done:
|
|||||||
return exitcode;
|
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.
|
* cvtsudoers configuration data.
|
||||||
*/
|
*/
|
||||||
@@ -462,6 +488,7 @@ static struct cvtsudoers_conf_table cvtsudoers_conf_vars[] = {
|
|||||||
{ "input_format", CONF_STR, &cvtsudoers_config.input_format },
|
{ "input_format", CONF_STR, &cvtsudoers_config.input_format },
|
||||||
{ "output_format", CONF_STR, &cvtsudoers_config.output_format },
|
{ "output_format", CONF_STR, &cvtsudoers_config.output_format },
|
||||||
{ "match", CONF_STR, &cvtsudoers_config.filter },
|
{ "match", CONF_STR, &cvtsudoers_config.filter },
|
||||||
|
{ "logfile", CONF_STR, &cvtsudoers_config.logfile },
|
||||||
{ "defaults", CONF_STR, &cvtsudoers_config.defstr },
|
{ "defaults", CONF_STR, &cvtsudoers_config.defstr },
|
||||||
{ "suppress", CONF_STR, &cvtsudoers_config.supstr },
|
{ "suppress", CONF_STR, &cvtsudoers_config.supstr },
|
||||||
{ "expand_aliases", CONF_BOOL, &cvtsudoers_config.expand_aliases },
|
{ "expand_aliases", CONF_BOOL, &cvtsudoers_config.expand_aliases },
|
||||||
|
@@ -57,6 +57,7 @@ struct cvtsudoers_config {
|
|||||||
char *input_format;
|
char *input_format;
|
||||||
char *output_format;
|
char *output_format;
|
||||||
char *filter;
|
char *filter;
|
||||||
|
char *logfile;
|
||||||
char *defstr;
|
char *defstr;
|
||||||
char *supstr;
|
char *supstr;
|
||||||
};
|
};
|
||||||
@@ -83,6 +84,7 @@ struct cvtsudoers_filter {
|
|||||||
|
|
||||||
/* cvtsudoers.c */
|
/* cvtsudoers.c */
|
||||||
extern struct cvtsudoers_filter *filters;
|
extern struct cvtsudoers_filter *filters;
|
||||||
|
void log_warnx(const char *fmt, ...) __printflike(1, 2);
|
||||||
|
|
||||||
/* cvtsudoers_csv.c */
|
/* cvtsudoers_csv.c */
|
||||||
bool convert_sudoers_csv(struct sudoers_parse_tree *parse_tree, const char *output_file, struct cvtsudoers_config *conf);
|
bool convert_sudoers_csv(struct sudoers_parse_tree *parse_tree, const char *output_file, struct cvtsudoers_config *conf);
|
||||||
|
@@ -477,7 +477,8 @@ print_defaults_json(struct json_container *jsonc,
|
|||||||
TAILQ_FOREACH_SAFE(def, &parse_tree->defaults, entries, next) {
|
TAILQ_FOREACH_SAFE(def, &parse_tree->defaults, entries, next) {
|
||||||
type = get_defaults_type(def);
|
type = get_defaults_type(def);
|
||||||
if (type == -1) {
|
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? */
|
/* XXX - just pass it through as a string anyway? */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -511,7 +512,8 @@ print_defaults_json(struct json_container *jsonc,
|
|||||||
def = next;
|
def = next;
|
||||||
type = get_defaults_type(def);
|
type = get_defaults_type(def);
|
||||||
if (type == -1) {
|
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? */
|
/* XXX - just pass it through as a string anyway? */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -720,7 +722,8 @@ print_cmndspec_json(struct json_container *jsonc,
|
|||||||
TAILQ_FOREACH(def, options, entries) {
|
TAILQ_FOREACH(def, options, entries) {
|
||||||
int type = get_defaults_type(def);
|
int type = get_defaults_type(def);
|
||||||
if (type == -1) {
|
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? */
|
/* XXX - just pass it through as a string anyway? */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ctype.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);
|
sudo_warnx(U_("unable to find alias %s"), old_name);
|
||||||
debug_return_bool(false);
|
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);
|
free(a->name);
|
||||||
a->name = strdup(new_name);
|
a->name = strdup(new_name);
|
||||||
if (a->name == NULL)
|
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,
|
sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
|
||||||
"removing duplicate alias %s from %p", a->name, parse_tree);
|
"removing duplicate alias %s from %p", a->name, parse_tree);
|
||||||
b = alias_remove(parse_tree, a->name, a->type);
|
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);
|
alias_free(b);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -569,7 +574,7 @@ defaults_has_conflict(struct defaults *def,
|
|||||||
TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
|
TAILQ_FOREACH(d, &parse_tree->defaults, entries) {
|
||||||
if (defaults_var_matches(def, d)) {
|
if (defaults_var_matches(def, d)) {
|
||||||
if (!defaults_val_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,
|
def->file, def->line, def->column, def->var,
|
||||||
d->file, d->line, d->column);
|
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__,
|
sudo_fatalx(U_("%s: %s"), __func__,
|
||||||
U_("unable to allocate memory"));
|
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);
|
m->name = strdup(parse_tree->lhost);
|
||||||
if (m->name == NULL) {
|
if (m->name == NULL) {
|
||||||
sudo_fatalx(U_("%s: %s"), __func__,
|
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.
|
* Only add Defaults entry if not overridden by subsequent sudoers.
|
||||||
*/
|
*/
|
||||||
if (defaults_has_conflict(def, parse_tree)) {
|
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);
|
free_default(def);
|
||||||
} else {
|
} else {
|
||||||
if (def->type != DEFAULTS_HOST) {
|
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);
|
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.
|
* XXX - do this at the privilege/cmndspec level instead.
|
||||||
*/
|
*/
|
||||||
if (userspec_is_duplicate(us, parse_tree)) {
|
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);
|
free_userspec(us);
|
||||||
} else {
|
} else {
|
||||||
TAILQ_INSERT_TAIL(&merged_tree->userspecs, us, entries);
|
TAILQ_INSERT_TAIL(&merged_tree->userspecs, us, entries);
|
||||||
|
Reference in New Issue
Block a user