# include "compat/getopt.h"
#endif /* HAVE_GETOPT_LONG */
-static bool convert_sudoers_sudoers(const char *output_file);
-extern bool convert_sudoers_json(const char *output_file);
+static bool convert_sudoers_sudoers(const char *output_file, bool expand_aliases);
+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 void get_hostname(void);
*/
struct sudo_user sudo_user;
struct passwd *list_pw;
-static const char short_opts[] = "b:f:ho:V";
+static const char short_opts[] = "b:ef:ho: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
const char *input_file = "-";
const char *output_file = "-";
const char *sudoers_base = NULL;
+ bool expand_aliases = false;
debug_decl(main, SUDOERS_DEBUG_MAIN)
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
case 'b':
sudoers_base = optarg;
break;
+ case 'e':
+ expand_aliases = true;
+ break;
case 'f':
if (strcasecmp(optarg, "json") == 0) {
output_format = output_json;
switch (output_format) {
case output_json:
- exitcode = !convert_sudoers_json(output_file);
+ 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);
+ exitcode = !convert_sudoers_sudoers(output_file, expand_aliases);
break;
default:
sudo_fatalx("error: unhandled output format %d", output_format);
* Display Defaults entries
*/
static bool
-print_defaults_sudoers(struct sudo_lbuf *lbuf)
+print_defaults_sudoers(struct sudo_lbuf *lbuf, bool expand_aliases)
{
struct defaults *def, *next;
- struct member *m;
debug_decl(print_defaults_sudoers, SUDOERS_DEBUG_UTIL)
TAILQ_FOREACH_SAFE(def, &defaults, entries, next) {
+ struct member *m;
+ int alias_type;
+
/* Print Defaults type and binding (if present) */
switch (def->type) {
- case DEFAULTS:
- sudo_lbuf_append(lbuf, "Defaults");
- break;
case DEFAULTS_HOST:
sudo_lbuf_append(lbuf, "Defaults@");
+ alias_type = HOSTALIAS;
break;
case DEFAULTS_USER:
sudo_lbuf_append(lbuf, "Defaults:");
+ alias_type = expand_aliases ? USERALIAS : UNSPEC;
break;
case DEFAULTS_RUNAS:
sudo_lbuf_append(lbuf, "Defaults>");
+ alias_type = expand_aliases ? RUNASALIAS : UNSPEC;
break;
case DEFAULTS_CMND:
sudo_lbuf_append(lbuf, "Defaults!");
+ alias_type = expand_aliases ? CMNDALIAS : UNSPEC;
+ break;
+ default:
+ sudo_lbuf_append(lbuf, "Defaults");
+ alias_type = UNSPEC;
break;
}
TAILQ_FOREACH(m, def->binding, entries) {
if (m != TAILQ_FIRST(def->binding))
sudo_lbuf_append(lbuf, ", ");
- sudoers_format_member(lbuf, m, NULL, UNSPEC);
+ sudoers_format_member(lbuf, m, ", ", alias_type);
}
/* Print Defaults with the same binding, there may be multiple. */
* Convert back to sudoers.
*/
static bool
-convert_sudoers_sudoers(const char *output_file)
+convert_sudoers_sudoers(const char *output_file, bool expand_aliases)
{
bool ret = true;
struct sudo_lbuf lbuf;
sudo_lbuf_init(&lbuf, convert_sudoers_output, 4, "\\", 80);
/* Print Defaults */
- if (!print_defaults_sudoers(&lbuf))
+ if (!print_defaults_sudoers(&lbuf, expand_aliases))
goto done;
if (lbuf.len > 0) {
sudo_lbuf_print(&lbuf);
}
/* Print Aliases */
- if (!print_aliases_sudoers(&lbuf))
- goto done;
- if (lbuf.len > 1) {
- sudo_lbuf_print(&lbuf);
- sudo_lbuf_append(&lbuf, "\n");
+ if (!expand_aliases) {
+ if (!print_aliases_sudoers(&lbuf))
+ goto done;
+ if (lbuf.len > 1) {
+ sudo_lbuf_print(&lbuf);
+ sudo_lbuf_append(&lbuf, "\n");
+ }
}
/* Print User_Specs */
- if (!sudoers_format_userspecs(&lbuf, &userspecs, false))
+ if (!sudoers_format_userspecs(&lbuf, &userspecs, expand_aliases))
goto done;
if (lbuf.len > 1) {
sudo_lbuf_print(&lbuf);
static void
usage(int fatal)
{
- (void) fprintf(fatal ? stderr : stdout,
- "usage: %s [-hV] [-b dn] [-f format] [-o output_file] [sudoers_file]\n",
- getprogname());
+ (void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehV] [-b dn] "
+ "[-f format] [-o output_file] [sudoers_file]\n", getprogname());
if (fatal)
exit(1);
}
* that closes the object.
*/
static void
-print_command_json(FILE *fp, struct member *m, int indent, bool last_one)
+print_command_json(FILE *fp, const char *name, int type, bool negated, int indent, bool last_one)
{
- struct sudo_command *c = (struct sudo_command *)m->name;
+ struct sudo_command *c = (struct sudo_command *)name;
struct json_value value;
const char *digest_name;
debug_decl(print_command_json, SUDOERS_DEBUG_UTIL)
printstr_json(fp, "{", NULL, NULL, indent);
- if (m->negated || c->digest != NULL) {
+ if (negated || c->digest != NULL) {
putc('\n', fp);
indent += 4;
} else {
}
/* Command may be negated. */
- if (m->negated) {
+ if (negated) {
fputs(",\n", fp);
value.type = JSON_BOOL;
value.u.boolean = true;
* that closes the object.
*/
static void
-print_member_json(FILE *fp, struct member *m, enum word_type word_type,
- bool last_one, int indent)
+print_member_json_int(FILE *fp, char *name, int type, bool negated,
+ enum word_type word_type, bool last_one, int indent, bool expand_aliases)
{
struct json_value value;
const char *typestr;
const char *errstr;
+ int alias_type = UNSPEC;
+ bool need_newline = true;
id_t id;
debug_decl(print_member_json, SUDOERS_DEBUG_UTIL)
/* Most of the time we print a string. */
value.type = JSON_STRING;
- value.u.string = m->name;
+ value.u.string = name;
- switch (m->type) {
+ switch (type) {
case USERGROUP:
value.u.string++; /* skip leading '%' */
if (*value.u.string == ':') {
value.u.string++;
typestr = "nonunixgroup";
if (*value.u.string == '#') {
- id = sudo_strtoid(m->name + 3, NULL, NULL, &errstr);
+ id = sudo_strtoid(name + 3, NULL, NULL, &errstr);
if (errstr != NULL) {
sudo_warnx("internal error: non-Unix group ID %s: \"%s\"",
- errstr, m->name);
+ errstr, name);
} else {
value.type = JSON_ID;
value.u.id = id;
} else {
typestr = "usergroup";
if (*value.u.string == '#') {
- id = sudo_strtoid(m->name + 2, NULL, NULL, &errstr);
+ id = sudo_strtoid(name + 2, NULL, NULL, &errstr);
if (errstr != NULL) {
sudo_warnx("internal error: group ID %s: \"%s\"",
- errstr, m->name);
+ errstr, name);
} else {
value.type = JSON_ID;
value.u.id = id;
typestr = "networkaddr";
break;
case COMMAND:
- print_command_json(fp, m, indent, last_one);
+ print_command_json(fp, name, type, negated, indent, last_one);
debug_return;
case ALL:
value.u.string = "ALL";
case TYPE_USERNAME:
typestr = "username";
if (*value.u.string == '#') {
- id = sudo_strtoid(m->name + 1, NULL, NULL, &errstr);
+ id = sudo_strtoid(name + 1, NULL, NULL, &errstr);
if (errstr != NULL) {
sudo_warnx("internal error: user ID %s: \"%s\"",
- errstr, m->name);
+ errstr, name);
} else {
value.type = JSON_ID;
value.u.id = id;
case ALIAS:
switch (word_type) {
case TYPE_COMMAND:
- typestr = "cmndalias";
+ if (expand_aliases) {
+ alias_type = CMNDALIAS;
+ } else {
+ typestr = "cmndalias";
+ }
break;
case TYPE_HOSTNAME:
- typestr = "hostalias";
+ if (expand_aliases) {
+ alias_type = HOSTALIAS;
+ } else {
+ typestr = "hostalias";
+ }
break;
case TYPE_RUNASGROUP:
case TYPE_RUNASUSER:
- typestr = "runasalias";
+ if (expand_aliases) {
+ alias_type = RUNASALIAS;
+ } else {
+ typestr = "runasalias";
+ }
break;
case TYPE_USERNAME:
- typestr = "useralias";
+ if (expand_aliases) {
+ alias_type = USERALIAS;
+ } else {
+ typestr = "useralias";
+ }
break;
default:
sudo_fatalx("unexpected word type %d", word_type);
}
break;
default:
- sudo_fatalx("unexpected member type %d", m->type);
+ sudo_fatalx("unexpected member type %d", type);
}
- if (m->negated) {
+ if (expand_aliases && type == ALIAS) {
+ struct alias *a;
+ struct member *m;
+
+ if ((a = alias_get(name, alias_type)) != NULL) {
+ TAILQ_FOREACH(m, &a->members, entries) {
+ print_member_json_int(fp, m->name, m->type,
+ negated ? !m->negated : m->negated,
+ alias_to_word_type(alias_type),
+ last_one && TAILQ_NEXT(m, entries) == NULL, indent, true);
+ }
+ alias_put(a);
+ need_newline = false;
+ }
+ } else if (negated) {
print_indent(fp, indent);
fputs("{\n", fp);
indent += 4;
}
if (!last_one)
putc(',', fp);
- putc('\n', fp);
+ if (need_newline)
+ putc('\n', fp);
debug_return;
}
+static void
+print_member_json(FILE *fp, struct member *m, enum word_type word_type,
+ bool last_one, int indent, bool expand_aliases)
+{
+ return print_member_json_int(fp, m->name, m->type, m->negated, word_type,
+ last_one, indent, expand_aliases);
+}
+
/*
* Callback for alias_apply() to print an alias entry if it matches
* the type specified in the closure.
TAILQ_FOREACH(m, &a->members, entries) {
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, false);
}
closure->indent -= 4;
debug_return_int(0);
* Print the binding for a Defaults entry of the specified type.
*/
static void
-print_binding_json(FILE *fp, struct member_list *binding, int type, int indent)
+print_binding_json(FILE *fp, struct member_list *binding, int type, int indent, bool expand_aliases)
{
struct member *m;
debug_decl(print_binding_json, SUDOERS_DEBUG_UTIL)
/* Print each member object in binding. */
TAILQ_FOREACH(m, binding, entries) {
print_member_json(fp, m, defaults_to_word_type(type),
- TAILQ_NEXT(m, entries) == NULL, indent);
+ TAILQ_NEXT(m, entries) == NULL, indent, expand_aliases);
}
indent -= 4;
* Export all Defaults in JSON format.
*/
static bool
-print_defaults_json(FILE *fp, int indent, bool need_comma)
+print_defaults_json(FILE *fp, int indent, bool expand_aliases, bool need_comma)
{
struct json_value value;
struct defaults *def, *next;
/* Found it, print object container and binding (if any). */
fprintf(fp, "%*s{\n", indent, "");
indent += 4;
- print_binding_json(fp, def->binding, def->type, indent);
+ print_binding_json(fp, def->binding, def->type, indent, expand_aliases);
/* Validation checks. */
/* XXX - validate values in addition to names? */
*/
static void
print_cmndspec_json(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp,
- int indent)
+ bool expand_aliases, int indent)
{
struct cmndspec *next = *nextp;
struct json_value value;
indent += 4;
TAILQ_FOREACH(m, cs->runasuserlist, entries) {
print_member_json(fp, m, TYPE_RUNASUSER,
- TAILQ_NEXT(m, entries) == NULL, indent);
+ TAILQ_NEXT(m, entries) == NULL, indent, expand_aliases);
}
indent -= 4;
fprintf(fp, "%*s],\n", indent, "");
indent += 4;
TAILQ_FOREACH(m, cs->runasgrouplist, entries) {
print_member_json(fp, m, TYPE_RUNASGROUP,
- TAILQ_NEXT(m, entries) == NULL, indent);
+ TAILQ_NEXT(m, entries) == NULL, indent, expand_aliases);
}
indent -= 4;
fprintf(fp, "%*s],\n", indent, "");
#endif /* HAVE_SELINUX */
;
- print_member_json(fp, cs->cmnd, TYPE_COMMAND, last_one, indent);
+ print_member_json(fp, cs->cmnd, TYPE_COMMAND,
+ last_one, indent, expand_aliases);
if (last_one)
break;
cs = next;
* Print a User_Spec in JSON format at the specified indent level.
*/
static void
-print_userspec_json(FILE *fp, struct userspec *us, int indent)
+print_userspec_json(FILE *fp, struct userspec *us, int indent, bool expand_aliases)
{
struct privilege *priv;
struct member *m;
indent += 4;
TAILQ_FOREACH(m, &us->users, entries) {
print_member_json(fp, m, TYPE_USERNAME,
- TAILQ_NEXT(m, entries) == NULL, indent);
+ TAILQ_NEXT(m, entries) == NULL, indent, expand_aliases);
}
indent -= 4;
fprintf(fp, "%*s],\n", indent, "");
indent += 4;
TAILQ_FOREACH(m, &priv->hostlist, entries) {
print_member_json(fp, m, TYPE_HOSTNAME,
- TAILQ_NEXT(m, entries) == NULL, indent);
+ TAILQ_NEXT(m, entries) == NULL, indent, expand_aliases);
}
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, indent);
+ print_cmndspec_json(fp, cs, &next, expand_aliases, indent);
}
indent -= 4;
fprintf(fp, "%*s]\n", indent, "");
}
static bool
-print_userspecs_json(FILE *fp, int indent, bool need_comma)
+print_userspecs_json(FILE *fp, int indent, bool expand_aliases, bool need_comma)
{
struct userspec *us;
debug_decl(print_userspecs_json, SUDOERS_DEBUG_UTIL)
fprintf(fp, "%s\n%*s\"User_Specs\": [\n", need_comma ? "," : "", indent, "");
indent += 4;
TAILQ_FOREACH(us, &userspecs, entries) {
- print_userspec_json(fp, us, indent);
+ print_userspec_json(fp, us, indent, expand_aliases);
}
indent -= 4;
fprintf(fp, "%*s]", indent, "");
* Export the parsed sudoers file in JSON format.
*/
bool
-convert_sudoers_json(const char *output_file)
+convert_sudoers_json(const char *output_file, bool expand_aliases)
{
bool ret = true, need_comma = false;
const int indent = 4;
putc('{', output_fp);
/* Dump Defaults in JSON format. */
- need_comma = print_defaults_json(output_fp, indent, need_comma);
+ need_comma = print_defaults_json(output_fp, indent, expand_aliases, need_comma);
/* Dump Aliases in JSON format. */
- need_comma = print_aliases_json(output_fp, indent, need_comma);
+ if (!expand_aliases)
+ need_comma = print_aliases_json(output_fp, indent, need_comma);
/* Dump User_Specs in JSON format. */
- print_userspecs_json(output_fp, indent, need_comma);
+ print_userspecs_json(output_fp, indent, expand_aliases, need_comma);
/* Close JSON output. */
fputs("\n}\n", output_fp);