From 5999cfb906762f386cb500ea196a1a7db6029081 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Sat, 24 Feb 2018 09:23:14 -0700 Subject: [PATCH] Add support for setting default options in a config file. In addition to expand_aliases, input_format and output_format, both the initial sudoOrder and the increment when updating sudoOrder for subsequent sudoRole objects can be specified. Command line options have also been added for the start order and increment. --- MANIFEST | 1 + doc/cvtsudoers.cat | 59 +++++- doc/cvtsudoers.man.in | 90 +++++++-- doc/cvtsudoers.mdoc.in | 83 ++++++-- pathnames.h.in | 7 + plugins/sudoers/Makefile.in | 32 ++-- plugins/sudoers/cvtsudoers.c | 305 ++++++++++++++++++++++++------ plugins/sudoers/cvtsudoers.h | 56 ++++++ plugins/sudoers/cvtsudoers_json.c | 11 +- plugins/sudoers/cvtsudoers_ldif.c | 31 +-- 10 files changed, 551 insertions(+), 124 deletions(-) create mode 100644 plugins/sudoers/cvtsudoers.h diff --git a/MANIFEST b/MANIFEST index ac7d0d66b..2656f8ff6 100644 --- a/MANIFEST +++ b/MANIFEST @@ -265,6 +265,7 @@ plugins/sudoers/bsm_audit.h plugins/sudoers/check.c plugins/sudoers/check.h plugins/sudoers/cvtsudoers.c +plugins/sudoers/cvtsudoers.h plugins/sudoers/cvtsudoers_json.c plugins/sudoers/cvtsudoers_ldif.c plugins/sudoers/def_data.c diff --git a/doc/cvtsudoers.cat b/doc/cvtsudoers.cat index 206164d5d..2777a43ca 100644 --- a/doc/cvtsudoers.cat +++ b/doc/cvtsudoers.cat @@ -4,8 +4,9 @@ NNAAMMEE ccvvttssuuddooeerrss - convert between sudoers file formats SSYYNNOOPPSSIISS - ccvvttssuuddooeerrss [--eehhVV] [--bb _d_n] [--ii _i_n_p_u_t___f_o_r_m_a_t] [--ff _o_u_t_p_u_t___f_o_r_m_a_t] - [--oo _o_u_t_p_u_t___f_i_l_e] [_i_n_p_u_t___f_i_l_e] + ccvvttssuuddooeerrss [--eehhVV] [--bb _d_n] [--cc _c_o_n_f___f_i_l_e] [--II _i_n_c_r_e_m_e_n_t] [--ii _i_n_p_u_t___f_o_r_m_a_t] + [--ff _o_u_t_p_u_t___f_o_r_m_a_t] [--OO _s_t_a_r_t___p_o_i_n_t] [--oo _o_u_t_p_u_t___f_i_l_e] + [_i_n_p_u_t___f_i_l_e] DDEESSCCRRIIPPTTIIOONN ccvvttssuuddooeerrss can be used to convert between _s_u_d_o_e_r_s security policy file @@ -27,6 +28,10 @@ DDEESSCCRRIIPPTTIIOONN environment variable will be used instead. Only necessary when converting to LDIF format. + --cc, ----ccoonnffiigg + Specify the path to a configuration file. Defaults to + _/_e_t_c_/_c_v_t_s_u_d_o_e_r_s_._c_o_n_f. + --ee, ----eexxppaanndd--aalliiaasseess Expand aliases in _i_n_p_u_t___f_i_l_e. Aliases are preserved by default when the output _f_o_r_m_a_t is JSON or sudoers. @@ -63,10 +68,10 @@ DDEESSCCRRIIPPTTIIOONN --hh, ----hheellpp Display a short help message to the standard output and exit. - --oo _o_u_t_p_u_t___f_i_l_e, ----oouuttppuutt=_o_u_t_p_u_t___f_i_l_e - Write the converted output to _o_u_t_p_u_t___f_i_l_e. If no _o_u_t_p_u_t___f_i_l_e - is specified, or if it is `-', the converted _s_u_d_o_e_r_s policy - will be written to the standard output. + --II _i_n_c_r_e_m_e_n_t, ----iinnccrreemmeenntt=_i_n_c_r_e_m_e_n_t + When generating LDIF output, increment each sudoOrder + attribute by the specified number. Defaults to an increment + of 1. --ii _i_n_p_u_t___f_o_r_m_a_t, ----iinnppuutt--ffoorrmmaatt=_i_n_p_u_t___f_o_r_m_a_t Specify the input format. The following formats are @@ -83,9 +88,49 @@ DDEESSCCRRIIPPTTIIOONN sudoers Traditional sudoers format. This is the default input format. + --OO _s_t_a_r_t___p_o_i_n_t, ----oorrddeerr--ssttaarrtt=_s_t_a_r_t___p_o_i_n_t + When generating LDIF output, use the number specified by + _s_t_a_r_t___p_o_i_n_t in the sudoOrder attribute of the first sudoRole + object. Subsequent sudoRole object use a sudoOrder value + generated by adding an _i_n_c_r_e_m_e_n_t, see the --II option for + details. Defaults to a starting point of 1. + + --oo _o_u_t_p_u_t___f_i_l_e, ----oouuttppuutt=_o_u_t_p_u_t___f_i_l_e + Write the converted output to _o_u_t_p_u_t___f_i_l_e. If no _o_u_t_p_u_t___f_i_l_e + is specified, or if it is `-', the converted _s_u_d_o_e_r_s policy + will be written to the standard output. + --VV, ----vveerrssiioonn Print the ccvvttssuuddooeerrss and _s_u_d_o_e_r_s grammar versions and exit. + Options in the form "keyword = value" may also be specified in a + configuration file, _/_e_t_c_/_c_v_t_s_u_d_o_e_r_s_._c_o_n_f by default. The following + keywords are recognized: + + eexxppaanndd__aalliiaasseess == _y_e_s | _n_o + See the description of the --ee command line option. + + iinnppuutt__ffoorrmmaatt == _l_d_i_f | _s_u_d_o_e_r_s + See the description of the --ii command line option. + + oorrddeerr__iinnccrreemmeenntt == _i_n_c_r_e_m_e_n_t + See the description of the --II command line option. + + oorrddeerr__ssttaarrtt == _s_t_a_r_t___p_o_r_t + See the description of the --OO command line option. + + oouuttppuutt__ffoorrmmaatt == _j_s_o_n | _l_d_i_f | _s_u_d_o_e_r_s + See the description of the --ff command line option. + + ssuuddooeerrss__bbaassee == _d_n + See the description of the --bb command line option. + + Options on the command line will override values from the configuration + file. + +FFIILLEESS + _/_e_t_c_/_c_v_t_s_u_d_o_e_r_s_._c_o_n_f default configuration for cvtsudoers + SSEEEE AALLSSOO sudoers(4), sudoers.ldap(4), sudo(1m) @@ -115,4 +160,4 @@ DDIISSCCLLAAIIMMEERR file distributed with ssuuddoo or https://www.sudo.ws/license.html for complete details. -Sudo 1.8.23 February 22, 2018 Sudo 1.8.23 +Sudo 1.8.23 February 23, 2018 Sudo 1.8.23 diff --git a/doc/cvtsudoers.man.in b/doc/cvtsudoers.man.in index d2892d974..1e223fe74 100644 --- a/doc/cvtsudoers.man.in +++ b/doc/cvtsudoers.man.in @@ -16,7 +16,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.TH "CVTSUDOERS" "8" "February 22, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" +.TH "CVTSUDOERS" "8" "February 23, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" .nh .if n .ad l .SH "NAME" @@ -27,8 +27,11 @@ \fBcvtsudoers\fR [\fB\-ehV\fR] [\fB\-b\fR\ \fIdn\fR] +[\fB\-c\fR\ \fIconf_file\fR] +[\fB\-I\fR\ \fIincrement\fR] [\fB\-i\fR\ \fIinput_format\fR] [\fB\-f\fR\ \fIoutput_format\fR] +[\fB\-O\fR\ \fIstart_point\fR] [\fB\-o\fR\ \fIoutput_file\fR] [\fIinput_file\fR] .SH "DESCRIPTION" @@ -63,6 +66,11 @@ If this option is not specified, the value of the environment variable will be used instead. Only necessary when converting to LDIF format. .TP 12n +\fB\-c\fR, \fB\--config\fR +Specify the path to a configuration file. +Defaults to +\fI@sysconfdir@/cvtsudoers.conf\fR. +.TP 12n \fB\-e\fR, \fB\--expand-aliases\fR Expand aliases in \fIinput_file\fR. @@ -124,16 +132,10 @@ output inline. \fB\-h\fR, \fB\--help\fR Display a short help message to the standard output and exit. .TP 12n -\fB\-o\fR \fIoutput_file\fR, \fB\--output\fR=\fIoutput_file\fR -Write the converted output to -\fIoutput_file\fR. -If no -\fIoutput_file\fR -is specified, or if it is -\(oq-\(cq, -the converted -\fIsudoers\fR -policy will be written to the standard output. +\fB\-I\fR \fIincrement\fR, \fB\--increment\fR=\fIincrement\fR +When generating LDIF output, increment each sudoOrder attribute by +the specified number. +Defaults to an increment of 1. .TP 12n \fB\-i\fR \fIinput_format\fR, \fB\--input-format\fR=\fIinput_format\fR Specify the input format. @@ -160,12 +162,78 @@ This is the default input format. .RE .PD .TP 12n +\fB\-O\fR \fIstart_point\fR, \fB\--order-start\fR=\fIstart_point\fR +When generating LDIF output, use the number specified by +\fIstart_point\fR +in the sudoOrder attribute of the first sudoRole object. +Subsequent sudoRole object use a sudoOrder value generated by adding an +\fIincrement\fR, +see the +\fB\-I\fR +option for details. +Defaults to a starting point of 1. +.TP 12n +\fB\-o\fR \fIoutput_file\fR, \fB\--output\fR=\fIoutput_file\fR +Write the converted output to +\fIoutput_file\fR. +If no +\fIoutput_file\fR +is specified, or if it is +\(oq-\(cq, +the converted +\fIsudoers\fR +policy will be written to the standard output. +.TP 12n \fB\-V\fR, \fB\--version\fR Print the \fBcvtsudoers\fR and \fIsudoers\fR grammar versions and exit. +.PP +Options in the form +\(Lqkeyword = value\(Rq +may also be specified in a configuration file, +\fI@sysconfdir@/cvtsudoers.conf\fR +by default. +The following keywords are recognized: +.TP 6n +\fBexpand_aliases =\fR \fIyes\fR | \fIno\fR +See the description of the +\fB\-e\fR +command line option. +.TP 6n +\fBinput_format =\fR \fIldif\fR | \fIsudoers\fR +See the description of the +\fB\-i\fR +command line option. +.TP 6n +\fBorder_increment =\fR \fIincrement\fR +See the description of the +\fB\-I\fR +command line option. +.TP 6n +\fBorder_start =\fR \fIstart_port\fR +See the description of the +\fB\-O\fR +command line option. +.TP 6n +\fBoutput_format =\fR \fIjson\fR | \fIldif\fR | \fIsudoers\fR +See the description of the +\fB\-f\fR +command line option. +.TP 6n +\fBsudoers_base =\fR \fIdn\fR +See the description of the +\fB\-b\fR +command line option. +.PP +Options on the command line will override values from the +configuration file. +.SH "FILES" +.TP 26n +\fI@sysconfdir@/cvtsudoers.conf\fR +default configuration for cvtsudoers .SH "SEE ALSO" sudoers(@mansectform@), sudoers.ldap(@mansectform@), diff --git a/doc/cvtsudoers.mdoc.in b/doc/cvtsudoers.mdoc.in index 8ff3c8008..8c99d1892 100644 --- a/doc/cvtsudoers.mdoc.in +++ b/doc/cvtsudoers.mdoc.in @@ -14,7 +14,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd February 22, 2018 +.Dd February 23, 2018 .Dt CVTSUDOERS @mansectsu@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -24,8 +24,11 @@ .Nm cvtsudoers .Op Fl ehV .Op Fl b Ar dn +.Op Fl c Ar conf_file +.Op Fl I Ar increment .Op Fl i Ar input_format .Op Fl f Ar output_format +.Op Fl O Ar start_point .Op Fl o Ar output_file .Op Ar input_file .Sh DESCRIPTION @@ -59,6 +62,10 @@ If this option is not specified, the value of the .Ev SUDOERS_BASE environment variable will be used instead. Only necessary when converting to LDIF format. +.It Fl c , Fl -config +Specify the path to a configuration file. +Defaults to +.Pa @sysconfdir@/cvtsudoers.conf . .It Fl e , Fl -expand-aliases Expand aliases in .Ar input_file . @@ -100,16 +107,10 @@ output inline. .El .It Fl h , Fl -help Display a short help message to the standard output and exit. -.It Fl o Ar output_file , Fl -output Ns = Ns Ar output_file -Write the converted output to -.Ar output_file . -If no -.Ar output_file -is specified, or if it is -.Ql - , -the converted -.Em sudoers -policy will be written to the standard output. +.It Fl I Ar increment , Fl -increment Ns = Ns Ar increment +When generating LDIF output, increment each sudoOrder attribute by +the specified number. +Defaults to an increment of 1. .It Fl i Ar input_format , Fl -input-format Ns = Ns Ar input_format Specify the input format. The following formats are supported: @@ -126,6 +127,26 @@ LDIF to sudoers format. Traditional sudoers format. This is the default input format. .El +.It Fl O Ar start_point , Fl -order-start Ns = Ns Ar start_point +When generating LDIF output, use the number specified by +.Ar start_point +in the sudoOrder attribute of the first sudoRole object. +Subsequent sudoRole object use a sudoOrder value generated by adding an +.Ar increment , +see the +.Fl I +option for details. +Defaults to a starting point of 1. +.It Fl o Ar output_file , Fl -output Ns = Ns Ar output_file +Write the converted output to +.Ar output_file . +If no +.Ar output_file +is specified, or if it is +.Ql - , +the converted +.Em sudoers +policy will be written to the standard output. .It Fl V , -version Print the .Nm @@ -133,6 +154,46 @@ and .Em sudoers grammar versions and exit. .El +.Pp +Options in the form +.Dq keyword = value +may also be specified in a configuration file, +.Pa @sysconfdir@/cvtsudoers.conf +by default. +The following keywords are recognized: +.Bl -tag -width 4n +.It Sy expand_aliases = Ar yes | no +See the description of the +.Fl e +command line option. +.It Sy input_format = Ar ldif | sudoers +See the description of the +.Fl i +command line option. +.It Sy order_increment = Ar increment +See the description of the +.Fl I +command line option. +.It Sy order_start = Ar start_port +See the description of the +.Fl O +command line option. +.It Sy output_format = Ar json | ldif | sudoers +See the description of the +.Fl f +command line option. +.It Sy sudoers_base = Ar dn +See the description of the +.Fl b +command line option. +.El +.Pp +Options on the command line will override values from the +configuration file. +.Sh FILES +.Bl -tag -width 24n +.It Pa @sysconfdir@/cvtsudoers.conf +default configuration for cvtsudoers .El .Sh SEE ALSO .Xr sudoers @mansectform@ , diff --git a/pathnames.h.in b/pathnames.h.in index d55b90971..31db3a452 100644 --- a/pathnames.h.in +++ b/pathnames.h.in @@ -69,6 +69,13 @@ # define _PATH_SUDOERS "/etc/sudoers" #endif /* _PATH_SUDOERS */ +/* + * NOTE: _PATH_CVTSUDOERS_CONF is usually overridden by the Makefile. + */ +#ifndef _PATH_CVTSUDOERS_CONF +# define _PATH_CVTSUDOERS_CONF "/etc/cvtsudoers.conf" +#endif /* _PATH_CVTSUDOERS_CONF */ + /* * The following paths are controlled via the configure script. */ diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 0c9e10d4f..33dd2dcc3 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -1,5 +1,5 @@ # -# Copyright (c) 1996, 1998-2005, 2007-2017 +# Copyright (c) 1996, 1998-2005, 2007-2018 # Todd C. Miller # # Permission to use, copy, modify, and distribute this software for any @@ -63,6 +63,7 @@ TESTSUDOERS_LIBS = $(NET_LIBS) @LIBMD@ # C preprocessor defines CPPDEFS = -DLIBDIR=\"$(libdir)\" -DLOCALEDIR=\"$(localedir)\" \ -D_PATH_SUDOERS=\"$(sudoersdir)/sudoers\" \ + -D_PATH_CVTSUDOERS_CONF=\"$(sysconfdir)/cvtsudoers.conf\" \ -DSUDOERS_UID=$(sudoers_uid) -DSUDOERS_GID=$(sudoers_gid) \ -DSUDOERS_MODE=$(sudoers_mode) @@ -449,7 +450,7 @@ check: $(TEST_PROGS) visudo testsudoers diff $$toke $(srcdir)/$$toke.ok || true; \ fi; \ total=`expr $$total + 1`; \ - ./cvtsudoers -f json $$t >$$json 2>/dev/null || true; \ + ./cvtsudoers -c "" -f json $$t >$$json 2>/dev/null || true; \ total=`expr $$total + 1`; \ if cmp $$json $(srcdir)/$$json.ok >/dev/null; then \ passed=`expr $$passed + 1`; \ @@ -460,7 +461,7 @@ check: $(TEST_PROGS) visudo testsudoers diff $$json $(srcdir)/$$json.ok || true; \ fi; \ SUDOERS_BASE="ou=SUDOers,dc=sudo,dc=ws" \ - ./cvtsudoers -f ldif $$t >$$ldif 2>/dev/null || true; \ + ./cvtsudoers -c "" -f ldif $$t >$$ldif 2>/dev/null || true; \ total=`expr $$total + 1`; \ if cmp $$ldif $(srcdir)/$$ldif.ok >/dev/null; then \ passed=`expr $$passed + 1`; \ @@ -470,7 +471,7 @@ check: $(TEST_PROGS) visudo testsudoers echo "$$dir/$$base: (ldif) FAIL"; \ diff $$ldif $(srcdir)/$$ldif.ok || true; \ fi; \ - ./cvtsudoers -f sudoers $$t >$$sudo 2>/dev/null || true; \ + ./cvtsudoers -c "" -f sudoers $$t >$$sudo 2>/dev/null || true; \ total=`expr $$total + 1`; \ if ./visudo -qcf $$sudo; then \ passed=`expr $$passed + 1`; \ @@ -712,8 +713,9 @@ cvtsudoers.o: $(srcdir)/cvtsudoers.c $(devdir)/def_data.h $(devdir)/gram.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_gettext.h $(incdir)/sudo_lbuf.h \ $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ - $(incdir)/sudo_util.h $(srcdir)/defaults.h $(srcdir)/logging.h \ - $(srcdir)/parse.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ + $(incdir)/sudo_util.h $(srcdir)/cvtsudoers.h \ + $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ + $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ $(srcdir)/sudoers_debug.h $(srcdir)/sudoers_version.h \ $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/cvtsudoers.c @@ -723,10 +725,10 @@ cvtsudoers_json.o: $(srcdir)/cvtsudoers_json.c $(devdir)/def_data.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ - $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ - $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h + $(srcdir)/cvtsudoers.h $(srcdir)/defaults.h \ + $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_nss.h \ + $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/cvtsudoers_json.c cvtsudoers_ldif.o: $(srcdir)/cvtsudoers_ldif.c $(devdir)/def_data.h \ $(devdir)/gram.h $(incdir)/compat/stdbool.h \ @@ -734,11 +736,11 @@ cvtsudoers_ldif.o: $(srcdir)/cvtsudoers_ldif.c $(devdir)/def_data.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ - $(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \ - $(srcdir)/redblack.h $(srcdir)/sudo_ldap.h \ - $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ - $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ - $(top_builddir)/pathnames.h + $(srcdir)/cvtsudoers.h $(srcdir)/defaults.h \ + $(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/redblack.h \ + $(srcdir)/sudo_ldap.h $(srcdir)/sudo_nss.h \ + $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \ + $(top_builddir)/config.h $(top_builddir)/pathnames.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/cvtsudoers_ldif.c dce.lo: $(authdir)/dce.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c index c24736663..728c51ed5 100644 --- a/plugins/sudoers/cvtsudoers.c +++ b/plugins/sudoers/cvtsudoers.c @@ -29,15 +29,17 @@ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ -#include +#include #include #include +#include #include "sudoers.h" #include "parse.h" #include "sudoers_version.h" #include "sudo_conf.h" #include "sudo_lbuf.h" +#include "cvtsudoers.h" #include #ifdef HAVE_GETOPT_LONG @@ -46,25 +48,21 @@ # include "compat/getopt.h" #endif /* HAVE_GETOPT_LONG */ -static bool convert_sudoers_sudoers(const char *output_file, bool expand_aliases); -static bool parse_sudoers(const char *input_file); -extern bool convert_sudoers_json(const char *output_file, bool expand_aliases); -extern bool convert_sudoers_ldif(const char *output_file, const char *base); -extern bool parse_ldif(const char *input_file, bool store_options, const char *base); -extern void get_hostname(void); - /* * Globals */ struct sudo_user sudo_user; struct passwd *list_pw; -static const char short_opts[] = "b:ef:hi:o:V"; +static const char short_opts[] = "b:c:ef:hi:I:o:O:V"; static struct option long_opts[] = { { "base", required_argument, NULL, 'b' }, + { "config", required_argument, NULL, 'c' }, { "expand-aliases", no_argument, NULL, 'e' }, - { "format", required_argument, NULL, 'f' }, + { "output-format", required_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, { "input-format", required_argument, NULL, 'i' }, + { "increment", required_argument, NULL, 'I' }, + { "order-start", required_argument, NULL, 'O' }, { "output", required_argument, NULL, 'o' }, { "version", no_argument, NULL, 'V' }, { NULL, no_argument, NULL, '\0' }, @@ -73,12 +71,10 @@ static struct option long_opts[] = { __dso_public int main(int argc, char *argv[]); static void help(void) __attribute__((__noreturn__)); static void usage(int); - -enum sudoers_formats { - format_json, - format_ldif, - format_sudoers -}; +static bool convert_sudoers_sudoers(const char *output_file, struct cvtsudoers_config *conf); +static bool parse_sudoers(const char *input_file, struct cvtsudoers_config *conf); +static struct cvtsudoers_config *cvtsudoers_conf_read(const char *conf_file); +static void cvtsudoers_conf_free(struct cvtsudoers_config *conf); int main(int argc, char *argv[]) @@ -86,11 +82,11 @@ main(int argc, char *argv[]) int ch, exitcode = EXIT_FAILURE; enum sudoers_formats output_format = format_ldif; enum sudoers_formats input_format = format_sudoers; - bool store_options = true; + struct cvtsudoers_config *conf; const char *input_file = "-"; const char *output_file = "-"; - const char *sudoers_base = NULL; - bool expand_aliases = false; + const char *conf_file = _PATH_CVTSUDOERS_CONF; + const char *errstr; debug_decl(main, SUDOERS_DEBUG_MAIN) #if defined(SUDO_DEVEL) && defined(__OpenBSD__) @@ -115,48 +111,79 @@ main(int argc, char *argv[]) if (!sudoers_debug_register(getprogname(), sudo_conf_debug_files(getprogname()))) goto done; + /* Check for --config option first (no getopt warnings). */ + opterr = 0; + while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { + switch (ch) { + case 'c': + conf_file = optarg; + break; + } + } + + /* Read conf file. */ + conf = cvtsudoers_conf_read(conf_file); + /* - * Arg handling. + * Reset getopt and handle the rest of the arguments. */ + opterr = 1; + optind = 1; +#ifdef HAVE_OPTRESET + optreset = 1; +#endif while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { switch (ch) { case 'b': - sudoers_base = optarg; + free(conf->sudoers_base); + conf->sudoers_base = strdup(optarg); + if (conf->sudoers_base == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + break; + case 'c': + /* handled above */ break; case 'e': - expand_aliases = true; + conf->expand_aliases = true; break; case 'f': - if (strcasecmp(optarg, "json") == 0) { - output_format = format_json; - store_options = true; - } else if (strcasecmp(optarg, "ldif") == 0) { - output_format = format_ldif; - store_options = true; - } else if (strcasecmp(optarg, "sudoers") == 0) { - output_format = format_sudoers; - store_options = false; - } else { - sudo_warnx("unsupported output format %s", optarg); - usage(1); + free(conf->output_format); + conf->output_format = strdup(optarg); + if (conf->output_format == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); } break; case 'h': help(); break; case 'i': - if (strcasecmp(optarg, "ldif") == 0) { - input_format = format_ldif; - } else if (strcasecmp(optarg, "sudoers") == 0) { - input_format = format_sudoers; - } else { - sudo_warnx("unsupported input format %s", optarg); + free(conf->input_format); + conf->input_format = strdup(optarg); + if (conf->input_format == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + break; + case 'I': + conf->order_increment = sudo_strtonum(optarg, 1, UINT_MAX, &errstr); + if (errstr != NULL) { + sudo_warnx(U_("order increment: %s: %s"), optarg, U_(errstr)); usage(1); } break; case 'o': output_file = optarg; break; + case 'O': + conf->sudo_order = sudo_strtonum(optarg, 1, UINT_MAX, &errstr); + if (errstr != NULL) { + sudo_warnx(U_("starting order: %s: %s"), optarg, U_(errstr)); + usage(1); + } + break; case 'V': (void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION); @@ -171,15 +198,48 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; + if (conf->input_format != NULL) { + if (strcasecmp(conf->input_format, "ldif") == 0) { + input_format = format_ldif; + } else if (strcasecmp(conf->input_format, "sudoers") == 0) { + input_format = format_sudoers; + } else { + sudo_warnx(U_("unsupported input format %s"), conf->input_format); + usage(1); + } + } + if (conf->output_format != NULL) { + if (strcasecmp(conf->output_format, "json") == 0) { + output_format = format_json; + conf->store_options = true; + } else if (strcasecmp(conf->output_format, "ldif") == 0) { + output_format = format_ldif; + conf->store_options = true; + } else if (strcasecmp(conf->output_format, "sudoers") == 0) { + output_format = format_sudoers; + conf->store_options = false; + } else { + sudo_warnx(U_("unsupported output format %s"), conf->output_format); + usage(1); + } + } + /* If no base DN specified, check SUDOERS_BASE. */ - if (sudoers_base == NULL) - sudoers_base = getenv("SUDOERS_BASE"); + if (conf->sudoers_base == NULL) { + conf->sudoers_base = getenv("SUDOERS_BASE"); + if (conf->sudoers_base != NULL && *conf->sudoers_base != '\0') { + if ((conf->sudoers_base = strdup(conf->sudoers_base)) == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + } + } /* Input file (defaults to stdin). */ if (argc > 0) { if (argc > 1) usage(1); - input_file = argv[0]; + input_file = argv[0]; } if (strcmp(input_file, "-") != 0) { @@ -190,7 +250,7 @@ main(int argc, char *argv[]) } /* Mock up a fake sudo_user struct. */ - /* XXX - common with visudo */ + /* XXX - should not be required */ user_cmnd = user_base = ""; if (geteuid() == 0) { const char *user = getenv("SUDO_USER"); @@ -209,11 +269,11 @@ main(int argc, char *argv[]) switch (input_format) { case format_ldif: - if (!parse_ldif(input_file, store_options, sudoers_base)) + if (!parse_ldif(input_file, conf)) goto done; break; case format_sudoers: - if (!parse_sudoers(input_file)) + if (!parse_sudoers(input_file, conf)) goto done; break; default: @@ -222,25 +282,145 @@ main(int argc, char *argv[]) switch (output_format) { case format_json: - exitcode = !convert_sudoers_json(output_file, expand_aliases); + exitcode = !convert_sudoers_json(output_file, conf); break; case format_ldif: - exitcode = !convert_sudoers_ldif(output_file, sudoers_base); + exitcode = !convert_sudoers_ldif(output_file, conf); break; case format_sudoers: - exitcode = !convert_sudoers_sudoers(output_file, expand_aliases); + exitcode = !convert_sudoers_sudoers(output_file, conf); break; default: sudo_fatalx("error: unhandled output format %d", output_format); } done: + cvtsudoers_conf_free(conf); sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode); return exitcode; } +/* + * cvtsudoers configuration data. + */ +static struct cvtsudoers_config cvtsudoers_config = INITIAL_CONFIG; +static struct cvtsudoers_conf_table cvtsudoers_conf_vars[] = { + { "order_start", CONF_UINT, &cvtsudoers_config.sudo_order }, + { "order_increment", CONF_UINT, &cvtsudoers_config.order_increment }, + { "sudoers_base", CONF_STR, &cvtsudoers_config.sudoers_base }, + { "input_format", CONF_STR, &cvtsudoers_config.input_format }, + { "output_format", CONF_STR, &cvtsudoers_config.output_format }, + { "expand_aliases", CONF_BOOL, &cvtsudoers_config.expand_aliases } +}; + +/* + * Look up keyword in config table. + * Returns true if found, else false. + */ +static bool +cvtsudoers_parse_keyword(const char *conf_file, const char *keyword, + const char *value, struct cvtsudoers_conf_table *table) +{ + struct cvtsudoers_conf_table *cur; + const char *errstr; + debug_decl(sudo_ldap_parse_keyword, SUDOERS_DEBUG_UTIL) + + /* Look up keyword in config tables */ + for (cur = table; cur->conf_str != NULL; cur++) { + if (strcasecmp(keyword, cur->conf_str) == 0) { + switch (cur->type) { + case CONF_BOOL: + *(bool *)(cur->valp) = sudo_strtobool(value) == true; + break; + case CONF_UINT: + { + unsigned int uval = + strtonum(value, 0, UINT_MAX, &errstr); + if (errstr != NULL) { + sudo_warnx(U_("%s: %s: %s: %s"), + conf_file, keyword, value, U_(errstr)); + continue; + } + *(unsigned int *)(cur->valp) = uval; + } + break; + case CONF_STR: + { + char *cp = strdup(value); + if (cp == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + free(*(char **)(cur->valp)); + *(char **)(cur->valp) = cp; + break; + } + } + debug_return_bool(true); + } + } + debug_return_bool(false); +} + +static struct cvtsudoers_config * +cvtsudoers_conf_read(const char *conf_file) +{ + char *line = NULL; + size_t linesize = 0; + FILE *fp; + debug_decl(cvtsudoers_conf_read, SUDOERS_DEBUG_UTIL) + + if ((fp = fopen(conf_file, "r")) == NULL) + debug_return_ptr(&cvtsudoers_config); + + while (sudo_parseln(&line, &linesize, NULL, fp, 0) != -1) { + char *cp, *keyword, *value; + + if (*line == '\0') + continue; /* skip empty line */ + + /* Parse keyword = value */ + keyword = line; + if ((cp = strchr(line, '=')) == NULL) + continue; + value = cp-- + 1; + + /* Trim whitespace after keyword. */ + while (cp != line && isblank((unsigned char)cp[-1])) + cp--; + *cp = '\0'; + + /* Trim whitespace before value. */ + while (isblank((unsigned char)*value)) + value++; + + /* Look up keyword in config tables */ + if (!cvtsudoers_parse_keyword(conf_file, keyword, value, cvtsudoers_conf_vars)) + sudo_warnx(U_("%s: unknown key word: %s"), conf_file, keyword); + } + free(line); + fclose(fp); + + debug_return_ptr(&cvtsudoers_config); +} + +static void +cvtsudoers_conf_free(struct cvtsudoers_config *conf) +{ + debug_decl(cvtsudoers_conf_free, SUDOERS_DEBUG_UTIL) + + free(conf->sudoers_base); + free(conf->input_format); + free(conf->output_format); + conf->sudoers_base = NULL; + conf->input_format = NULL; + conf->output_format = NULL; + + debug_return; +} + static bool -parse_sudoers(const char *input_file) +parse_sudoers(const char *input_file, struct cvtsudoers_config *conf) { debug_decl(parse_sudoers, SUDOERS_DEBUG_UTIL) @@ -377,7 +557,7 @@ convert_sudoers_output(const char *buf) * Convert back to sudoers. */ static bool -convert_sudoers_sudoers(const char *output_file, bool expand_aliases) +convert_sudoers_sudoers(const char *output_file, struct cvtsudoers_config *conf) { bool ret = true; struct sudo_lbuf lbuf; @@ -394,7 +574,7 @@ convert_sudoers_sudoers(const char *output_file, bool expand_aliases) sudo_lbuf_init(&lbuf, convert_sudoers_output, 4, "\\", 80); /* Print Defaults */ - if (!print_defaults_sudoers(&lbuf, expand_aliases)) + if (!print_defaults_sudoers(&lbuf, conf->expand_aliases)) goto done; if (lbuf.len > 0) { sudo_lbuf_print(&lbuf); @@ -402,7 +582,7 @@ convert_sudoers_sudoers(const char *output_file, bool expand_aliases) } /* Print Aliases */ - if (!expand_aliases) { + if (!conf->expand_aliases) { if (!print_aliases_sudoers(&lbuf)) goto done; if (lbuf.len > 1) { @@ -412,7 +592,7 @@ convert_sudoers_sudoers(const char *output_file, bool expand_aliases) } /* Print User_Specs */ - if (!sudoers_format_userspecs(&lbuf, &userspecs, expand_aliases)) + if (!sudoers_format_userspecs(&lbuf, &userspecs, conf->expand_aliases)) goto done; if (lbuf.len > 1) { sudo_lbuf_print(&lbuf); @@ -441,7 +621,8 @@ static void usage(int fatal) { (void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehV] [-b dn] " - "[-f output_format] [-i input_format] [-o output_file] [input_file]\n", + "[-c conf_file ] [-f output_format] [-i input_format] [-I increment] " + "[-o output_file] [-O start_point] [input_file]\n", getprogname()); if (fatal) exit(1); @@ -453,10 +634,14 @@ help(void) (void) printf(_("%s - convert between sudoers file formats\n\n"), getprogname()); usage(0); (void) puts(_("\nOptions:\n" - " -b, --base=dn the base DN for sudo LDAP queries\n" - " -f, --format=JSON|LDIF specify output format (JSON or LDIF)\n" - " -h, --help display help message and exit\n" - " -o, --output=output_file write converted sudoers to output_file\n" - " -V, --version display version information and exit")); + " -b, --base=dn the base DN for sudo LDAP queries\n" + " -e, --expand-aliases expand aliases when converting\n" + " -f, --output-format=format set output format: JSON, LDIF or sudoers\n" + " -I, --increment=num amount to increase each sudoOrder by\n" + " -i, --input-format=format set input format: LDIF or sudoers\n" + " -h, --help display help message and exit\n" + " -O, --order-start=num starting point for first sudoOrder\n" + " -o, --output=output_file write converted sudoers to output_file\n" + " -V, --version display version information and exit")); exit(0); } diff --git a/plugins/sudoers/cvtsudoers.h b/plugins/sudoers/cvtsudoers.h new file mode 100644 index 000000000..f24f15472 --- /dev/null +++ b/plugins/sudoers/cvtsudoers.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SUDOERS_CVTSUDOERS_H +#define SUDOERS_CVTSUDOERS_H + +/* Supported input/output formats. */ +enum sudoers_formats { + format_json, + format_ldif, + format_sudoers +}; + +/* cvtsudoers.conf settings */ +struct cvtsudoers_config { + char *sudoers_base; + char *input_format; + char *output_format; + unsigned int sudo_order; + unsigned int order_increment; + bool expand_aliases; + bool store_options; +}; + +/* Initial config settings for above. */ +#define INITIAL_CONFIG { NULL, NULL, NULL, 1, 1, false, true } + +#define CONF_BOOL 0 +#define CONF_UINT 1 +#define CONF_STR 2 + +struct cvtsudoers_conf_table { + const char *conf_str; /* config file string */ + int type; /* CONF_BOOL, CONF_UINT, CONF_STR */ + void *valp; /* pointer into cvtsudoers_config */ +}; + +bool convert_sudoers_json(const char *output_file, struct cvtsudoers_config *conf); +bool convert_sudoers_ldif(const char *output_file, struct cvtsudoers_config *conf); +bool parse_ldif(const char *input_file, struct cvtsudoers_config *conf); +void get_hostname(void); + +#endif /* SUDOERS_CVTSUDOERS_H */ diff --git a/plugins/sudoers/cvtsudoers_json.c b/plugins/sudoers/cvtsudoers_json.c index fcdf11c53..825bb3826 100644 --- a/plugins/sudoers/cvtsudoers_json.c +++ b/plugins/sudoers/cvtsudoers_json.c @@ -32,6 +32,7 @@ #include "sudoers.h" #include "parse.h" +#include "cvtsudoers.h" #include /* @@ -1089,14 +1090,14 @@ print_userspecs_json(FILE *fp, int indent, bool expand_aliases, bool need_comma) * Export the parsed sudoers file in JSON format. */ bool -convert_sudoers_json(const char *output_file, bool expand_aliases) +convert_sudoers_json(const char *output_file, struct cvtsudoers_config *conf) { bool ret = true, need_comma = false; const int indent = 4; FILE *output_fp = stdout; debug_decl(convert_sudoers_json, SUDOERS_DEBUG_UTIL) - if (strcmp(output_file, "-") != 0) { + if (strcmp(output_file, "-") != 0) { if ((output_fp = fopen(output_file, "w")) == NULL) sudo_fatal(U_("unable to open %s"), output_file); } @@ -1105,14 +1106,14 @@ convert_sudoers_json(const char *output_file, bool expand_aliases) putc('{', output_fp); /* Dump Defaults in JSON format. */ - need_comma = print_defaults_json(output_fp, indent, expand_aliases, need_comma); + need_comma = print_defaults_json(output_fp, indent, conf->expand_aliases, need_comma); /* Dump Aliases in JSON format. */ - if (!expand_aliases) + if (!conf->expand_aliases) need_comma = print_aliases_json(output_fp, indent, need_comma); /* Dump User_Specs in JSON format. */ - print_userspecs_json(output_fp, indent, expand_aliases, need_comma); + print_userspecs_json(output_fp, indent, conf->expand_aliases, need_comma); /* Close JSON output. */ fputs("\n}\n", output_fp); diff --git a/plugins/sudoers/cvtsudoers_ldif.c b/plugins/sudoers/cvtsudoers_ldif.c index 28e35d55e..120b3bd18 100644 --- a/plugins/sudoers/cvtsudoers_ldif.c +++ b/plugins/sudoers/cvtsudoers_ldif.c @@ -33,6 +33,7 @@ #include "sudo_ldap.h" #include "parse.h" #include "redblack.h" +#include "cvtsudoers.h" #include struct seen_user { @@ -40,7 +41,6 @@ struct seen_user { unsigned long count; }; -static int sudo_order; static struct rbtree *seen_users; static int @@ -400,7 +400,7 @@ bad: * Print a single User_Spec. */ static bool -print_userspec_ldif(FILE *fp, struct userspec *us, const char *base) +print_userspec_ldif(FILE *fp, struct userspec *us, struct cvtsudoers_config *conf) { struct privilege *priv; struct member *m; @@ -427,7 +427,7 @@ print_userspec_ldif(FILE *fp, struct userspec *us, const char *base) U_("unable to allocate memory")); } - fprintf(fp, "dn: cn=%s,%s\n", cn, base); + fprintf(fp, "dn: cn=%s,%s\n", cn, conf->sudoers_base); fprintf(fp, "objectClass: top\n"); fprintf(fp, "objectClass: sudoRole\n"); fprintf(fp, "cn: %s\n", cn); @@ -445,7 +445,8 @@ print_userspec_ldif(FILE *fp, struct userspec *us, const char *base) print_cmndspec_ldif(fp, cs, &next, &priv->defaults); - fprintf(fp, "sudoOrder: %d\n\n", ++sudo_order); + fprintf(fp, "sudoOrder: %d\n\n", conf->sudo_order); + conf->sudo_order += conf->order_increment; } } @@ -456,13 +457,13 @@ print_userspec_ldif(FILE *fp, struct userspec *us, const char *base) * Print User_Specs. */ static bool -print_userspecs_ldif(FILE *fp, const char *base) +print_userspecs_ldif(FILE *fp, struct cvtsudoers_config *conf) { struct userspec *us; debug_decl(print_userspecs_ldif, SUDOERS_DEBUG_UTIL) TAILQ_FOREACH(us, &userspecs, entries) { - if (!print_userspec_ldif(fp, us, base)) + if (!print_userspec_ldif(fp, us, conf)) debug_return_bool(false); } debug_return_bool(true); @@ -472,17 +473,17 @@ print_userspecs_ldif(FILE *fp, const char *base) * Export the parsed sudoers file in LDIF format. */ bool -convert_sudoers_ldif(const char *output_file, const char *base) +convert_sudoers_ldif(const char *output_file, struct cvtsudoers_config *conf) { bool ret = true; FILE *output_fp = stdout; debug_decl(convert_sudoers_ldif, SUDOERS_DEBUG_UTIL) - if (base == NULL) { + if (conf->sudoers_base == NULL) { sudo_fatalx(U_("the SUDOERS_BASE environment variable is not set and the -b option was not specified.")); } - if (strcmp(output_file, "-") != 0) { + if (output_file != NULL && strcmp(output_file, "-") != 0) { if ((output_fp = fopen(output_file, "w")) == NULL) sudo_fatal(U_("unable to open %s"), output_file); } @@ -491,10 +492,10 @@ convert_sudoers_ldif(const char *output_file, const char *base) seen_users = rbcreate(seen_user_compare); /* Dump global Defaults in LDIF format. */ - print_global_defaults_ldif(output_fp, base); + print_global_defaults_ldif(output_fp, conf->sudoers_base); /* Dump User_Specs in LDIF format, expanding Aliases. */ - print_userspecs_ldif(output_fp, base); + print_userspecs_ldif(output_fp, conf); /* Warn about non-translatable Defaults entries. */ warn_bound_defaults_ldif(output_fp); @@ -697,7 +698,7 @@ ldif_store_options(struct ldif_str_list *options) * create aliases on the fly for multiple users/hosts? */ bool -parse_ldif(const char *input_file, bool store_options, const char *base) +parse_ldif(const char *input_file, struct cvtsudoers_config *conf) { struct sudo_role_list roles = STAILQ_HEAD_INITIALIZER(roles); struct sudo_role **role_array, *role = NULL; @@ -798,7 +799,7 @@ parse_ldif(const char *input_file, bool store_options, const char *base) /* Parse dn and objectClass. */ if (strncasecmp(line, "dn:", 3) == 0) { /* Compare dn to base, if specified. */ - if (base != NULL) { + if (conf->sudoers_base != NULL) { cp = line + 3; while (isblank((unsigned char)*cp)) cp++; @@ -810,7 +811,7 @@ parse_ldif(const char *input_file, bool store_options, const char *base) if (*cp == ',') cp++; } - if (strcasecmp(cp, base) != 0) { + if (strcasecmp(cp, conf->sudoers_base) != 0) { /* Doesn't match base, skip the rest of it. */ mismatch = true; continue; @@ -936,7 +937,7 @@ parse_ldif(const char *input_file, bool store_options, const char *base) priv = sudo_ldap_role_to_priv(role->cn, STAILQ_FIRST(&role->hosts), STAILQ_FIRST(&role->runasusers), STAILQ_FIRST(&role->runasgroups), STAILQ_FIRST(&role->cmnds), STAILQ_FIRST(&role->options), - role->notbefore, role->notafter, true, store_options, + role->notbefore, role->notafter, true, conf->store_options, ldif_string_iter); if (priv == NULL) { sudo_fatalx(U_("%s: %s"), __func__, -- 2.40.0