Change visudo -x to take a file name argument, which may be '-' to

write the exported sudoers file to stdout.
This commit is contained in:
Todd C. Miller
2013-12-16 14:32:42 -07:00
parent b2c456341a
commit 68f6e23b07
5 changed files with 231 additions and 191 deletions

View File

@@ -4,7 +4,7 @@ NNAAMMEE
vviissuuddoo - edit the sudoers file vviissuuddoo - edit the sudoers file
SSYYNNOOPPSSIISS SSYYNNOOPPSSIISS
vviissuuddoo [--cchhqqssVVxx] [--ff _s_u_d_o_e_r_s] vviissuuddoo [--cchhqqssVV] [--ff _s_u_d_o_e_r_s] [--xx _f_i_l_e]
DDEESSCCRRIIPPTTIIOONN DDEESSCCRRIIPPTTIIOONN
vviissuuddoo edits the _s_u_d_o_e_r_s file in a safe fashion, analogous to vipw(1m). vviissuuddoo edits the _s_u_d_o_e_r_s file in a safe fashion, analogous to vipw(1m).
@@ -72,12 +72,14 @@ DDEESSCCRRIIPPTTIIOONN
--VV, ----vveerrssiioonn --VV, ----vveerrssiioonn
Print the vviissuuddoo and _s_u_d_o_e_r_s grammar versions and exit. Print the vviissuuddoo and _s_u_d_o_e_r_s grammar versions and exit.
--xx, ----eexxppoorrtt --xx _f_i_l_e, ----eexxppoorrtt=_f_i_l_e
Export _s_u_d_o_e_r_s in JSON format to the standard output. The Export _s_u_d_o_e_r_s in JSON format and write it to _f_i_l_e. If _f_i_l_e
result is intended to be easier for third-party applications is `-', the exported _s_u_d_o_e_r_s policy will to be written to the
to parse that the normal _s_u_d_o_e_r_s format. The various values standard output. The exported format is intended to be
have explicit types which removes some of the ambiguity of easier for third-party applications to parse that the
the _s_u_d_o_e_r_s format. traditional _s_u_d_o_e_r_s format. The various values have explicit
types which removes much of the ambiguity of the _s_u_d_o_e_r_s
format.
EENNVVIIRROONNMMEENNTT EENNVVIIRROONNMMEENNTT
The following environment variables may be consulted depending on the The following environment variables may be consulted depending on the
@@ -154,4 +156,4 @@ DDIISSCCLLAAIIMMEERR
file distributed with ssuuddoo or http://www.sudo.ws/sudo/license.html for file distributed with ssuuddoo or http://www.sudo.ws/sudo/license.html for
complete details. complete details.
Sudo 1.8.9 November 15, 2013 Sudo 1.8.9 Sudo 1.8.9 December 16, 2013 Sudo 1.8.9

View File

@@ -21,7 +21,7 @@
.\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
.\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
.\" .\"
.TH "VISUDO" "@mansectsu@" "November 15, 2013" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" .TH "VISUDO" "@mansectsu@" "December 16, 2013" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -30,8 +30,9 @@
.SH "SYNOPSIS" .SH "SYNOPSIS"
.HP 7n .HP 7n
\fBvisudo\fR \fBvisudo\fR
[\fB\-chqsVx\fR] [\fB\-chqsV\fR]
[\fB\-f\fR\ \fIsudoers\fR] [\fB\-f\fR\ \fIsudoers\fR]
[\fB\-x\fR\ \fIfile\fR]
.SH "DESCRIPTION" .SH "DESCRIPTION"
\fBvisudo\fR \fBvisudo\fR
edits the edits the
@@ -204,15 +205,23 @@ and
\fIsudoers\fR \fIsudoers\fR
grammar versions and exit. grammar versions and exit.
.TP 12n .TP 12n
\fB\-x\fR, \fB\--export\fR \fB\-x\fR \fIfile\fR, \fB\--export\fR=\fIfile\fR
Export Export
\fIsudoers\fR \fIsudoers\fR
in JSON format to the standard output. in JSON format and write it to
The result is intended to be easier for third-party applications \fIfile\fR.
to parse that the normal If
\fIfile\fR
is
`-',
the exported
\fIsudoers\fR
policy will to be written to the standard output.
The exported format is intended to be easier for third-party
applications to parse that the traditional
\fIsudoers\fR \fIsudoers\fR
format. format.
The various values have explicit types which removes some of the The various values have explicit types which removes much of the
ambiguity of the ambiguity of the
\fIsudoers\fR \fIsudoers\fR
format. format.

View File

@@ -19,7 +19,7 @@
.\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Agency (DARPA) and Air Force Research Laboratory, Air Force
.\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
.\" .\"
.Dd November 15, 2013 .Dd December 16, 2013
.Dt VISUDO @mansectsu@ .Dt VISUDO @mansectsu@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -27,10 +27,13 @@
.Nd edit the sudoers file .Nd edit the sudoers file
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm visudo .Nm visudo
.Op Fl chqsVx .Op Fl chqsV
.Bk -words .Bk -words
.Op Fl f Ar sudoers .Op Fl f Ar sudoers
.Ek .Ek
.Bk -words
.Op Fl x Ar file
.Ek
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm visudo .Nm visudo
edits the edits the
@@ -197,15 +200,23 @@ Print the
and and
.Em sudoers .Em sudoers
grammar versions and exit. grammar versions and exit.
.It Fl x , -export .It Fl x Ar file , Fl -export Ns No = Ns Ar file
Export Export
.Em sudoers .Em sudoers
in JSON format to the standard output. in JSON format and write it to
The result is intended to be easier for third-party applications .Ar file .
to parse that the normal If
.Ar file
is
.Ql - ,
the exported
.Em sudoers
policy will to be written to the standard output.
The exported format is intended to be easier for third-party
applications to parse that the traditional
.Em sudoers .Em sudoers
format. format.
The various values have explicit types which removes some of the The various values have explicit types which removes much of the
ambiguity of the ambiguity of the
.Em sudoers .Em sudoers
format. format.

View File

@@ -114,7 +114,7 @@ static void help(void) __attribute__((__noreturn__));
static void usage(int); static void usage(int);
static void visudo_cleanup(void); static void visudo_cleanup(void);
extern bool export_sudoers(const char *, bool, bool); extern bool export_sudoers(const char *, const char *, bool, bool);
extern void sudoerserror(const char *); extern void sudoerserror(const char *);
extern void sudoersrestart(FILE *); extern void sudoersrestart(FILE *);
@@ -127,10 +127,10 @@ struct passwd *list_pw;
static struct sudoersfile_list sudoerslist = TAILQ_HEAD_INITIALIZER(sudoerslist); static struct sudoersfile_list sudoerslist = TAILQ_HEAD_INITIALIZER(sudoerslist);
static struct rbtree *alias_freelist; static struct rbtree *alias_freelist;
static bool checkonly; static bool checkonly;
static const char short_opts[] = "cf:hqsVx"; static const char short_opts[] = "cf:hqsVx:";
static struct option long_opts[] = { static struct option long_opts[] = {
{ "check", no_argument, NULL, 'c' }, { "check", no_argument, NULL, 'c' },
{ "export", no_argument, NULL, 'x' }, { "export", required_argument, NULL, 'x' },
{ "file", required_argument, NULL, 'f' }, { "file", required_argument, NULL, 'f' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "quiet", no_argument, NULL, 'q' }, { "quiet", no_argument, NULL, 'q' },
@@ -147,7 +147,8 @@ main(int argc, char *argv[])
struct sudoersfile *sp; struct sudoersfile *sp;
char *args, *editor, *sudoers_path; char *args, *editor, *sudoers_path;
int ch, exitcode = 0; int ch, exitcode = 0;
bool quiet, strict, oldperms, export; bool quiet, strict, oldperms;
const char *export_path;
debug_decl(main, SUDO_DEBUG_MAIN) debug_decl(main, SUDO_DEBUG_MAIN)
#if defined(SUDO_DEVEL) && defined(__OpenBSD__) #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
@@ -174,7 +175,8 @@ main(int argc, char *argv[])
/* /*
* Arg handling. * Arg handling.
*/ */
checkonly = oldperms = quiet = strict = export = false; checkonly = oldperms = quiet = strict = false;
export_path = NULL;
sudoers_path = _PATH_SUDOERS; sudoers_path = _PATH_SUDOERS;
while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) { while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (ch) { switch (ch) {
@@ -201,7 +203,7 @@ main(int argc, char *argv[])
quiet = true; /* quiet mode */ quiet = true; /* quiet mode */
break; break;
case 'x': case 'x':
export = true; /* export mode */ export_path = optarg; /* export mode */
break; break;
default: default:
usage(1); usage(1);
@@ -227,8 +229,8 @@ main(int argc, char *argv[])
exitcode = check_syntax(sudoers_path, quiet, strict, oldperms) ? 0 : 1; exitcode = check_syntax(sudoers_path, quiet, strict, oldperms) ? 0 : 1;
goto done; goto done;
} }
if (export) { if (export_path != NULL) {
exitcode = export_sudoers(sudoers_path, quiet, strict) ? 0 : 1; exitcode = export_sudoers(sudoers_path, export_path, quiet, strict) ? 0 : 1;
goto done; goto done;
} }
@@ -1317,7 +1319,7 @@ static void
usage(int fatal) usage(int fatal)
{ {
(void) fprintf(fatal ? stderr : stdout, (void) fprintf(fatal ? stderr : stdout,
"usage: %s [-chqsVx] [-f sudoers]\n", getprogname()); "usage: %s [-chqsV] [-f sudoers] [-x file]\n", getprogname());
if (fatal) if (fatal)
exit(1); exit(1);
} }
@@ -1334,6 +1336,6 @@ help(void)
" -q, --quiet less verbose (quiet) syntax error messages\n" " -q, --quiet less verbose (quiet) syntax error messages\n"
" -s, --strict strict syntax checking\n" " -s, --strict strict syntax checking\n"
" -V, --version display version information and exit\n" " -V, --version display version information and exit\n"
" -x, --export export sudoers in JSON format")); " -x, --export=file export sudoers in JSON format"));
exit(0); exit(0);
} }

View File

@@ -74,6 +74,7 @@ struct json_value {
* Closure used to store state when iterating over all aliases. * Closure used to store state when iterating over all aliases.
*/ */
struct json_alias_closure { struct json_alias_closure {
FILE *fp;
const char *title; const char *title;
unsigned int count; unsigned int count;
int alias_type; int alias_type;
@@ -108,10 +109,10 @@ static const char *digest_names[] = {
* Print "indent" number of blank characters. * Print "indent" number of blank characters.
*/ */
static void static void
print_indent(int indent) print_indent(FILE *fp, int indent)
{ {
while (indent--) while (indent--)
putchar(' '); putc(' ', fp);
} }
/* /*
@@ -119,7 +120,7 @@ print_indent(int indent)
* Does not support unicode escapes. * Does not support unicode escapes.
*/ */
static void static void
print_string_json_unquoted(const char *str) print_string_json_unquoted(FILE *fp, const char *str)
{ {
char ch; char ch;
@@ -128,30 +129,30 @@ print_string_json_unquoted(const char *str)
case '"': case '"':
case '\\': case '\\':
case '/': case '/':
putchar('\\'); putc('\\', fp);
break; break;
case '\b': case '\b':
ch = 'b'; ch = 'b';
putchar('\\'); putc('\\', fp);
break; break;
case '\f': case '\f':
ch = 'f'; ch = 'f';
putchar('\\'); putc('\\', fp);
break; break;
case '\n': case '\n':
ch = 'n'; ch = 'n';
putchar('\\'); putc('\\', fp);
break; break;
case '\r': case '\r':
ch = 'r'; ch = 'r';
putchar('\\'); putc('\\', fp);
break; break;
case '\t': case '\t':
ch = 't'; ch = 't';
putchar('\\'); putc('\\', fp);
break; break;
} }
putchar(ch); putc(ch, fp);
} }
} }
@@ -160,49 +161,49 @@ print_string_json_unquoted(const char *str)
* Does not support unicode escapes. * Does not support unicode escapes.
*/ */
static void static void
print_string_json(const char *str) print_string_json(FILE *fp, const char *str)
{ {
putchar('\"'); putc('\"', fp);
print_string_json_unquoted(str); print_string_json_unquoted(fp, str);
putchar('\"'); putc('\"', fp);
} }
/* /*
* Print a JSON name: value pair with proper quoting and escaping. * Print a JSON name: value pair with proper quoting and escaping.
*/ */
static void static void
print_pair_json(const char *pre, const char *name, print_pair_json(FILE *fp, const char *pre, const char *name,
const struct json_value *value, const char *post, int indent) const struct json_value *value, const char *post, int indent)
{ {
debug_decl(print_pair_json, SUDO_DEBUG_UTIL) debug_decl(print_pair_json, SUDO_DEBUG_UTIL)
print_indent(indent); print_indent(fp, indent);
/* prefix */ /* prefix */
if (pre != NULL) if (pre != NULL)
fputs(pre, stdout); fputs(pre, fp);
/* name */ /* name */
print_string_json(name); print_string_json(fp, name);
putchar(':'); putc(':', fp);
putchar(' '); putc(' ', fp);
/* value */ /* value */
switch (value->type) { switch (value->type) {
case JSON_STRING: case JSON_STRING:
print_string_json(value->u.string); print_string_json(fp, value->u.string);
break; break;
case JSON_ID: case JSON_ID:
printf("%u", (unsigned int)value->u.id); fprintf(fp, "%u", (unsigned int)value->u.id);
break; break;
case JSON_NUMBER: case JSON_NUMBER:
printf("%d", value->u.number); fprintf(fp, "%d", value->u.number);
break; break;
case JSON_NULL: case JSON_NULL:
fputs("null", stdout); fputs("null", fp);
break; break;
case JSON_BOOL: case JSON_BOOL:
fputs(value->u.boolean ? "true" : "false", stdout); fputs(value->u.boolean ? "true" : "false", fp);
break; break;
case JSON_OBJECT: case JSON_OBJECT:
fatalx("internal error: can't print JSON_OBJECT"); fatalx("internal error: can't print JSON_OBJECT");
@@ -214,28 +215,29 @@ print_pair_json(const char *pre, const char *name,
/* postfix */ /* postfix */
if (post != NULL) if (post != NULL)
fputs(post, stdout); fputs(post, fp);
debug_return; debug_return;
} }
/* /*
* Print a JSON string with optional prefix and postfix to stdout. * Print a JSON string with optional prefix and postfix to fp.
* Strings are not quoted but are escaped as per the JSON spec. * Strings are not quoted but are escaped as per the JSON spec.
*/ */
static void static void
printstr_json(const char *pre, const char *str, const char *post, int indent) printstr_json(FILE *fp, const char *pre, const char *str, const char *post,
int indent)
{ {
debug_decl(printstr_json, SUDO_DEBUG_UTIL) debug_decl(printstr_json, SUDO_DEBUG_UTIL)
print_indent(indent); print_indent(fp, indent);
if (pre != NULL) if (pre != NULL)
fputs(pre, stdout); fputs(pre, fp);
if (str != NULL) { if (str != NULL) {
print_string_json_unquoted(str); print_string_json_unquoted(fp, str);
} }
if (post != NULL) if (post != NULL)
fputs(post, stdout); fputs(post, fp);
debug_return; debug_return;
} }
@@ -245,15 +247,15 @@ printstr_json(const char *pre, const char *str, const char *post, int indent)
* that closes the object. * that closes the object.
*/ */
static void static void
print_command_json(struct sudo_command *c, int indent, bool last_one) print_command_json(FILE *fp, struct sudo_command *c, int indent, bool last_one)
{ {
struct json_value value; struct json_value value;
const char *digest_type; const char *digest_type;
debug_decl(print_command_json, SUDO_DEBUG_UTIL) debug_decl(print_command_json, SUDO_DEBUG_UTIL)
printstr_json("{", NULL, NULL, indent); printstr_json(fp, "{", NULL, NULL, indent);
if (c->digest != NULL) { if (c->digest != NULL) {
putchar('\n'); putc('\n', fp);
indent += 4; indent += 4;
if (c->digest->digest_type < SUDO_DIGEST_INVALID) { if (c->digest->digest_type < SUDO_DIGEST_INVALID) {
digest_type = digest_names[c->digest->digest_type]; digest_type = digest_names[c->digest->digest_type];
@@ -262,31 +264,31 @@ print_command_json(struct sudo_command *c, int indent, bool last_one)
} }
value.type = JSON_STRING; value.type = JSON_STRING;
value.u.string = c->digest->digest_str; value.u.string = c->digest->digest_str;
print_pair_json(NULL, digest_type, &value, ",\n", indent); print_pair_json(fp, NULL, digest_type, &value, ",\n", indent);
} else { } else {
putchar(' '); putc(' ', fp);
indent = 0; indent = 0;
} }
if (c->args != NULL) { if (c->args != NULL) {
printstr_json("\"", "command", "\": ", indent); printstr_json(fp, "\"", "command", "\": ", indent);
printstr_json("\"", c->cmnd, " ", 0); printstr_json(fp, "\"", c->cmnd, " ", 0);
printstr_json(NULL, c->args, "\"", 0); printstr_json(fp, NULL, c->args, "\"", 0);
} else { } else {
value.type = JSON_STRING; value.type = JSON_STRING;
value.u.string = c->cmnd; value.u.string = c->cmnd;
print_pair_json(NULL, "command", &value, NULL, indent); print_pair_json(fp, NULL, "command", &value, NULL, indent);
} }
if (c->digest != NULL) { if (c->digest != NULL) {
indent -= 4; indent -= 4;
putchar('\n'); putc('\n', fp);
print_indent(indent); print_indent(fp, indent);
} else { } else {
putchar(' '); putc(' ', fp);
} }
putchar('}'); putc('}', fp);
if (!last_one) if (!last_one)
putchar(','); putc(',', fp);
putchar('\n'); putc('\n', fp);
debug_return; debug_return;
} }
@@ -337,8 +339,8 @@ defaults_to_word_type(int defaults_type)
* that closes the object. * that closes the object.
*/ */
static void static void
print_member_json(struct member *m, enum word_type word_type, bool last_one, print_member_json(FILE *fp, struct member *m, enum word_type word_type,
int indent) bool last_one, int indent)
{ {
struct json_value value; struct json_value value;
const char *typestr; const char *typestr;
@@ -390,7 +392,7 @@ print_member_json(struct member *m, enum word_type word_type, bool last_one,
typestr = "networkaddr"; typestr = "networkaddr";
break; break;
case COMMAND: case COMMAND:
print_command_json((struct sudo_command *)m->name, indent, last_one); print_command_json(fp, (struct sudo_command *)m->name, indent, last_one);
debug_return; debug_return;
case WORD: case WORD:
switch (word_type) { switch (word_type) {
@@ -444,10 +446,10 @@ print_member_json(struct member *m, enum word_type word_type, bool last_one,
default: default:
fatalx("unexpected member type %d", m->type); fatalx("unexpected member type %d", m->type);
} }
print_pair_json("{ ", typestr, &value, " }", indent); print_pair_json(fp, "{ ", typestr, &value, " }", indent);
if (!last_one) if (!last_one)
putchar(','); putc(',', fp);
putchar('\n'); putc('\n', fp);
debug_return; debug_return;
} }
@@ -469,17 +471,19 @@ print_alias_json(void *v1, void *v2)
/* Open the aliases object or close the last entry, then open new one. */ /* Open the aliases object or close the last entry, then open new one. */
if (closure->count++ == 0) { if (closure->count++ == 0) {
printf("%s\n%*s\"%s\": {\n", closure->need_comma ? "," : "", fprintf(closure->fp, "%s\n%*s\"%s\": {\n",
closure->indent, "", closure->title); closure->need_comma ? "," : "", closure->indent, "",
closure->title);
closure->indent += 4; closure->indent += 4;
} else { } else {
printf("%*s],\n", closure->indent, ""); fprintf(closure->fp, "%*s],\n", closure->indent, "");
} }
printstr_json("\"", a->name, "\": [\n", closure->indent); printstr_json(closure->fp, "\"", a->name, "\": [\n", closure->indent);
closure->indent += 4; closure->indent += 4;
TAILQ_FOREACH(m, &a->members, entries) { TAILQ_FOREACH(m, &a->members, entries) {
print_member_json(m, alias_to_word_type(closure->alias_type), print_member_json(closure->fp, m,
alias_to_word_type(closure->alias_type),
TAILQ_NEXT(m, entries) == NULL, closure->indent); TAILQ_NEXT(m, entries) == NULL, closure->indent);
} }
closure->indent -= 4; closure->indent -= 4;
@@ -490,7 +494,7 @@ print_alias_json(void *v1, void *v2)
* Print the binding for a Defaults entry of the specified type. * Print the binding for a Defaults entry of the specified type.
*/ */
static void static void
print_binding_json(struct member_list *binding, int type, int indent) print_binding_json(FILE *fp, struct member_list *binding, int type, int indent)
{ {
struct member *m; struct member *m;
debug_decl(print_binding_json, SUDO_DEBUG_UTIL) debug_decl(print_binding_json, SUDO_DEBUG_UTIL)
@@ -498,17 +502,17 @@ print_binding_json(struct member_list *binding, int type, int indent)
if (TAILQ_EMPTY(binding)) if (TAILQ_EMPTY(binding))
debug_return; debug_return;
printf("%*s\"Binding\": [\n", indent, ""); fprintf(fp, "%*s\"Binding\": [\n", indent, "");
indent += 4; indent += 4;
/* Print each member object in binding. */ /* Print each member object in binding. */
TAILQ_FOREACH(m, binding, entries) { TAILQ_FOREACH(m, binding, entries) {
print_member_json(m, defaults_to_word_type(type), print_member_json(fp, m, defaults_to_word_type(type),
TAILQ_NEXT(m, entries) == NULL, indent); TAILQ_NEXT(m, entries) == NULL, indent);
} }
indent -= 4; indent -= 4;
printf("%*s],\n", indent, ""); fprintf(fp, "%*s],\n", indent, "");
debug_return; debug_return;
} }
@@ -517,13 +521,13 @@ print_binding_json(struct member_list *binding, int type, int indent)
* Print a Defaults list JSON format. * Print a Defaults list JSON format.
*/ */
static void static void
print_defaults_list_json(struct defaults *def, int indent) print_defaults_list_json(FILE *fp, struct defaults *def, int indent)
{ {
char savech, *start, *end = def->val; char savech, *start, *end = def->val;
struct json_value value; struct json_value value;
debug_decl(print_defaults_list_json, SUDO_DEBUG_UTIL) debug_decl(print_defaults_list_json, SUDO_DEBUG_UTIL)
printf("%*s{\n", indent, ""); fprintf(fp, "%*s{\n", indent, "");
indent += 4; indent += 4;
value.type = JSON_STRING; value.type = JSON_STRING;
switch (def->op) { switch (def->op) {
@@ -541,10 +545,10 @@ print_defaults_list_json(struct defaults *def, int indent)
value.u.string = "unsupported"; value.u.string = "unsupported";
break; break;
} }
print_pair_json(NULL, "operation", &value, ",\n", indent); print_pair_json(fp, NULL, "operation", &value, ",\n", indent);
printstr_json("\"", def->var, "\": [\n", indent); printstr_json(fp, "\"", def->var, "\": [\n", indent);
indent += 4; indent += 4;
print_indent(indent); print_indent(fp, indent);
/* Split value into multiple space-separated words. */ /* Split value into multiple space-separated words. */
do { do {
/* Remove leading blanks, must have a non-empty string. */ /* Remove leading blanks, must have a non-empty string. */
@@ -558,16 +562,16 @@ print_defaults_list_json(struct defaults *def, int indent)
; ;
savech = *end; savech = *end;
*end = '\0'; *end = '\0';
print_string_json(start); print_string_json(fp, start);
if (savech != '\0') if (savech != '\0')
putchar(','); putc(',', fp);
*end = savech; *end = savech;
} while (*end++ != '\0'); } while (*end++ != '\0');
putchar('\n'); putc('\n', fp);
indent -= 4; indent -= 4;
printf("%*s]\n", indent, ""); fprintf(fp, "%*s]\n", indent, "");
indent -= 4; indent -= 4;
printf("%*s}", indent, ""); fprintf(fp, "%*s}", indent, "");
debug_return; debug_return;
} }
@@ -589,7 +593,7 @@ get_defaults_type(struct defaults *def)
* Export all Defaults in JSON format. * Export all Defaults in JSON format.
*/ */
static bool static bool
print_defaults_json(int indent, bool need_comma) print_defaults_json(FILE *fp, int indent, bool need_comma)
{ {
struct json_value value; struct json_value value;
struct defaults *def, *next; struct defaults *def, *next;
@@ -599,7 +603,7 @@ print_defaults_json(int indent, bool need_comma)
if (TAILQ_EMPTY(&defaults)) if (TAILQ_EMPTY(&defaults))
debug_return_bool(need_comma); debug_return_bool(need_comma);
printf("%s\n%*s\"Defaults\": [\n", need_comma ? "," : "", indent, ""); fprintf(fp, "%s\n%*s\"Defaults\": [\n", need_comma ? "," : "", indent, "");
TAILQ_FOREACH_SAFE(def, &defaults, entries, next) { TAILQ_FOREACH_SAFE(def, &defaults, entries, next) {
type = get_defaults_type(def); type = get_defaults_type(def);
@@ -610,16 +614,16 @@ print_defaults_json(int indent, bool need_comma)
} }
/* Found it, print object container and binding (if any). */ /* Found it, print object container and binding (if any). */
indent = 8; indent += 4;
printf("%*s{\n", indent, ""); fprintf(fp, "%*s{\n", indent, "");
indent = 12; indent += 4;
print_binding_json(def->binding, def->type, indent); print_binding_json(fp, def->binding, def->type, indent);
/* Validation checks. */ /* Validation checks. */
/* XXX - validate values in addition to names? */ /* XXX - validate values in addition to names? */
/* Print options, merging ones with the same binding. */ /* Print options, merging ones with the same binding. */
printf("%*s\"Options\": [\n", indent, ""); fprintf(fp, "%*s\"Options\": [\n", indent, "");
indent += 4; indent += 4;
for (;;) { for (;;) {
next = TAILQ_NEXT(def, entries); next = TAILQ_NEXT(def, entries);
@@ -627,13 +631,13 @@ print_defaults_json(int indent, bool need_comma)
if ((type & T_MASK) == T_FLAG || def->val == NULL) { if ((type & T_MASK) == T_FLAG || def->val == NULL) {
value.type = JSON_BOOL; value.type = JSON_BOOL;
value.u.boolean = def->op; value.u.boolean = def->op;
print_pair_json("{ ", def->var, &value, " }", indent); print_pair_json(fp, "{ ", def->var, &value, " }", indent);
} else if ((type & T_MASK) == T_LIST) { } else if ((type & T_MASK) == T_LIST) {
print_defaults_list_json(def, indent); print_defaults_list_json(fp, def, indent);
} else { } else {
value.type = JSON_STRING; value.type = JSON_STRING;
value.u.string = def->val; value.u.string = def->val;
print_pair_json("{ ", def->var, &value, " }", indent); print_pair_json(fp, "{ ", def->var, &value, " }", indent);
} }
if (next == NULL || def->binding != next->binding) if (next == NULL || def->binding != next->binding)
break; break;
@@ -644,21 +648,21 @@ print_defaults_json(int indent, bool need_comma)
/* XXX - just pass it through as a string anyway? */ /* XXX - just pass it through as a string anyway? */
break;; break;;
} }
fputs(",\n", stdout); fputs(",\n", fp);
} }
putchar('\n'); putc('\n', fp);
indent -= 4; indent -= 4;
print_indent(indent); print_indent(fp, indent);
fputs("]\n", stdout); fputs("]\n", fp);
indent -= 4; indent -= 4;
print_indent(indent); print_indent(fp, indent);
printf("}%s\n", next != NULL ? "," : ""); fprintf(fp, "}%s\n", next != NULL ? "," : "");
} }
/* Close Defaults array; comma (if any) & newline will be printer later. */ /* Close Defaults array; comma (if any) & newline will be printer later. */
indent -= 4; indent -= 4;
print_indent(indent); print_indent(fp, indent);
fputs("]", stdout); fputs("]", fp);
debug_return_bool(true); debug_return_bool(true);
} }
@@ -668,12 +672,13 @@ print_defaults_json(int indent, bool need_comma)
* Iterates through the entire aliases tree. * Iterates through the entire aliases tree.
*/ */
static bool static bool
print_aliases_by_type_json(int alias_type, const char *title, int indent, print_aliases_by_type_json(FILE *fp, int alias_type, const char *title,
bool need_comma) int indent, bool need_comma)
{ {
struct json_alias_closure closure; struct json_alias_closure closure;
debug_decl(print_aliases_by_type_json, SUDO_DEBUG_UTIL) debug_decl(print_aliases_by_type_json, SUDO_DEBUG_UTIL)
closure.fp = fp;
closure.indent = indent; closure.indent = indent;
closure.count = 0; closure.count = 0;
closure.alias_type = alias_type; closure.alias_type = alias_type;
@@ -681,11 +686,11 @@ print_aliases_by_type_json(int alias_type, const char *title, int indent,
closure.need_comma = need_comma; closure.need_comma = need_comma;
alias_apply(print_alias_json, &closure); alias_apply(print_alias_json, &closure);
if (closure.count != 0) { if (closure.count != 0) {
print_indent(closure.indent); print_indent(fp, closure.indent);
fputs("]\n", stdout); fputs("]\n", fp);
closure.indent -= 4; closure.indent -= 4;
print_indent(closure.indent); print_indent(fp, closure.indent);
putchar('}'); putc('}', fp);
need_comma = true; need_comma = true;
} }
@@ -696,17 +701,17 @@ print_aliases_by_type_json(int alias_type, const char *title, int indent,
* Export all aliases in JSON format. * Export all aliases in JSON format.
*/ */
static bool static bool
print_aliases_json(int indent, bool need_comma) print_aliases_json(FILE *fp, int indent, bool need_comma)
{ {
debug_decl(print_aliases_json, SUDO_DEBUG_UTIL) debug_decl(print_aliases_json, SUDO_DEBUG_UTIL)
need_comma = print_aliases_by_type_json(USERALIAS, "User_Aliases", need_comma = print_aliases_by_type_json(fp, USERALIAS, "User_Aliases",
indent, need_comma); indent, need_comma);
need_comma = print_aliases_by_type_json(RUNASALIAS, "Runas_Aliases", need_comma = print_aliases_by_type_json(fp, RUNASALIAS, "Runas_Aliases",
indent, need_comma); indent, need_comma);
need_comma = print_aliases_by_type_json(HOSTALIAS, "Host_Aliases", need_comma = print_aliases_by_type_json(fp, HOSTALIAS, "Host_Aliases",
indent, need_comma); indent, need_comma);
need_comma = print_aliases_by_type_json(CMNDALIAS, "Command_Aliases", need_comma = print_aliases_by_type_json(fp, CMNDALIAS, "Command_Aliases",
indent, need_comma); indent, need_comma);
debug_return_bool(need_comma); debug_return_bool(need_comma);
@@ -734,7 +739,8 @@ print_aliases_json(int indent, bool need_comma)
* merge adjacent entries that are identical in all but the command. * merge adjacent entries that are identical in all but the command.
*/ */
static void static void
print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent) print_cmndspec_json(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp,
int indent)
{ {
struct cmndspec *next = *nextp; struct cmndspec *next = *nextp;
struct json_value value; struct json_value value;
@@ -743,38 +749,38 @@ print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent)
debug_decl(print_cmndspec_json, SUDO_DEBUG_UTIL) debug_decl(print_cmndspec_json, SUDO_DEBUG_UTIL)
/* Open Cmnd_Spec object. */ /* Open Cmnd_Spec object. */
printf("%*s{\n", indent, ""); fprintf(fp, "%*s{\n", indent, "");
indent += 4; indent += 4;
/* Print runasuserlist */ /* Print runasuserlist */
if (cs->runasuserlist != NULL) { if (cs->runasuserlist != NULL) {
printf("%*s\"runasusers\": [\n", indent, ""); fprintf(fp, "%*s\"runasusers\": [\n", indent, "");
indent += 4; indent += 4;
TAILQ_FOREACH(m, cs->runasuserlist, entries) { TAILQ_FOREACH(m, cs->runasuserlist, entries) {
print_member_json(m, TYPE_RUNASUSER, print_member_json(fp, m, TYPE_RUNASUSER,
TAILQ_NEXT(m, entries) == NULL, indent); TAILQ_NEXT(m, entries) == NULL, indent);
} }
indent -= 4; indent -= 4;
printf("%*s],\n", indent, ""); fprintf(fp, "%*s],\n", indent, "");
} }
/* Print runasgrouplist */ /* Print runasgrouplist */
if (cs->runasgrouplist != NULL) { if (cs->runasgrouplist != NULL) {
printf("%*s\"runasgroups\": [\n", indent, ""); fprintf(fp, "%*s\"runasgroups\": [\n", indent, "");
indent += 4; indent += 4;
TAILQ_FOREACH(m, cs->runasgrouplist, entries) { TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
print_member_json(m, TYPE_RUNASGROUP, print_member_json(fp, m, TYPE_RUNASGROUP,
TAILQ_NEXT(m, entries) == NULL, indent); TAILQ_NEXT(m, entries) == NULL, indent);
} }
indent -= 4; indent -= 4;
printf("%*s],\n", indent, ""); fprintf(fp, "%*s],\n", indent, "");
} }
/* Print tags */ /* Print tags */
if (cs->tags.nopasswd != UNSPEC || cs->tags.noexec != UNSPEC || if (cs->tags.nopasswd != UNSPEC || cs->tags.noexec != UNSPEC ||
cs->tags.setenv != UNSPEC || cs->tags.log_input != UNSPEC || cs->tags.setenv != UNSPEC || cs->tags.log_input != UNSPEC ||
cs->tags.log_output != UNSPEC) { cs->tags.log_output != UNSPEC) {
printf("%*s\"Options\": {\n", indent, ""); fprintf(fp, "%*s\"Options\": {\n", indent, "");
indent += 4; indent += 4;
if (cs->tags.nopasswd != UNSPEC) { if (cs->tags.nopasswd != UNSPEC) {
value.type = JSON_BOOL; value.type = JSON_BOOL;
@@ -782,7 +788,7 @@ print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent)
last_one = cs->tags.noexec == UNSPEC && last_one = cs->tags.noexec == UNSPEC &&
cs->tags.setenv == UNSPEC && cs->tags.log_input == UNSPEC && cs->tags.setenv == UNSPEC && cs->tags.log_input == UNSPEC &&
cs->tags.log_output == UNSPEC; cs->tags.log_output == UNSPEC;
print_pair_json(NULL, "authenticate", &value, print_pair_json(fp, NULL, "authenticate", &value,
last_one ? "\n" : ",\n", indent); last_one ? "\n" : ",\n", indent);
} }
if (cs->tags.noexec != UNSPEC) { if (cs->tags.noexec != UNSPEC) {
@@ -790,7 +796,7 @@ print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent)
value.u.boolean = cs->tags.noexec; value.u.boolean = cs->tags.noexec;
last_one = cs->tags.setenv == UNSPEC && last_one = cs->tags.setenv == UNSPEC &&
cs->tags.log_input == UNSPEC && cs->tags.log_output == UNSPEC; cs->tags.log_input == UNSPEC && cs->tags.log_output == UNSPEC;
print_pair_json(NULL, "noexec", &value, print_pair_json(fp, NULL, "noexec", &value,
last_one ? "\n" : ",\n", indent); last_one ? "\n" : ",\n", indent);
} }
if (cs->tags.setenv != UNSPEC) { if (cs->tags.setenv != UNSPEC) {
@@ -798,57 +804,57 @@ print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent)
value.u.boolean = cs->tags.setenv; value.u.boolean = cs->tags.setenv;
last_one = cs->tags.log_input == UNSPEC && last_one = cs->tags.log_input == UNSPEC &&
cs->tags.log_output == UNSPEC; cs->tags.log_output == UNSPEC;
print_pair_json(NULL, "setenv", &value, print_pair_json(fp, NULL, "setenv", &value,
last_one ? "\n" : ",\n", indent); last_one ? "\n" : ",\n", indent);
} }
if (cs->tags.log_input != UNSPEC) { if (cs->tags.log_input != UNSPEC) {
value.type = JSON_BOOL; value.type = JSON_BOOL;
value.u.boolean = cs->tags.log_input; value.u.boolean = cs->tags.log_input;
last_one = cs->tags.log_output == UNSPEC; last_one = cs->tags.log_output == UNSPEC;
print_pair_json(NULL, "log_input", &value, print_pair_json(fp, NULL, "log_input", &value,
last_one ? "\n" : ",\n", indent); last_one ? "\n" : ",\n", indent);
} }
if (cs->tags.log_output != UNSPEC) { if (cs->tags.log_output != UNSPEC) {
value.type = JSON_BOOL; value.type = JSON_BOOL;
value.u.boolean = cs->tags.log_output; value.u.boolean = cs->tags.log_output;
print_pair_json(NULL, "log_output", &value, "\n", indent); print_pair_json(fp, NULL, "log_output", &value, "\n", indent);
} }
indent -= 4; indent -= 4;
printf("%*s},\n", indent, ""); fprintf(fp, "%*s},\n", indent, "");
} }
#ifdef HAVE_SELINUX #ifdef HAVE_SELINUX
/* Print SELinux role/type */ /* Print SELinux role/type */
if (cs->role != NULL && cs->type != NULL) { if (cs->role != NULL && cs->type != NULL) {
printf("%*s\"SELinux_Spec\": [\n", indent, ""); fprintf(fp, "%*s\"SELinux_Spec\": [\n", indent, "");
indent += 4; indent += 4;
value.type = JSON_STRING; value.type = JSON_STRING;
value.u.string = cs->role; value.u.string = cs->role;
print_pair_json(NULL, "role", &value, ",\n", indent); print_pair_json(fp, NULL, "role", &value, ",\n", indent);
value.u.string = cs->type; value.u.string = cs->type;
print_pair_json(NULL, "type", &value, "\n", indent); print_pair_json(fp, NULL, "type", &value, "\n", indent);
indent -= 4; indent -= 4;
printf("%*s],\n", indent, ""); fprintf(fp, "%*s],\n", indent, "");
} }
#endif /* HAVE_SELINUX */ #endif /* HAVE_SELINUX */
#ifdef HAVE_PRIV_SET #ifdef HAVE_PRIV_SET
/* Print Solaris privs/limitprivs */ /* Print Solaris privs/limitprivs */
if (cs->privs != NULL || cs->limitprivs != NULL) { if (cs->privs != NULL || cs->limitprivs != NULL) {
printf("%*s\"Solaris_Priv_Spec\": [\n", indent, ""); fprintf(fp, "%*s\"Solaris_Priv_Spec\": [\n", indent, "");
indent += 4; indent += 4;
value.type = JSON_STRING; value.type = JSON_STRING;
if (cs->privs != NULL) { if (cs->privs != NULL) {
value.u.string = cs->privs; value.u.string = cs->privs;
print_pair_json(NULL, "privs", &value, print_pair_json(fp, NULL, "privs", &value,
cs->limitprivs != NULL ? ",\n" : "\n", indent); cs->limitprivs != NULL ? ",\n" : "\n", indent);
} }
if (cs->limitprivs != NULL) { if (cs->limitprivs != NULL) {
value.u.string = cs->limitprivs; value.u.string = cs->limitprivs;
print_pair_json(NULL, "limitprivs", &value, "\n", indent); print_pair_json(fp, NULL, "limitprivs", &value, "\n", indent);
} }
indent -= 4; indent -= 4;
printf("%*s],\n", indent, ""); fprintf(fp, "%*s],\n", indent, "");
} }
#endif /* HAVE_PRIV_SET */ #endif /* HAVE_PRIV_SET */
@@ -856,7 +862,7 @@ print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent)
* Merge adjacent commands with matching tags, runas, SELinux * Merge adjacent commands with matching tags, runas, SELinux
* role/type and Solaris priv settings. * role/type and Solaris priv settings.
*/ */
printf("%*s\"Commands\": [\n", indent, ""); fprintf(fp, "%*s\"Commands\": [\n", indent, "");
indent += 4; indent += 4;
for (;;) { for (;;) {
/* Does the next entry differ only in the command itself? */ /* Does the next entry differ only in the command itself? */
@@ -871,18 +877,18 @@ print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent)
#endif /* HAVE_SELINUX */ #endif /* HAVE_SELINUX */
; ;
print_member_json(cs->cmnd, TYPE_COMMAND, last_one, indent); print_member_json(fp, cs->cmnd, TYPE_COMMAND, last_one, indent);
if (last_one) if (last_one)
break; break;
cs = next; cs = next;
next = TAILQ_NEXT(cs, entries); next = TAILQ_NEXT(cs, entries);
} }
indent -= 4; indent -= 4;
printf("%*s]\n", indent, ""); fprintf(fp, "%*s]\n", indent, "");
/* Close Cmnd_Spec object. */ /* Close Cmnd_Spec object. */
indent -= 4; indent -= 4;
printf("%*s}%s\n", indent, "", TAILQ_NEXT(cs, entries) != NULL ? "," : ""); fprintf(fp, "%*s}%s\n", indent, "", TAILQ_NEXT(cs, entries) != NULL ? "," : "");
*nextp = next; *nextp = next;
@@ -893,7 +899,7 @@ print_cmndspec_json(struct cmndspec *cs, struct cmndspec **nextp, int indent)
* Print a User_Spec in JSON format at the specified indent level. * Print a User_Spec in JSON format at the specified indent level.
*/ */
static void static void
print_userspec_json(struct userspec *us, int indent) print_userspec_json(FILE *fp, struct userspec *us, int indent)
{ {
struct privilege *priv; struct privilege *priv;
struct member *m; struct member *m;
@@ -907,41 +913,41 @@ print_userspec_json(struct userspec *us, int indent)
*/ */
TAILQ_FOREACH(priv, &us->privileges, entries) { TAILQ_FOREACH(priv, &us->privileges, entries) {
/* Open User_Spec object. */ /* Open User_Spec object. */
printf("%*s{\n", indent, ""); fprintf(fp, "%*s{\n", indent, "");
indent += 4; indent += 4;
/* Print users list. */ /* Print users list. */
printf("%*s\"User_List\": [\n", indent, ""); fprintf(fp, "%*s\"User_List\": [\n", indent, "");
indent += 4; indent += 4;
TAILQ_FOREACH(m, &us->users, entries) { TAILQ_FOREACH(m, &us->users, entries) {
print_member_json(m, TYPE_USERNAME, print_member_json(fp, m, TYPE_USERNAME,
TAILQ_NEXT(m, entries) == NULL, indent); TAILQ_NEXT(m, entries) == NULL, indent);
} }
indent -= 4; indent -= 4;
printf("%*s],\n", indent, ""); fprintf(fp, "%*s],\n", indent, "");
/* Print hosts list. */ /* Print hosts list. */
printf("%*s\"Host_List\": [\n", indent, ""); fprintf(fp, "%*s\"Host_List\": [\n", indent, "");
indent += 4; indent += 4;
TAILQ_FOREACH(m, &priv->hostlist, entries) { TAILQ_FOREACH(m, &priv->hostlist, entries) {
print_member_json(m, TYPE_HOSTNAME, print_member_json(fp, m, TYPE_HOSTNAME,
TAILQ_NEXT(m, entries) == NULL, indent); TAILQ_NEXT(m, entries) == NULL, indent);
} }
indent -= 4; indent -= 4;
printf("%*s],\n", indent, ""); fprintf(fp, "%*s],\n", indent, "");
/* Print commands. */ /* Print commands. */
printf("%*s\"Cmnd_Specs\": [\n", indent, ""); fprintf(fp, "%*s\"Cmnd_Specs\": [\n", indent, "");
indent += 4; indent += 4;
TAILQ_FOREACH_SAFE(cs, &priv->cmndlist, entries, next) { TAILQ_FOREACH_SAFE(cs, &priv->cmndlist, entries, next) {
print_cmndspec_json(cs, &next, indent); print_cmndspec_json(fp, cs, &next, indent);
} }
indent -= 4; indent -= 4;
printf("%*s]\n", indent, ""); fprintf(fp, "%*s]\n", indent, "");
/* Close User_Spec object. */ /* Close User_Spec object. */
indent -= 4; indent -= 4;
printf("%*s}%s\n", indent, "", TAILQ_NEXT(priv, entries) != NULL || fprintf(fp, "%*s}%s\n", indent, "", TAILQ_NEXT(priv, entries) != NULL ||
TAILQ_NEXT(us, entries) != NULL ? "," : ""); TAILQ_NEXT(us, entries) != NULL ? "," : "");
} }
@@ -949,7 +955,7 @@ print_userspec_json(struct userspec *us, int indent)
} }
static bool static bool
print_userspecs_json(int indent, bool need_comma) print_userspecs_json(FILE *fp, int indent, bool need_comma)
{ {
struct userspec *us; struct userspec *us;
debug_decl(print_userspecs_json, SUDO_DEBUG_UTIL) debug_decl(print_userspecs_json, SUDO_DEBUG_UTIL)
@@ -957,13 +963,13 @@ print_userspecs_json(int indent, bool need_comma)
if (TAILQ_EMPTY(&userspecs)) if (TAILQ_EMPTY(&userspecs))
debug_return_bool(need_comma); debug_return_bool(need_comma);
printf("%s\n%*s\"User_Specs\": [\n", need_comma ? "," : "", indent, ""); fprintf(fp, "%s\n%*s\"User_Specs\": [\n", need_comma ? "," : "", indent, "");
indent += 4; indent += 4;
TAILQ_FOREACH(us, &userspecs, entries) { TAILQ_FOREACH(us, &userspecs, entries) {
print_userspec_json(us, indent); print_userspec_json(fp, us, indent);
} }
indent -= 4; indent -= 4;
printf("%*s]", indent, ""); fprintf(fp, "%*s]", indent, "");
debug_return_bool(true); debug_return_bool(true);
} }
@@ -971,13 +977,14 @@ print_userspecs_json(int indent, bool need_comma)
/* /*
* Export the parsed sudoers file in JSON format. * Export the parsed sudoers file in JSON format.
* XXX - ignores strict flag and doesn't pass through quiet flag * XXX - ignores strict flag and doesn't pass through quiet flag
* XXX - pass indent=4 to other json functions
*/ */
bool bool
export_sudoers(char *sudoers_path, bool quiet, bool strict) export_sudoers(const char *sudoers_path, const char *export_path,
bool quiet, bool strict)
{ {
bool ok = false, need_comma = false; bool ok = false, need_comma = false;
const int indent = 4; const int indent = 4;
FILE *export_fp;
debug_decl(export_sudoers, SUDO_DEBUG_UTIL) debug_decl(export_sudoers, SUDO_DEBUG_UTIL)
if (strcmp(sudoers_path, "-") == 0) { if (strcmp(sudoers_path, "-") == 0) {
@@ -988,6 +995,14 @@ export_sudoers(char *sudoers_path, bool quiet, bool strict)
warning(U_("unable to open %s"), sudoers_path); warning(U_("unable to open %s"), sudoers_path);
goto done; goto done;
} }
if (strcmp(export_path, "-") == 0) {
export_fp = stdout;
export_path = "stdout";
} else if ((export_fp = fopen(export_path, "w")) == NULL) {
if (!quiet)
warning(U_("unable to open %s"), export_path);
goto done;
}
init_parser(sudoers_path, quiet); init_parser(sudoers_path, quiet);
if (sudoersparse() && !parse_error) { if (sudoersparse() && !parse_error) {
if (!quiet) if (!quiet)
@@ -1000,28 +1015,29 @@ export_sudoers(char *sudoers_path, bool quiet, bool strict)
if (parse_error) { if (parse_error) {
if (!quiet) { if (!quiet) {
if (errorlineno != -1) if (errorlineno != -1)
printf(_("parse error in %s near line %d\n"), warningx(_("parse error in %s near line %d\n"),
errorfile, errorlineno); errorfile, errorlineno);
else if (errorfile != NULL) else if (errorfile != NULL)
printf(_("parse error in %s\n"), errorfile); warningx(_("parse error in %s\n"), errorfile);
} }
goto done; goto done;
} }
/* Open JSON output. */ /* Open JSON output. */
putchar('{'); putc('{', export_fp);
/* Dump Defaults in JSON format. */ /* Dump Defaults in JSON format. */
need_comma = print_defaults_json(indent, need_comma); need_comma = print_defaults_json(export_fp, indent, need_comma);
/* Dump Aliases in JSON format. */ /* Dump Aliases in JSON format. */
need_comma = print_aliases_json(indent, need_comma); need_comma = print_aliases_json(export_fp, indent, need_comma);
/* Dump User_Specs in JSON format. */ /* Dump User_Specs in JSON format. */
print_userspecs_json(indent, need_comma); print_userspecs_json(export_fp, indent, need_comma);
/* Close JSON output. */ /* Close JSON output. */
puts("\n}"); fputs("\n}\n", export_fp);
fclose(export_fp);
done: done:
debug_return_bool(ok); debug_return_bool(ok);