c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs - convert between sudoers file formats
S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS
- c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs [-\b-e\beh\bhV\bV] [-\b-b\bb _\bd_\bn] [-\b-f\bf _\bf_\bo_\br_\bm_\ba_\bt] [-\b-o\bo _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be] [_\bs_\bu_\bd_\bo_\be_\br_\bs_\b__\bf_\bi_\bl_\be]
+ c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs [-\b-e\beh\bhV\bV] [-\b-b\bb _\bd_\bn] [-\b-i\bi _\bi_\bn_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt] [-\b-f\bf _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt]
+ [-\b-o\bo _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be] [_\bi_\bn_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be]
D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN
- c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs can be used to convert a policy file in _\bs_\bu_\bd_\bo_\be_\br_\bs format to
- other formats. The default output format is LDIF. It is only possible
- to convert a _\bs_\bu_\bd_\bo_\be_\br_\bs file that is syntactically correct.
+ c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs can be used to convert between _\bs_\bu_\bd_\bo_\be_\br_\bs security policy file
+ formats. The default input format is sudoers. The default output format
+ is LDIF. It is only possible to convert a _\bs_\bu_\bd_\bo_\be_\br_\bs file that is
+ syntactically correct.
- If no _\bs_\bu_\bd_\bo_\be_\br_\bs_\b__\bf_\bi_\bl_\be is specified, or if it is `-', the policy is read from
+ If no _\bi_\bn_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be is specified, or if it is `-', the policy is read from
the standard input. By default, the result is written to the standard
output.
when converting to LDIF format.
-\b-e\be, -\b--\b-e\bex\bxp\bpa\ban\bnd\bd-\b-a\bal\bli\bia\bas\bse\bes\bs
- Expand aliases in _\bs_\bu_\bd_\bo_\be_\br_\bs_\b__\bf_\bi_\bl_\be. Aliases are preserved by
+ Expand aliases in _\bi_\bn_\bp_\bu_\bt_\b__\bf_\bi_\bl_\be. Aliases are preserved by
default when the output _\bf_\bo_\br_\bm_\ba_\bt is JSON or sudoers.
-\b-f\bf _\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt, -\b--\b-f\bfo\bor\brm\bma\bat\bt=_\bo_\bu_\bt_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt
is specified, or if it is `-', the converted _\bs_\bu_\bd_\bo_\be_\br_\bs policy
will be written to the standard output.
+ -\b-i\bi _\bi_\bn_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt, -\b--\b-i\bin\bnp\bpu\but\bt-\b-f\bfo\bor\brm\bma\bat\bt=_\bi_\bn_\bp_\bu_\bt_\b__\bf_\bo_\br_\bm_\ba_\bt
+ 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.
+
-\b-V\bV, -\b--\b-v\bve\ber\brs\bsi\bio\bon\bn
Print the c\bcv\bvt\bts\bsu\bud\bdo\boe\ber\brs\bs and _\bs_\bu_\bd_\bo_\be_\br_\bs grammar versions and exit.
file distributed with s\bsu\bud\bdo\bo 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
.\" 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"
\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.
.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.
\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
.\" 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
.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.
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.
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
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
$(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 \
$(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 \
$(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 \
#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);
/*
*/
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' },
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;
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);
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;
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];
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;
errorfile, errorlineno);
else if (errorfile != NULL)
sudo_warnx(U_("parse error in %s\n"), errorfile);
- goto done;
- }
-
- 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);
+ debug_return_bool(false);
}
-
-done:
- sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
- return exitcode;
+ debug_return_bool(true);
}
FILE *
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);
}
*/
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;
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) {
} 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";
}
}
}
} 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, "");
}
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, "");
#include <ctype.h>
#include "sudoers.h"
+#include "sudo_ldap.h"
#include "parse.h"
#include "redblack.h"
#include <gram.h>
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))
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));
* 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;
}
}
+ /* Print timeout as a sudoOption. */
+ if (cs->timeout > 0) {
+ fprintf(fp, "sudoOption: command_timeout=%d\n", cs->timeout);
+ }
+
/* Print tags as sudoOption attributes */
- if (cs->timeout > 0 || TAGS_SET(cs->tags)) {
+ if (TAGS_SET(cs->tags)) {
struct cmndtag tag = cs->tags;
- if (cs->timeout > 0) {
- fprintf(fp, "sudoOption: command_timeout=%d\n", cs->timeout);
- }
if (tag.nopasswd != UNSPEC) {
fprintf(fp, "sudoOption: %sauthenticate\n", tag.nopasswd ? "!" : "");
}
fprintf(fp, "sudoOption: %slog_output\n", tag.log_output ? "" : "!");
}
}
+ print_options_ldif(fp, options);
#ifdef HAVE_SELINUX
/* Print SELinux role/type */
HOSTALIAS, "sudoHost");
}
- print_cmndspec_ldif(fp, cs, &next);
+ print_cmndspec_ldif(fp, cs, &next, &priv->defaults);
fprintf(fp, "sudoOrder: %d\n\n", ++sudo_order);
}
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."));
+ sudo_fatalx(U_("the SUDOERS_BASE environment variable is not set and the -b option was not specified."));
}
if (strcmp(output_file, "-") != 0) {
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);
+}
/* 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)
#include <config.h>
#include <sys/types.h>
+#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# 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"
}
/*
- * 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 *
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;
if (priv->ldap_role == NULL)
goto oom;
- /* 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);
+ 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));
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) {
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);
TAILQ_INSERT_TAIL(&priv->defaults, def, entries);
} else {
/* Convert to tags. */
- if (op != true && op != false)
+ 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) {
+ cmndspec->tags.follow = op == true;
+ } else if (strcmp(var, "noexec") == 0) {
+ cmndspec->tags.noexec = op == true;
+ } else if (strcmp(var, "setenv") == 0) {
+ cmndspec->tags.setenv = op == true;
+ } 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;
- if (strcmp(var, "authenticate") == 0) {
- cmndspec->tags.nopasswd = op == false;
- } else if (strcmp(var, "sudoedit_follow") == 0) {
- cmndspec->tags.follow = op == true;
- } else if (strcmp(var, "noexec") == 0) {
- cmndspec->tags.noexec = op == true;
- } else if (strcmp(var, "setenv") == 0) {
- cmndspec->tags.setenv = op == true;
- } else if (strcmp(var, "mail_all_cmnds") == 0 ||
- strcmp(var, "mail_always") == 0) {
- cmndspec->tags.send_mail = op == true;
}
}
}
/* 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)
/* 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 */