Initial support for parsing sudoers LDIF files in cvtsudoers.
This makes it possible to convert from LDAP sudoers to a traditional sudoers file. Semantic differences between file sudoers and LDAP sudoers mean that LDIF -> sudoers is not completely equivalent.
This commit is contained in:
@@ -4,14 +4,16 @@ NNAAMMEE
|
||||
ccvvttssuuddooeerrss - convert between sudoers file formats
|
||||
|
||||
SSYYNNOOPPSSIISS
|
||||
ccvvttssuuddooeerrss [--eehhVV] [--bb _d_n] [--ff _f_o_r_m_a_t] [--oo _o_u_t_p_u_t___f_i_l_e] [_s_u_d_o_e_r_s___f_i_l_e]
|
||||
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]
|
||||
|
||||
DDEESSCCRRIIPPTTIIOONN
|
||||
ccvvttssuuddooeerrss can be used to convert a policy file in _s_u_d_o_e_r_s format to
|
||||
other formats. The default output format is LDIF. It is only possible
|
||||
to convert a _s_u_d_o_e_r_s file that is syntactically correct.
|
||||
ccvvttssuuddooeerrss can be used to convert between _s_u_d_o_e_r_s security policy file
|
||||
formats. The default input format is sudoers. The default output format
|
||||
is LDIF. It is only possible to convert a _s_u_d_o_e_r_s file that is
|
||||
syntactically correct.
|
||||
|
||||
If no _s_u_d_o_e_r_s___f_i_l_e is specified, or if it is `-', the policy is read from
|
||||
If no _i_n_p_u_t___f_i_l_e is specified, or if it is `-', the policy is read from
|
||||
the standard input. By default, the result is written to the standard
|
||||
output.
|
||||
|
||||
@@ -26,7 +28,7 @@ DDEESSCCRRIIPPTTIIOONN
|
||||
when converting to LDIF format.
|
||||
|
||||
--ee, ----eexxppaanndd--aalliiaasseess
|
||||
Expand aliases in _s_u_d_o_e_r_s___f_i_l_e. Aliases are preserved by
|
||||
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.
|
||||
|
||||
--ff _o_u_t_p_u_t___f_o_r_m_a_t, ----ffoorrmmaatt=_o_u_t_p_u_t___f_o_r_m_a_t
|
||||
@@ -66,6 +68,21 @@ DDEESSCCRRIIPPTTIIOONN
|
||||
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_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
|
||||
supported:
|
||||
|
||||
LDIF LDIF (LDAP Data Interchange Format) files can be
|
||||
exported from an LDAP server to convert security
|
||||
policies used by sudoers.ldap(4). If a base DN
|
||||
(distinguished name) is specified, only sudoRole
|
||||
objects that match the base DN will be processed.
|
||||
Not all sudoOptions specified in a sudoRole can be
|
||||
translated from LDIF to sudoers format.
|
||||
|
||||
sudoers Traditional sudoers format. This is the default
|
||||
input format.
|
||||
|
||||
--VV, ----vveerrssiioonn
|
||||
Print the ccvvttssuuddooeerrss and _s_u_d_o_e_r_s grammar versions and exit.
|
||||
|
||||
@@ -98,4 +115,4 @@ DDIISSCCLLAAIIMMEERR
|
||||
file distributed with ssuuddoo or https://www.sudo.ws/license.html for
|
||||
complete details.
|
||||
|
||||
Sudo 1.8.23 February 18, 2018 Sudo 1.8.23
|
||||
Sudo 1.8.23 February 22, 2018 Sudo 1.8.23
|
||||
|
@@ -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 18, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
|
||||
.TH "CVTSUDOERS" "8" "February 22, 2018" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
|
||||
.nh
|
||||
.if n .ad l
|
||||
.SH "NAME"
|
||||
@@ -27,21 +27,23 @@
|
||||
\fBcvtsudoers\fR
|
||||
[\fB\-ehV\fR]
|
||||
[\fB\-b\fR\ \fIdn\fR]
|
||||
[\fB\-f\fR\ \fIformat\fR]
|
||||
[\fB\-i\fR\ \fIinput_format\fR]
|
||||
[\fB\-f\fR\ \fIoutput_format\fR]
|
||||
[\fB\-o\fR\ \fIoutput_file\fR]
|
||||
[\fIsudoers_file\fR]
|
||||
[\fIinput_file\fR]
|
||||
.SH "DESCRIPTION"
|
||||
\fBcvtsudoers\fR
|
||||
can be used to convert a policy file in
|
||||
can be used to convert between
|
||||
\fIsudoers\fR
|
||||
format to other formats.
|
||||
security policy file formats.
|
||||
The default input format is sudoers.
|
||||
The default output format is LDIF.
|
||||
It is only possible to convert a
|
||||
\fIsudoers\fR
|
||||
file that is syntactically correct.
|
||||
.PP
|
||||
If no
|
||||
\fIsudoers_file\fR
|
||||
\fIinput_file\fR
|
||||
is specified, or if it is
|
||||
\(oq-\(cq,
|
||||
the policy is read from the standard input.
|
||||
@@ -63,7 +65,7 @@ Only necessary when converting to LDIF format.
|
||||
.TP 12n
|
||||
\fB\-e\fR, \fB\--expand-aliases\fR
|
||||
Expand aliases in
|
||||
\fIsudoers_file\fR.
|
||||
\fIinput_file\fR.
|
||||
Aliases are preserved by default when the output
|
||||
\fIformat\fR
|
||||
is JSON or sudoers.
|
||||
@@ -133,6 +135,31 @@ the converted
|
||||
\fIsudoers\fR
|
||||
policy will be written to the standard output.
|
||||
.TP 12n
|
||||
\fB\-i\fR \fIinput_format\fR, \fB\--input-format\fR=\fIinput_format\fR
|
||||
Specify the input format.
|
||||
The following formats are supported:
|
||||
.PP
|
||||
.RS 12n
|
||||
.PD 0
|
||||
.TP 10n
|
||||
LDIF
|
||||
LDIF (LDAP Data Interchange Format) files can be exported from an LDAP
|
||||
server to convert security policies used by
|
||||
sudoers.ldap(@mansectform@).
|
||||
If a base DN (distinguished name) is specified, only sudoRole objects
|
||||
that match the base DN will be processed.
|
||||
Not all sudoOptions specified in a sudoRole can be translated from
|
||||
LDIF to sudoers format.
|
||||
.PD
|
||||
.TP 10n
|
||||
sudoers
|
||||
Traditional sudoers format.
|
||||
This is the default input format.
|
||||
.PD 0
|
||||
.PP
|
||||
.RE
|
||||
.PD
|
||||
.TP 12n
|
||||
\fB\-V\fR, \fB\--version\fR
|
||||
Print the
|
||||
\fBcvtsudoers\fR
|
||||
|
@@ -14,7 +14,7 @@
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd February 18, 2018
|
||||
.Dd February 22, 2018
|
||||
.Dt CVTSUDOERS @mansectsu@
|
||||
.Os Sudo @PACKAGE_VERSION@
|
||||
.Sh NAME
|
||||
@@ -24,21 +24,23 @@
|
||||
.Nm cvtsudoers
|
||||
.Op Fl ehV
|
||||
.Op Fl b Ar dn
|
||||
.Op Fl f Ar format
|
||||
.Op Fl i Ar input_format
|
||||
.Op Fl f Ar output_format
|
||||
.Op Fl o Ar output_file
|
||||
.Op Ar sudoers_file
|
||||
.Op Ar input_file
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
can be used to convert a policy file in
|
||||
can be used to convert between
|
||||
.Em sudoers
|
||||
format to other formats.
|
||||
security policy file formats.
|
||||
The default input format is sudoers.
|
||||
The default output format is LDIF.
|
||||
It is only possible to convert a
|
||||
.Em sudoers
|
||||
file that is syntactically correct.
|
||||
.Pp
|
||||
If no
|
||||
.Ar sudoers_file
|
||||
.Ar input_file
|
||||
is specified, or if it is
|
||||
.Ql - ,
|
||||
the policy is read from the standard input.
|
||||
@@ -59,7 +61,7 @@ environment variable will be used instead.
|
||||
Only necessary when converting to LDIF format.
|
||||
.It Fl e , Fl -expand-aliases
|
||||
Expand aliases in
|
||||
.Ar sudoers_file .
|
||||
.Ar input_file .
|
||||
Aliases are preserved by default when the output
|
||||
.Ar format
|
||||
is JSON or sudoers.
|
||||
@@ -108,6 +110,22 @@ is specified, or if it is
|
||||
the converted
|
||||
.Em sudoers
|
||||
policy will be written to the standard output.
|
||||
.It Fl i Ar input_format , Fl -input-format Ns = Ns Ar input_format
|
||||
Specify the input format.
|
||||
The following formats are supported:
|
||||
.Bl -tag -width 8n
|
||||
.It LDIF
|
||||
LDIF (LDAP Data Interchange Format) files can be exported from an LDAP
|
||||
server to convert security policies used by
|
||||
.Xr sudoers.ldap @mansectform@ .
|
||||
If a base DN (distinguished name) is specified, only sudoRole objects
|
||||
that match the base DN will be processed.
|
||||
Not all sudoOptions specified in a sudoRole can be translated from
|
||||
LDIF to sudoers format.
|
||||
.It sudoers
|
||||
Traditional sudoers format.
|
||||
This is the default input format.
|
||||
.El
|
||||
.It Fl V , -version
|
||||
Print the
|
||||
.Nm
|
||||
|
@@ -162,7 +162,7 @@ SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo \
|
||||
VISUDO_OBJS = editor.o find_path.o goodpath.o locale.o stubs.o sudo_printf.o visudo.o
|
||||
|
||||
CVTSUDOERS_OBJS = cvtsudoers.o cvtsudoers_json.o cvtsudoers_ldif.o \
|
||||
fmtsudoers.o locale.o stubs.o sudo_printf.o
|
||||
fmtsudoers.o locale.o stubs.o sudo_printf.o ldap_common.o
|
||||
|
||||
REPLAY_OBJS = getdate.o sudoreplay.o
|
||||
|
||||
@@ -735,9 +735,10 @@ cvtsudoers_ldif.o: $(srcdir)/cvtsudoers_ldif.c $(devdir)/def_data.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_nss.h \
|
||||
$(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
|
||||
$(top_builddir)/config.h $(top_builddir)/pathnames.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 \
|
||||
@@ -972,9 +973,9 @@ ldap.lo: $(srcdir)/ldap.c $(devdir)/def_data.h $(devdir)/gram.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_ldap.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
|
||||
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
|
||||
$(top_builddir)/pathnames.h
|
||||
$(srcdir)/sudo_ldap.h $(srcdir)/sudo_ldap_conf.h $(srcdir)/sudo_nss.h \
|
||||
$(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
|
||||
$(top_builddir)/config.h $(top_builddir)/pathnames.h
|
||||
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/ldap.c
|
||||
ldap_common.lo: $(srcdir)/ldap_common.c $(devdir)/def_data.h $(devdir)/gram.h \
|
||||
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
||||
@@ -982,21 +983,24 @@ ldap_common.lo: $(srcdir)/ldap_common.c $(devdir)/def_data.h $(devdir)/gram.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_ldap.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
|
||||
$(srcdir)/defaults.h $(srcdir)/interfaces.h \
|
||||
$(srcdir)/logging.h $(srcdir)/parse.h $(srcdir)/sudo_ldap.h \
|
||||
$(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
|
||||
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
|
||||
$(top_builddir)/pathnames.h
|
||||
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/ldap_common.c
|
||||
ldap_conf.lo: $(srcdir)/ldap_conf.c $(devdir)/def_data.h $(devdir)/gram.h \
|
||||
ldap_common.o: ldap_common.lo
|
||||
ldap_conf.lo: $(srcdir)/ldap_conf.c $(devdir)/def_data.h \
|
||||
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
||||
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
|
||||
$(incdir)/sudo_dso.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_ldap.h $(srcdir)/sudo_nss.h \
|
||||
$(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
|
||||
$(top_builddir)/config.h $(top_builddir)/pathnames.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_ldap.h $(srcdir)/sudo_ldap_conf.h \
|
||||
$(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
|
||||
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
|
||||
$(top_builddir)/pathnames.h
|
||||
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/ldap_conf.c
|
||||
linux_audit.lo: $(srcdir)/linux_audit.c $(devdir)/def_data.h \
|
||||
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
|
||||
|
@@ -47,8 +47,10 @@
|
||||
#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);
|
||||
|
||||
/*
|
||||
@@ -56,15 +58,13 @@ extern void get_hostname(void);
|
||||
*/
|
||||
struct sudo_user sudo_user;
|
||||
struct passwd *list_pw;
|
||||
static const char short_opts[] = "b:ef:ho:V";
|
||||
static const char short_opts[] = "b:ef:hi:o:V";
|
||||
static struct option long_opts[] = {
|
||||
{ "base", required_argument, NULL, 'b' },
|
||||
{ "expand-aliases", no_argument, NULL, 'e' },
|
||||
{ "format", required_argument, NULL, 'f' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
#ifdef notyet
|
||||
{ "input-format", required_argument, NULL, 'i' },
|
||||
#endif
|
||||
{ "output", required_argument, NULL, 'o' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ NULL, no_argument, NULL, '\0' },
|
||||
@@ -74,17 +74,19 @@ __dso_public int main(int argc, char *argv[]);
|
||||
static void help(void) __attribute__((__noreturn__));
|
||||
static void usage(int);
|
||||
|
||||
enum output_formats {
|
||||
output_json,
|
||||
output_ldif,
|
||||
output_sudoers
|
||||
enum sudoers_formats {
|
||||
format_json,
|
||||
format_ldif,
|
||||
format_sudoers
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int ch, exitcode = EXIT_FAILURE;
|
||||
enum output_formats output_format = output_ldif;
|
||||
enum sudoers_formats output_format = format_ldif;
|
||||
enum sudoers_formats input_format = format_sudoers;
|
||||
bool store_options = true;
|
||||
const char *input_file = "-";
|
||||
const char *output_file = "-";
|
||||
const char *sudoers_base = NULL;
|
||||
@@ -126,11 +128,14 @@ main(int argc, char *argv[])
|
||||
break;
|
||||
case 'f':
|
||||
if (strcasecmp(optarg, "json") == 0) {
|
||||
output_format = output_json;
|
||||
output_format = format_json;
|
||||
store_options = true;
|
||||
} else if (strcasecmp(optarg, "ldif") == 0) {
|
||||
output_format = output_ldif;
|
||||
output_format = format_ldif;
|
||||
store_options = true;
|
||||
} else if (strcasecmp(optarg, "sudoers") == 0) {
|
||||
output_format = output_sudoers;
|
||||
output_format = format_sudoers;
|
||||
store_options = false;
|
||||
} else {
|
||||
sudo_warnx("unsupported output format %s", optarg);
|
||||
usage(1);
|
||||
@@ -139,6 +144,16 @@ main(int argc, char *argv[])
|
||||
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);
|
||||
usage(1);
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
output_file = optarg;
|
||||
break;
|
||||
@@ -156,9 +171,12 @@ main(int argc, char *argv[])
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* If no base DN specified, check SUDOERS_BASE. */
|
||||
if (sudoers_base == NULL)
|
||||
sudoers_base = getenv("SUDOERS_BASE");
|
||||
|
||||
/* Input file (defaults to stdin). */
|
||||
if (argc > 0) {
|
||||
/* XXX - allow multiple input files? */
|
||||
if (argc > 1)
|
||||
usage(1);
|
||||
input_file = argv[0];
|
||||
@@ -189,6 +207,43 @@ main(int argc, char *argv[])
|
||||
if (!init_defaults())
|
||||
sudo_fatalx(U_("unable to initialize sudoers default values"));
|
||||
|
||||
switch (input_format) {
|
||||
case format_ldif:
|
||||
if (!parse_ldif(input_file, store_options, sudoers_base))
|
||||
goto done;
|
||||
break;
|
||||
case format_sudoers:
|
||||
if (!parse_sudoers(input_file))
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
sudo_fatalx("error: unhandled input %d", input_format);
|
||||
}
|
||||
|
||||
switch (output_format) {
|
||||
case format_json:
|
||||
exitcode = !convert_sudoers_json(output_file, expand_aliases);
|
||||
break;
|
||||
case format_ldif:
|
||||
exitcode = !convert_sudoers_ldif(output_file, sudoers_base);
|
||||
break;
|
||||
case format_sudoers:
|
||||
exitcode = !convert_sudoers_sudoers(output_file, expand_aliases);
|
||||
break;
|
||||
default:
|
||||
sudo_fatalx("error: unhandled output format %d", output_format);
|
||||
}
|
||||
|
||||
done:
|
||||
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
|
||||
return exitcode;
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_sudoers(const char *input_file)
|
||||
{
|
||||
debug_decl(parse_sudoers, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
/* Open sudoers file and parse it. */
|
||||
if (strcmp(input_file, "-") == 0) {
|
||||
sudoersin = stdin;
|
||||
@@ -209,26 +264,9 @@ main(int argc, char *argv[])
|
||||
errorfile, errorlineno);
|
||||
else if (errorfile != NULL)
|
||||
sudo_warnx(U_("parse error in %s\n"), errorfile);
|
||||
goto done;
|
||||
debug_return_bool(false);
|
||||
}
|
||||
|
||||
switch (output_format) {
|
||||
case output_json:
|
||||
exitcode = !convert_sudoers_json(output_file, expand_aliases);
|
||||
break;
|
||||
case output_ldif:
|
||||
exitcode = !convert_sudoers_ldif(output_file, sudoers_base);
|
||||
break;
|
||||
case output_sudoers:
|
||||
exitcode = !convert_sudoers_sudoers(output_file, expand_aliases);
|
||||
break;
|
||||
default:
|
||||
sudo_fatalx("error: unhandled output format %d", output_format);
|
||||
}
|
||||
|
||||
done:
|
||||
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
|
||||
return exitcode;
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
||||
FILE *
|
||||
@@ -403,7 +441,8 @@ static void
|
||||
usage(int fatal)
|
||||
{
|
||||
(void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehV] [-b dn] "
|
||||
"[-f format] [-o output_file] [sudoers_file]\n", getprogname());
|
||||
"[-f output_format] [-i input_format] [-o output_file] [input_file]\n",
|
||||
getprogname());
|
||||
if (fatal)
|
||||
exit(1);
|
||||
}
|
||||
|
@@ -773,10 +773,11 @@ print_aliases_json(FILE *fp, int indent, bool need_comma)
|
||||
*/
|
||||
static void
|
||||
print_cmndspec_json(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp,
|
||||
bool expand_aliases, int indent)
|
||||
struct defaults_list *options, bool expand_aliases, int indent)
|
||||
{
|
||||
struct cmndspec *next = *nextp;
|
||||
struct json_value value;
|
||||
struct defaults *def;
|
||||
struct member *m;
|
||||
struct tm *tp;
|
||||
bool last_one;
|
||||
@@ -811,18 +812,20 @@ print_cmndspec_json(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp,
|
||||
fprintf(fp, "%*s],\n", indent, "");
|
||||
}
|
||||
|
||||
/* Print tags */
|
||||
/* Print options and tags */
|
||||
if (cs->timeout > 0 || cs->notbefore != UNSPEC || cs->notafter != UNSPEC ||
|
||||
TAGS_SET(cs->tags)) {
|
||||
TAGS_SET(cs->tags) || !TAILQ_EMPTY(options)) {
|
||||
struct cmndtag tag = cs->tags;
|
||||
const char *prefix = "\n";
|
||||
|
||||
fprintf(fp, "%*s\"Options\": [\n", indent, "");
|
||||
fprintf(fp, "%*s\"Options\": [", indent, "");
|
||||
indent += 4;
|
||||
if (cs->timeout > 0) {
|
||||
value.type = JSON_NUMBER;
|
||||
value.u.number = cs->timeout;
|
||||
print_pair_json(fp, "{ ", "command_timeout", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "command_timeout", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
if (cs->notbefore != UNSPEC) {
|
||||
if ((tp = gmtime(&cs->notbefore)) == NULL) {
|
||||
@@ -833,9 +836,9 @@ print_cmndspec_json(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp,
|
||||
} else {
|
||||
value.type = JSON_STRING;
|
||||
value.u.string = timebuf;
|
||||
print_pair_json(fp, "{ ", "notbefore", &value,
|
||||
(TAGS_SET(tag) || cs->notafter != UNSPEC) ?
|
||||
" },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "notbefore", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -848,60 +851,83 @@ print_cmndspec_json(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp,
|
||||
} else {
|
||||
value.type = JSON_STRING;
|
||||
value.u.string = timebuf;
|
||||
print_pair_json(fp, "{ ", "notafter", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "notafter", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tag.nopasswd != UNSPEC) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = !tag.nopasswd;
|
||||
tag.nopasswd = UNSPEC;
|
||||
print_pair_json(fp, "{ ", "authenticate", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "authenticate", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
if (tag.noexec != UNSPEC) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = tag.noexec;
|
||||
tag.noexec = UNSPEC;
|
||||
print_pair_json(fp, "{ ", "noexec", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "noexec", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
if (tag.send_mail != UNSPEC) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = tag.send_mail;
|
||||
tag.send_mail = UNSPEC;
|
||||
print_pair_json(fp, "{ ", "send_mail", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "send_mail", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
if (tag.setenv != UNSPEC) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = tag.setenv;
|
||||
tag.setenv = UNSPEC;
|
||||
print_pair_json(fp, "{ ", "setenv", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "setenv", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
if (tag.follow != UNSPEC) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = tag.follow;
|
||||
tag.follow = UNSPEC;
|
||||
print_pair_json(fp, "{ ", "sudoedit_follow", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "sudoedit_follow", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
if (tag.log_input != UNSPEC) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = tag.log_input;
|
||||
tag.log_input = UNSPEC;
|
||||
print_pair_json(fp, "{ ", "log_input", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "log_input", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
if (tag.log_output != UNSPEC) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = tag.log_output;
|
||||
tag.log_output = UNSPEC;
|
||||
print_pair_json(fp, "{ ", "log_output", &value,
|
||||
TAGS_SET(tag) ? " },\n" : " }\n", indent);
|
||||
fputs(prefix, fp);
|
||||
print_pair_json(fp, "{ ", "log_output", &value, " }", indent);
|
||||
prefix = ",\n";
|
||||
}
|
||||
TAILQ_FOREACH(def, options, entries) {
|
||||
int type = get_defaults_type(def);
|
||||
if (type == -1) {
|
||||
sudo_warnx(U_("unknown defaults entry \"%s\""), def->var);
|
||||
/* XXX - just pass it through as a string anyway? */
|
||||
continue;
|
||||
}
|
||||
fputs(prefix, fp);
|
||||
if ((type & T_MASK) == T_FLAG || def->val == NULL) {
|
||||
value.type = JSON_BOOL;
|
||||
value.u.boolean = def->op;
|
||||
print_pair_json(fp, "{ ", def->var, &value, " }", indent);
|
||||
} else if ((type & T_MASK) == T_LIST) {
|
||||
print_defaults_list_json(fp, def, indent);
|
||||
} else {
|
||||
value.type = JSON_STRING;
|
||||
value.u.string = def->val;
|
||||
print_pair_json(fp, "{ ", def->var, &value, " }", indent);
|
||||
}
|
||||
prefix = ",\n";
|
||||
}
|
||||
putc('\n', fp);
|
||||
indent -= 4;
|
||||
fprintf(fp, "%*s],\n", indent, "");
|
||||
}
|
||||
@@ -1024,7 +1050,8 @@ print_userspec_json(FILE *fp, struct userspec *us, int indent, bool expand_alias
|
||||
fprintf(fp, "%*s\"Cmnd_Specs\": [\n", indent, "");
|
||||
indent += 4;
|
||||
TAILQ_FOREACH_SAFE(cs, &priv->cmndlist, entries, next) {
|
||||
print_cmndspec_json(fp, cs, &next, expand_aliases, indent);
|
||||
print_cmndspec_json(fp, cs, &next, &priv->defaults,
|
||||
expand_aliases, indent);
|
||||
}
|
||||
indent -= 4;
|
||||
fprintf(fp, "%*s]\n", indent, "");
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include "sudoers.h"
|
||||
#include "sudo_ldap.h"
|
||||
#include "parse.h"
|
||||
#include "redblack.h"
|
||||
#include <gram.h>
|
||||
@@ -60,13 +61,39 @@ seen_user_free(void *v)
|
||||
free(su);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print sudoOptions from a defaults_list.
|
||||
*/
|
||||
static bool
|
||||
print_options_ldif(FILE *fp, struct defaults_list *options)
|
||||
{
|
||||
struct defaults *opt;
|
||||
debug_decl(print_options_ldif, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
TAILQ_FOREACH(opt, options, entries) {
|
||||
if (opt->type != DEFAULTS)
|
||||
continue; /* don't support bound defaults */
|
||||
|
||||
if (opt->val != NULL) {
|
||||
/* There is no need to double quote values here. */
|
||||
fprintf(fp, "sudoOption: %s%s%s\n", opt->var,
|
||||
opt->op == '+' ? "+=" : opt->op == '-' ? "-=" : "=", opt->val);
|
||||
} else {
|
||||
/* Boolean flag. */
|
||||
fprintf(fp, "sudoOption: %s%s\n", opt->op == false ? "!" : "",
|
||||
opt->var);
|
||||
}
|
||||
}
|
||||
|
||||
debug_return_bool(!ferror(fp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Print global Defaults in a single sudoRole object.
|
||||
*/
|
||||
static bool
|
||||
print_global_defaults_ldif(FILE *fp, const char *base)
|
||||
{
|
||||
struct defaults *def;
|
||||
debug_decl(print_global_defaults_ldif, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
if (TAILQ_EMPTY(&defaults))
|
||||
@@ -78,20 +105,7 @@ print_global_defaults_ldif(FILE *fp, const char *base)
|
||||
fputs("cn: defaults\n", fp);
|
||||
fputs("description: Default sudoOption's go here\n", fp);
|
||||
|
||||
TAILQ_FOREACH(def, &defaults, entries) {
|
||||
if (def->type != DEFAULTS)
|
||||
continue; /* only want global defaults */
|
||||
|
||||
if (def->val != NULL) {
|
||||
/* There is no need to double quote values here. */
|
||||
fprintf(fp, "sudoOption: %s%s%s\n", def->var,
|
||||
def->op == '+' ? "+=" : def->op == '-' ? "-=" : "=", def->val);
|
||||
} else {
|
||||
/* Boolean flag. */
|
||||
fprintf(fp, "sudoOption: %s%s\n", def->op == false ? "!" : "",
|
||||
def->var);
|
||||
}
|
||||
}
|
||||
print_options_ldif(fp, &defaults);
|
||||
putc('\n', fp);
|
||||
|
||||
debug_return_bool(!ferror(fp));
|
||||
@@ -172,7 +186,7 @@ print_member_ldif(FILE *fp, char *name, int type, bool negated,
|
||||
* merge adjacent entries that are identical in all but the command.
|
||||
*/
|
||||
static void
|
||||
print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp)
|
||||
print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp, struct defaults_list *options)
|
||||
{
|
||||
struct cmndspec *next = *nextp;
|
||||
struct member *m;
|
||||
@@ -221,13 +235,15 @@ print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp)
|
||||
}
|
||||
}
|
||||
|
||||
/* Print tags as sudoOption attributes */
|
||||
if (cs->timeout > 0 || TAGS_SET(cs->tags)) {
|
||||
struct cmndtag tag = cs->tags;
|
||||
|
||||
/* Print timeout as a sudoOption. */
|
||||
if (cs->timeout > 0) {
|
||||
fprintf(fp, "sudoOption: command_timeout=%d\n", cs->timeout);
|
||||
}
|
||||
|
||||
/* Print tags as sudoOption attributes */
|
||||
if (TAGS_SET(cs->tags)) {
|
||||
struct cmndtag tag = cs->tags;
|
||||
|
||||
if (tag.nopasswd != UNSPEC) {
|
||||
fprintf(fp, "sudoOption: %sauthenticate\n", tag.nopasswd ? "!" : "");
|
||||
}
|
||||
@@ -256,6 +272,7 @@ print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp)
|
||||
fprintf(fp, "sudoOption: %slog_output\n", tag.log_output ? "" : "!");
|
||||
}
|
||||
}
|
||||
print_options_ldif(fp, options);
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
/* Print SELinux role/type */
|
||||
@@ -426,7 +443,7 @@ print_userspec_ldif(FILE *fp, struct userspec *us, const char *base)
|
||||
HOSTALIAS, "sudoHost");
|
||||
}
|
||||
|
||||
print_cmndspec_ldif(fp, cs, &next);
|
||||
print_cmndspec_ldif(fp, cs, &next, &priv->defaults);
|
||||
|
||||
fprintf(fp, "sudoOrder: %d\n\n", ++sudo_order);
|
||||
}
|
||||
@@ -462,8 +479,6 @@ convert_sudoers_ldif(const char *output_file, const char *base)
|
||||
debug_decl(convert_sudoers_ldif, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
if (base == NULL) {
|
||||
base = getenv("SUDOERS_BASE");
|
||||
if (base == NULL)
|
||||
sudo_fatalx(U_("the SUDOERS_BASE environment variable is not set and the -b option was not specified."));
|
||||
}
|
||||
|
||||
@@ -495,3 +510,451 @@ convert_sudoers_ldif(const char *output_file, const char *base)
|
||||
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
struct ldif_string {
|
||||
STAILQ_ENTRY(ldif_string) entries;
|
||||
char *str;
|
||||
};
|
||||
STAILQ_HEAD(ldif_str_list, ldif_string);
|
||||
|
||||
struct sudo_role {
|
||||
STAILQ_ENTRY(sudo_role) entries;
|
||||
char *cn;
|
||||
char *notbefore;
|
||||
char *notafter;
|
||||
double order;
|
||||
struct ldif_str_list cmnds;
|
||||
struct ldif_str_list hosts;
|
||||
struct ldif_str_list users;
|
||||
struct ldif_str_list runasusers;
|
||||
struct ldif_str_list runasgroups;
|
||||
struct ldif_str_list options;
|
||||
};
|
||||
STAILQ_HEAD(sudo_role_list, sudo_role);
|
||||
|
||||
static struct sudo_role *
|
||||
sudo_role_alloc(void)
|
||||
{
|
||||
struct sudo_role *role;
|
||||
debug_decl(sudo_role_alloc, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
role = calloc(1, sizeof(*role));
|
||||
if (role != NULL) {
|
||||
STAILQ_INIT(&role->cmnds);
|
||||
STAILQ_INIT(&role->hosts);
|
||||
STAILQ_INIT(&role->users);
|
||||
STAILQ_INIT(&role->runasusers);
|
||||
STAILQ_INIT(&role->runasgroups);
|
||||
STAILQ_INIT(&role->options);
|
||||
}
|
||||
|
||||
debug_return_ptr(role);
|
||||
}
|
||||
|
||||
static struct ldif_string *
|
||||
ldif_string_alloc(const char *s)
|
||||
{
|
||||
struct ldif_string *ls;
|
||||
debug_decl(ldif_string_alloc, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
if ((ls = malloc(sizeof(*ls))) != NULL) {
|
||||
if ((ls->str = strdup(s)) == NULL) {
|
||||
free(ls);
|
||||
ls = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
debug_return_ptr(ls);
|
||||
}
|
||||
|
||||
static void
|
||||
ldif_string_free(struct ldif_string *ls)
|
||||
{
|
||||
free(ls->str);
|
||||
free(ls);
|
||||
}
|
||||
|
||||
static void
|
||||
str_list_free(struct ldif_str_list *strlist)
|
||||
{
|
||||
struct ldif_string *first;
|
||||
debug_decl(str_list_free, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
while ((first = STAILQ_FIRST(strlist)) != NULL) {
|
||||
STAILQ_REMOVE_HEAD(strlist, entries);
|
||||
ldif_string_free(first);
|
||||
}
|
||||
debug_return;
|
||||
}
|
||||
|
||||
static void
|
||||
sudo_role_free(struct sudo_role *role)
|
||||
{
|
||||
debug_decl(sudo_role_free, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
if (role != NULL) {
|
||||
free(role->cn);
|
||||
free(role->notbefore);
|
||||
free(role->notafter);
|
||||
str_list_free(&role->cmnds);
|
||||
str_list_free(&role->hosts);
|
||||
str_list_free(&role->users);
|
||||
str_list_free(&role->runasusers);
|
||||
str_list_free(&role->runasgroups);
|
||||
str_list_free(&role->options);
|
||||
}
|
||||
|
||||
debug_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a struct ldif_string, store str in it and
|
||||
* insert into the specified strlist.
|
||||
*/
|
||||
static void
|
||||
ldif_store_string(const char *str, struct ldif_str_list *strlist)
|
||||
{
|
||||
struct ldif_string *ls;
|
||||
debug_decl(ldif_store_string, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
while (isblank((unsigned char)*str))
|
||||
str++;
|
||||
if ((ls = ldif_string_alloc(str)) == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
STAILQ_INSERT_TAIL(strlist, ls, entries);
|
||||
|
||||
debug_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterator for sudo_ldap_role_to_priv().
|
||||
* Takes a pointer to a struct ldif_string *.
|
||||
* Returns the string or NULL if we've reached the end.
|
||||
*/
|
||||
static char *
|
||||
ldif_string_iter(void **vp)
|
||||
{
|
||||
struct ldif_string *ls = *vp;
|
||||
|
||||
if (ls == NULL)
|
||||
return NULL;
|
||||
|
||||
*vp = STAILQ_NEXT(ls, entries);
|
||||
|
||||
return ls->str;
|
||||
}
|
||||
|
||||
static int
|
||||
role_order_cmp(const void *va, const void *vb)
|
||||
{
|
||||
const struct sudo_role *a = *(const struct sudo_role **)va;
|
||||
const struct sudo_role *b = *(const struct sudo_role **)vb;
|
||||
debug_decl(role_order_cmp, SUDOERS_DEBUG_LDAP)
|
||||
|
||||
debug_return_int(b->order < a->order ? -1 :
|
||||
(b->order > a->order ? 1 : 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse list of sudoOption and store in global defaults list.
|
||||
*/
|
||||
static void
|
||||
ldif_store_options(struct ldif_str_list *options)
|
||||
{
|
||||
struct defaults *d;
|
||||
struct ldif_string *ls;
|
||||
char *var, *val;
|
||||
debug_decl(ldif_store_options, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
STAILQ_FOREACH(ls, options, entries) {
|
||||
if ((d = calloc(1, sizeof(*d))) == NULL ||
|
||||
(d->binding = malloc(sizeof(*d->binding))) == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
TAILQ_INIT(d->binding);
|
||||
d->type = DEFAULTS;
|
||||
d->op = sudo_ldap_parse_option(ls->str, &var, &val);
|
||||
d->var = strdup(var);
|
||||
d->val = strdup(val);
|
||||
if (d->var == NULL || d->val == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&defaults, d, entries);
|
||||
}
|
||||
debug_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a sudoers file in LDIF format
|
||||
* https://tools.ietf.org/html/rfc2849
|
||||
*
|
||||
* TODO: order negated entries at the end (different semantics)
|
||||
* include the cn it came from in comments for each new privilege
|
||||
* create aliases on the fly for multiple users/hosts?
|
||||
*/
|
||||
bool
|
||||
parse_ldif(const char *input_file, bool store_options, const char *base)
|
||||
{
|
||||
struct sudo_role_list roles = STAILQ_HEAD_INITIALIZER(roles);
|
||||
struct sudo_role **role_array, *role = NULL;
|
||||
unsigned int n, numroles = 0;
|
||||
bool in_role = false;
|
||||
size_t linesize = 0;
|
||||
char *line = NULL;
|
||||
char *savedline = NULL;
|
||||
bool mismatch = false;
|
||||
ssize_t len, savedlen = 0;
|
||||
FILE *fp;
|
||||
char *cp;
|
||||
int ch;
|
||||
debug_decl(parse_ldif, SUDOERS_DEBUG_UTIL)
|
||||
|
||||
/* Open LDIF file and parse it. */
|
||||
if (strcmp(input_file, "-") == 0) {
|
||||
fp = stdin;
|
||||
input_file = "stdin";
|
||||
} else if ((fp = fopen(input_file, "r")) == NULL)
|
||||
sudo_fatal(U_("unable to open %s"), input_file);
|
||||
init_parser(input_file, false);
|
||||
|
||||
/* Read through input, parsing into sudo_roles and global defaults. */
|
||||
for (;;) {
|
||||
len = getline(&line, &linesize, fp);
|
||||
|
||||
/* Trim trailing return or newline. */
|
||||
while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n'))
|
||||
line[--len] = '\0';
|
||||
|
||||
/* Blank line or EOF terminates an entry. */
|
||||
if (len <= 0) {
|
||||
if (in_role) {
|
||||
if (role->cn != NULL && strcmp(role->cn, "defaults") == 0) {
|
||||
ldif_store_options(&role->options);
|
||||
sudo_role_free(role);
|
||||
} else if (STAILQ_EMPTY(&role->users) ||
|
||||
STAILQ_EMPTY(&role->hosts) || STAILQ_EMPTY(&role->cmnds)) {
|
||||
/* Incomplete role. */
|
||||
sudo_warnx(U_("ignoring incomplete sudoRole: cn: %s"),
|
||||
role->cn ? role->cn : "UNKNOWN");
|
||||
sudo_role_free(role);
|
||||
} else {
|
||||
/* Store finished role. */
|
||||
STAILQ_INSERT_TAIL(&roles, role, entries);
|
||||
numroles++;
|
||||
}
|
||||
role = NULL;
|
||||
in_role = false;
|
||||
}
|
||||
if (len == -1)
|
||||
break;
|
||||
mismatch = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (savedline != NULL) {
|
||||
char *tmp;
|
||||
|
||||
/* Append to saved line. */
|
||||
linesize = savedlen + len + 1;
|
||||
if ((tmp = realloc(savedline, linesize)) == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
memcpy(tmp + savedlen, line, len + 1);
|
||||
free(line);
|
||||
line = tmp;
|
||||
savedline = NULL;
|
||||
} else {
|
||||
/* Skip comment lines or records that don't match the base. */
|
||||
if (*line == '#' || mismatch)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for folded line */
|
||||
if ((ch = getc(fp)) == ' ') {
|
||||
/* folded line, append to the saved portion. */
|
||||
savedlen = len;
|
||||
savedline = line;
|
||||
line = NULL;
|
||||
linesize = 0;
|
||||
continue;
|
||||
} else {
|
||||
/* not folded, push back ch */
|
||||
ungetc(ch, fp);
|
||||
}
|
||||
|
||||
/* Allocate new role as needed. */
|
||||
if (role == NULL) {
|
||||
if ((role = sudo_role_alloc()) == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse dn and objectClass. */
|
||||
if (strncasecmp(line, "dn:", 3) == 0) {
|
||||
/* Compare dn to base, if specified. */
|
||||
if (base != NULL) {
|
||||
cp = line + 3;
|
||||
while (isblank((unsigned char)*cp))
|
||||
cp++;
|
||||
if (strncasecmp(cp, "cn=", 3) == 0) {
|
||||
cp += 3;
|
||||
/* XXX - handle escaped ','? */
|
||||
while (*cp != ',' && *cp != '\0')
|
||||
cp++;
|
||||
if (*cp == ',')
|
||||
cp++;
|
||||
}
|
||||
if (strcasecmp(cp, base) != 0) {
|
||||
/* Doesn't match base, skip the rest of it. */
|
||||
mismatch = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (strncmp(line, "objectClass:", 12) == 0) {
|
||||
cp = line + 12;
|
||||
while (isblank((unsigned char)*cp))
|
||||
cp++;
|
||||
if (strcmp(cp, "sudoRole") == 0)
|
||||
in_role = true;
|
||||
}
|
||||
|
||||
/* Not in a sudoRole, keep reading. */
|
||||
if (!in_role)
|
||||
continue;
|
||||
|
||||
/* Part of a sudoRole, parse it. */
|
||||
if (strncmp(line, "cn:", 3) == 0) {
|
||||
cp = line + 3;
|
||||
while (isblank((unsigned char)*cp))
|
||||
cp++;
|
||||
free(role->cn);
|
||||
/* XXX - unescape chars? */
|
||||
role->cn = strdup(cp);
|
||||
if (role->cn == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
} else if (strncmp(line, "sudoUser:", 9) == 0) {
|
||||
ldif_store_string(line + 9, &role->users);
|
||||
} else if (strncmp(line, "sudoHost:", 9) == 0) {
|
||||
ldif_store_string(line + 9, &role->hosts);
|
||||
} else if (strncmp(line, "sudoRunAs:", 10) == 0) {
|
||||
ldif_store_string(line + 10, &role->runasusers);
|
||||
} else if (strncmp(line, "sudoRunAsUser:", 14) == 0) {
|
||||
ldif_store_string(line + 14, &role->runasusers);
|
||||
} else if (strncmp(line, "sudoRunAsGroup:", 15) == 0) {
|
||||
ldif_store_string(line + 15, &role->runasgroups);
|
||||
} else if (strncmp(line, "sudoCommand:", 12) == 0) {
|
||||
ldif_store_string(line + 12, &role->cmnds);
|
||||
} else if (strncmp(line, "sudoOption:", 11) == 0) {
|
||||
ldif_store_string(line + 11, &role->options);
|
||||
} else if (strncmp(line, "sudoNotBefore:", 14) == 0) {
|
||||
cp = line + 14;
|
||||
while (isblank((unsigned char)*cp))
|
||||
cp++;
|
||||
free(role->notbefore);
|
||||
role->notbefore = strdup(cp);
|
||||
if (role->notbefore == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
} else if (strncmp(line, "sudoNotAfter:", 13) == 0) {
|
||||
cp = line + 13;
|
||||
while (isblank((unsigned char)*cp))
|
||||
cp++;
|
||||
free(role->notafter);
|
||||
role->notafter = strdup(cp);
|
||||
if (role->notafter == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert from list of roles to array and sort by order. */
|
||||
role_array = reallocarray(NULL, numroles + 1, sizeof(*role_array));
|
||||
for (n = 0; (role = STAILQ_FIRST(&roles)) != NULL; n++) {
|
||||
STAILQ_REMOVE_HEAD(&roles, entries);
|
||||
role_array[n] = role;
|
||||
}
|
||||
role_array[n] = NULL;
|
||||
qsort(role_array, numroles, sizeof(*role_array), role_order_cmp);
|
||||
|
||||
/*
|
||||
* Iterate over roles in sorted order, using sudo_ldap_role_to_priv()
|
||||
* to convert to privilege and store in userspecs.
|
||||
* TODO: merge multiple users with the same sudoOrder?
|
||||
* TODO: use cn to create a UserAlias if multiple users in it?
|
||||
*/
|
||||
for (n = 0; n < numroles; n++) {
|
||||
struct privilege *priv;
|
||||
struct ldif_string *ls;
|
||||
struct userspec *us;
|
||||
struct member *m;
|
||||
|
||||
/* Allocate a new userspec and fill in the user list. */
|
||||
if ((us = calloc(1, sizeof(*us))) == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
TAILQ_INIT(&us->privileges);
|
||||
TAILQ_INIT(&us->users);
|
||||
|
||||
role = role_array[n];
|
||||
STAILQ_FOREACH(ls, &role->users, entries) {
|
||||
char *user = ls->str;
|
||||
|
||||
if ((m = calloc(1, sizeof(*m))) == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
m->negated = sudo_ldap_is_negated(&user);
|
||||
m->name = strdup(user);
|
||||
if (m->name == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
if (strcmp(user, "ALL") == 0) {
|
||||
m->type = ALL;
|
||||
} else if (*user == '+') {
|
||||
m->type = NETGROUP;
|
||||
} else if (*user == '%') {
|
||||
m->type = USERGROUP;
|
||||
} else {
|
||||
m->type = WORD;
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&us->users, m, entries);
|
||||
}
|
||||
|
||||
/* Convert role to sudoers privilege. */
|
||||
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,
|
||||
ldif_string_iter);
|
||||
if (priv == NULL) {
|
||||
sudo_fatalx(U_("%s: %s"), __func__,
|
||||
U_("unable to allocate memory"));
|
||||
}
|
||||
TAILQ_INSERT_TAIL(&us->privileges, priv, entries);
|
||||
|
||||
/* Add finished userspec to the list. */
|
||||
TAILQ_INSERT_TAIL(&userspecs, us, entries);
|
||||
}
|
||||
|
||||
/* Clean up. */
|
||||
for (n = 0; n < numroles; n++)
|
||||
sudo_role_free(role_array[n]);
|
||||
free(role_array);
|
||||
|
||||
if (fp != stdin)
|
||||
fclose(fp);
|
||||
|
||||
debug_return_bool(true);
|
||||
}
|
||||
|
@@ -1556,9 +1556,10 @@ ldap_to_sudoers(LDAP *ld, struct ldap_result *lres)
|
||||
/* Parse sudoOptions. */
|
||||
opts = ldap_get_values_len(ld, entry, "sudoOption");
|
||||
|
||||
priv = sudo_ldap_role_to_priv(cn, runasusers, runasgroups,
|
||||
priv = sudo_ldap_role_to_priv(cn, NULL, runasusers, runasgroups,
|
||||
cmnds, opts, notbefore ? notbefore[0]->bv_val : NULL,
|
||||
notafter ? notafter[0]->bv_val : NULL, berval_iter);
|
||||
notafter ? notafter[0]->bv_val : NULL, false, long_list,
|
||||
berval_iter);
|
||||
|
||||
/* Cleanup */
|
||||
if (cn != NULL)
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef HAVE_STRING_H
|
||||
@@ -28,12 +29,11 @@
|
||||
# include <strings.h>
|
||||
#endif /* HAVE_STRINGS_H */
|
||||
#include <ctype.h>
|
||||
#ifdef HAVE_LBER_H
|
||||
# include <lber.h>
|
||||
#endif
|
||||
#include <ldap.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "sudoers.h"
|
||||
#include "interfaces.h"
|
||||
#include "parse.h"
|
||||
#include "gram.h"
|
||||
#include "sudo_lbuf.h"
|
||||
@@ -112,7 +112,7 @@ sudo_ldap_parse_option(char *optstr, char **varp, char **valp)
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array to a member list.
|
||||
* Convert an array of user/group names to a member list.
|
||||
* The caller is responsible for freeing the returned struct member_list.
|
||||
*/
|
||||
static struct member_list *
|
||||
@@ -180,14 +180,77 @@ bad:
|
||||
debug_return_ptr(NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
is_address(char *host)
|
||||
{
|
||||
union sudo_in_addr_un addr;
|
||||
bool ret = false;
|
||||
char *slash;
|
||||
debug_decl(is_address, SUDOERS_DEBUG_LDAP)
|
||||
|
||||
/* Check for mask, not currently parsed. */
|
||||
if ((slash = strchr(host, '/')) != NULL)
|
||||
*slash = '\0';
|
||||
|
||||
if (inet_pton(AF_INET, host, &addr.ip4) == 1)
|
||||
ret = true;
|
||||
#ifdef HAVE_STRUCT_IN6_ADDR
|
||||
else if (inet_pton(AF_INET6, host, &addr.ip6) == 1)
|
||||
ret = true;
|
||||
#endif
|
||||
|
||||
if (slash != NULL)
|
||||
*slash = '/';
|
||||
|
||||
debug_return_bool(ret);
|
||||
}
|
||||
|
||||
static struct member *
|
||||
host_to_member(char *host)
|
||||
{
|
||||
struct member *m;
|
||||
debug_decl(host_to_member, SUDOERS_DEBUG_LDAP)
|
||||
|
||||
if ((m = calloc(1, sizeof(*m))) == NULL)
|
||||
goto oom;
|
||||
m->negated = sudo_ldap_is_negated(&host);
|
||||
m->name = strdup(host);
|
||||
if (m->name == NULL)
|
||||
goto oom;
|
||||
switch (*host) {
|
||||
case '+':
|
||||
m->type = NETGROUP;
|
||||
break;
|
||||
case 'A':
|
||||
if (strcmp(host, "ALL") == 0) {
|
||||
m->type = ALL;
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
if (is_address(host)) {
|
||||
m->type = NTWKADDR;
|
||||
} else {
|
||||
m->type = WORD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
debug_return_ptr(m);
|
||||
oom:
|
||||
free(m);
|
||||
debug_return_ptr(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an LDAP sudoRole to a sudoers privilege.
|
||||
* Pass in struct berval ** for LDAP or char *** for SSSD.
|
||||
*/
|
||||
struct privilege *
|
||||
sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups,
|
||||
void *cmnds, void *opts, const char *notbefore,
|
||||
const char *notafter, sudo_ldap_iter_t iter)
|
||||
sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers,
|
||||
void *runasgroups, void *cmnds, void *opts, const char *notbefore,
|
||||
const char *notafter, bool warnings, bool store_options,
|
||||
sudo_ldap_iter_t iter)
|
||||
{
|
||||
struct cmndspec *cmndspec = NULL;
|
||||
struct cmndspec *prev_cmndspec = NULL;
|
||||
@@ -207,17 +270,27 @@ sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups,
|
||||
if (priv->ldap_role == NULL)
|
||||
goto oom;
|
||||
|
||||
if (hosts == NULL) {
|
||||
/* The host has already matched, use ALL as wildcard. */
|
||||
if ((m = calloc(1, sizeof(*m))) == NULL)
|
||||
goto oom;
|
||||
m->type = ALL;
|
||||
TAILQ_INSERT_TAIL(&priv->hostlist, m, entries);
|
||||
} else {
|
||||
char *host;
|
||||
while ((host = iter(&hosts)) != NULL) {
|
||||
if ((m = host_to_member(host)) == NULL)
|
||||
goto oom;
|
||||
TAILQ_INSERT_TAIL(&priv->hostlist, m, entries);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse sudoCommands and add to cmndlist.
|
||||
*/
|
||||
while ((cmnd = iter(&cmnds)) != NULL) {
|
||||
char *args;
|
||||
struct sudo_digest digest;
|
||||
|
||||
/* Allocate storage upfront. */
|
||||
cmndspec = calloc(1, sizeof(*cmndspec));
|
||||
@@ -237,23 +310,30 @@ sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups,
|
||||
cmndspec->notafter = UNSPEC;
|
||||
cmndspec->timeout = UNSPEC;
|
||||
|
||||
/* Fill in command. */
|
||||
/* Fill in member. */
|
||||
m->type = COMMAND;
|
||||
m->negated = sudo_ldap_is_negated(&cmnd);
|
||||
m->name = (char *)c;
|
||||
|
||||
/* Fill in command with optional digest. */
|
||||
if (sudo_ldap_extract_digest(&cmnd, &digest) != NULL) {
|
||||
if ((c->digest = malloc(sizeof(*c->digest))) == NULL) {
|
||||
free_member(m);
|
||||
goto oom;
|
||||
}
|
||||
*c->digest = digest;
|
||||
}
|
||||
if ((args = strpbrk(cmnd, " \t")) != NULL) {
|
||||
*args++ = '\0';
|
||||
if ((c->args = strdup(args)) == NULL) {
|
||||
free(c);
|
||||
free(m);
|
||||
free_member(m);
|
||||
goto oom;
|
||||
}
|
||||
}
|
||||
if ((c->cmnd = strdup(cmnd)) == NULL) {
|
||||
free(c->args);
|
||||
free(c);
|
||||
free(m);
|
||||
free_member(m);
|
||||
goto oom;
|
||||
}
|
||||
m->type = COMMAND;
|
||||
m->name = (char *)c;
|
||||
cmndspec->cmnd = m;
|
||||
|
||||
if (prev_cmndspec != NULL) {
|
||||
@@ -322,10 +402,11 @@ sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups,
|
||||
goto oom;
|
||||
}
|
||||
#endif /* HAVE_PRIV_SET */
|
||||
} else if (long_list) {
|
||||
} else if (store_options) {
|
||||
struct defaults *def = calloc(1, sizeof(*def));
|
||||
if (def == NULL)
|
||||
goto oom;
|
||||
def->type = DEFAULTS;
|
||||
def->op = op;
|
||||
if ((def->var = strdup(var)) == NULL) {
|
||||
free(def);
|
||||
@@ -341,8 +422,9 @@ sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups,
|
||||
TAILQ_INSERT_TAIL(&priv->defaults, def, entries);
|
||||
} else {
|
||||
/* Convert to tags. */
|
||||
if (op != true && op != false)
|
||||
continue;
|
||||
bool handled = true;
|
||||
|
||||
if (op == true || op == false) {
|
||||
if (strcmp(var, "authenticate") == 0) {
|
||||
cmndspec->tags.nopasswd = op == false;
|
||||
} else if (strcmp(var, "sudoedit_follow") == 0) {
|
||||
@@ -354,6 +436,19 @@ sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups,
|
||||
} else if (strcmp(var, "mail_all_cmnds") == 0 ||
|
||||
strcmp(var, "mail_always") == 0) {
|
||||
cmndspec->tags.send_mail = op == true;
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
if (!handled && warnings) {
|
||||
if (val != NULL) {
|
||||
sudo_warnx(U_("unable to convert sudoOption: %s%s%s"), var, op == '+' ? "+=" : op == '-' ? "-=" : "=", val);
|
||||
} else {
|
||||
sudo_warnx(U_("unable to convert sudoOption: %s%s%s"), op == false ? "!" : "", var, "");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1476,9 +1476,9 @@ sss_to_sudoers(struct sudo_sss_handle *handle, struct sss_sudo_result *sss_resul
|
||||
/* Parse sudoOptions. */
|
||||
handle->fn_get_values(rule, "sudoOption", &opts);
|
||||
|
||||
priv = sudo_ldap_role_to_priv(cn, runasusers, runasgroups, cmnds, opts,
|
||||
notbefore ? notbefore[0] : NULL, notafter ? notafter[0] : NULL,
|
||||
val_array_iter);
|
||||
priv = sudo_ldap_role_to_priv(cn, NULL, runasusers, runasgroups,
|
||||
cmnds, opts, notbefore ? notbefore[0] : NULL,
|
||||
notafter ? notafter[0] : NULL, false, long_list, val_array_iter);
|
||||
|
||||
/* Cleanup */
|
||||
if (cn_array != NULL)
|
||||
|
@@ -23,7 +23,7 @@ typedef char * (*sudo_ldap_iter_t)(void **);
|
||||
/* ldap_common.c */
|
||||
bool sudo_ldap_is_negated(char **valp);
|
||||
int sudo_ldap_parse_option(char *optstr, char **varp, char **valp);
|
||||
struct privilege *sudo_ldap_role_to_priv(const char *cn, void *runasusers, void *runasgroups, void *cmnds, void *opts, const char *notbefore, const char *notafter, sudo_ldap_iter_t iter);
|
||||
struct privilege *sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, void *runasgroups, void *cmnds, void *opts, const char *notbefore, const char *notafter, bool warnings, bool store_options, sudo_ldap_iter_t iter);
|
||||
struct sudo_digest *sudo_ldap_extract_digest(char **cmnd, struct sudo_digest *digest);
|
||||
|
||||
#endif /* SUDOERS_LDAP_H */
|
||||
|
Reference in New Issue
Block a user