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:
Todd C. Miller
2018-02-22 09:53:12 -07:00
parent 8b22ed7837
commit ceea24b965
11 changed files with 858 additions and 167 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 */