From: Todd C. Miller Date: Sun, 4 Mar 2018 14:03:43 +0000 (-0700) Subject: Initial support for adding comments that will be emitted when X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5c36f9dec31c54f1ebc35ef5dcdd473736947205;p=sudo Initial support for adding comments that will be emitted when sudoers is formatted. Currently adds a comment for the source sudoRole when converting from ldif -> sudoers. --- diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 2e650327c..14b4dfbba 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -461,7 +461,7 @@ check: $(TEST_PROGS) visudo testsudoers diff $$json $(srcdir)/$$json.ok || true; \ fi; \ SUDOERS_BASE="ou=SUDOers,dc=sudo,dc=ws" \ - ./cvtsudoers -c "" -f ldif $$t >$$ldif 2>/dev/null || true; \ + ./cvtsudoers -c "" -f ldif < $$t >$$ldif 2>/dev/null || true; \ total=`expr $$total + 1`; \ if cmp $$ldif $(srcdir)/$$ldif.ok >/dev/null; then \ passed=`expr $$passed + 1`; \ diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c index ecb2614d1..e3fd02153 100644 --- a/plugins/sudoers/cvtsudoers.c +++ b/plugins/sudoers/cvtsudoers.c @@ -455,51 +455,9 @@ print_defaults_sudoers(struct sudo_lbuf *lbuf, bool expand_aliases) struct defaults *def, *next; 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_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, ", ", alias_type); - } + TAILQ_FOREACH_SAFE(def, &defaults, entries, next) + sudoers_format_default_line(lbuf, def, &next, expand_aliases); - /* Print Defaults with the same binding, there may be multiple. */ - for (;;) { - sudo_lbuf_append(lbuf, " "); - sudoers_format_default(lbuf, def); - next = TAILQ_NEXT(def, entries); - if (next == NULL || def->binding != next->binding) - break; - def = next; - sudo_lbuf_append(lbuf, ","); - } - sudo_lbuf_append(lbuf, "\n"); - } debug_return_bool(!sudo_lbuf_error(lbuf)); } diff --git a/plugins/sudoers/cvtsudoers_ldif.c b/plugins/sudoers/cvtsudoers_ldif.c index 563bde4fb..70488a797 100644 --- a/plugins/sudoers/cvtsudoers_ldif.c +++ b/plugins/sudoers/cvtsudoers_ldif.c @@ -34,6 +34,7 @@ #include "parse.h" #include "redblack.h" #include "cvtsudoers.h" +#include "sudo_lbuf.h" #include struct seen_user { @@ -95,14 +96,26 @@ static bool print_global_defaults_ldif(FILE *fp, const char *base) { unsigned int count = 0; + struct sudo_lbuf lbuf; struct defaults *opt; debug_decl(print_global_defaults_ldif, SUDOERS_DEBUG_UTIL) + sudo_lbuf_init(&lbuf, NULL, 0, NULL, 80); + TAILQ_FOREACH(opt, &defaults, entries) { /* Skip bound Defaults (unsupported). */ - if (opt->type == DEFAULTS) + if (opt->type == DEFAULTS) { count++; + } else { + lbuf.len = 0; + sudo_lbuf_append(&lbuf, "# "); + sudoers_format_default_line(&lbuf, opt, false, true); + fprintf(fp, "# Unable to translate %s:%d\n%s\n", + opt->file, opt->lineno, lbuf.buf); + } } + sudo_lbuf_destroy(&lbuf); + if (count == 0) debug_return_bool(true); @@ -118,24 +131,6 @@ print_global_defaults_ldif(FILE *fp, const char *base) debug_return_bool(!ferror(fp)); } -static void -warn_bound_defaults_ldif(FILE *fp) -{ - struct defaults *def; - debug_decl(warn_bound_defaults_ldif, SUDOERS_DEBUG_UTIL) - - TAILQ_FOREACH(def, &defaults, entries) { - if (def->type == DEFAULTS) - continue; /* only want bound defaults */ - - /* XXX - print Defaults line */ - sudo_warnx(U_("%s:%d unable to translate Defaults line"), - def->file, def->lineno); - } - - debug_return; -} - /* * Print struct member in LDIF format, with specified prefix. * See print_member_int() in parse.c. @@ -506,9 +501,6 @@ convert_sudoers_ldif(const char *output_file, struct cvtsudoers_config *conf) /* Dump User_Specs in LDIF format, expanding Aliases. */ print_userspecs_ldif(output_fp, conf); - /* Warn about non-translatable Defaults entries. */ - warn_bound_defaults_ldif(output_fp); - /* Clean up. */ rbdestroy(seen_users, seen_user_free); @@ -812,7 +804,6 @@ role_to_sudoers(struct sudo_role *role, bool store_options, /* * TODO: use cn to create a UserAlias if multiple users in it? - * TODO: add comment info based on cn? */ if (reuse_userspec) { @@ -826,6 +817,7 @@ role_to_sudoers(struct sudo_role *role, bool store_options, } TAILQ_INIT(&us->privileges); TAILQ_INIT(&us->users); + STAILQ_INIT(&us->comments); STAILQ_FOREACH(ls, role->users, entries) { char *user = ls->str; @@ -853,6 +845,38 @@ role_to_sudoers(struct sudo_role *role, bool store_options, } } + /* Add source role as a comment. */ + if (role->cn != NULL) { + struct comment *comment = NULL; + if (reuse_userspec) { + /* Try to re-use comment too. */ + STAILQ_FOREACH(comment, &us->comments, entries) { + if (strncmp(comment->str, "sudoRole ", 9) == 0) { + char *tmpstr; + if (asprintf(&tmpstr, "%s, %s", comment->str, role->cn) == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + free(comment->str); + comment->str = tmpstr; + break; + } + } + } + if (comment == NULL) { + /* Create a new comment. */ + if ((comment = malloc(sizeof(*comment))) == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + if (asprintf(&comment->str, "sudoRole %s", role->cn) == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + STAILQ_INSERT_TAIL(&us->comments, comment, 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), @@ -966,6 +990,32 @@ ldif_to_sudoers(struct sudo_role_list *roles, unsigned int numroles, debug_return; } +/* + * Given a cn with possible quoted characters, return a copy of + * the cn with quote characters ('\\') removed. + * The caller is responsible for freeing the returned string. + */ +static +char *unquote_cn(const char *src) +{ + char *dst, *new_cn; + size_t len; + debug_decl(unquote_cn, SUDOERS_DEBUG_UTIL) + + len = strlen(src); + if ((new_cn = malloc(len + 1)) == NULL) + debug_return_str(NULL); + + for (dst = new_cn; *src != '\0';) { + if (src[0] == '\\' && src[1] != '\0') + src++; + *dst++ = *src++; + } + *dst = '\0'; + + debug_return_str(new_cn); +} + /* * Parse a sudoers file in LDIF format, https://tools.ietf.org/html/rfc2849 * Parsed sudoRole objects are stored in the global sudoers data structures. @@ -1136,8 +1186,7 @@ parse_ldif(const char *input_file, struct cvtsudoers_config *conf) while (isblank((unsigned char)*cp)) cp++; free(role->cn); - /* XXX - unescape chars? */ - role->cn = strdup(cp); + role->cn = unquote_cn(cp); if (role->cn == NULL) { sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); diff --git a/plugins/sudoers/fmtsudoers.c b/plugins/sudoers/fmtsudoers.c index 39372199b..9ae812e44 100644 --- a/plugins/sudoers/fmtsudoers.c +++ b/plugins/sudoers/fmtsudoers.c @@ -256,9 +256,15 @@ sudoers_format_userspec(struct sudo_lbuf *lbuf, struct userspec *us, bool expand_aliases) { struct privilege *priv; + struct comment *comment; struct member *m; debug_decl(sudoers_format_userspec, SUDOERS_DEBUG_UTIL) + /* Print comments (if any). */ + STAILQ_FOREACH(comment, &us->comments, entries) { + sudo_lbuf_append(lbuf, "# %s\n", comment->str); + } + /* Print users list. */ TAILQ_FOREACH(m, &us->users, entries) { if (m != TAILQ_FIRST(&us->users)) @@ -291,9 +297,10 @@ sudoers_format_userspecs(struct sudo_lbuf *lbuf, struct userspec_list *usl, debug_decl(sudoers_format_userspecs, SUDOERS_DEBUG_UTIL) TAILQ_FOREACH(us, usl, entries) { + if (us != TAILQ_FIRST(usl)) + sudo_lbuf_append(lbuf, sep); if (!sudoers_format_userspec(lbuf, us, expand_aliases)) break; - sudo_lbuf_append(lbuf, sep); sudo_lbuf_print(lbuf); } @@ -322,3 +329,64 @@ sudoers_format_default(struct sudo_lbuf *lbuf, struct defaults *d) } debug_return_bool(!sudo_lbuf_error(lbuf)); } + +/* + * Format and append a defaults line to the specified lbuf. + * If next, is specified, it must point to the next defaults + * entry in the list; this is used to print multiple defaults + * entries with the same binding on a single line. + */ +bool +sudoers_format_default_line(struct sudo_lbuf *lbuf, struct defaults *d, + struct defaults **next, bool expand_aliases) +{ + struct member *m; + int alias_type; + debug_decl(sudoers_format_default_line, SUDOERS_DEBUG_UTIL) + + /* Print Defaults type and binding (if present) */ + switch (d->type) { + 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, d->binding, entries) { + if (m != TAILQ_FIRST(d->binding)) + sudo_lbuf_append(lbuf, ", "); + sudoers_format_member(lbuf, m, ", ", alias_type); + } + + sudo_lbuf_append(lbuf, " "); + sudoers_format_default(lbuf, d); + + if (next != NULL) { + /* Merge Defaults with the same binding, there may be multiple. */ + struct defaults *n = *next; + while ((n = TAILQ_NEXT(d, entries)) && d->binding == n->binding) { + sudo_lbuf_append(lbuf, ", "); + sudoers_format_default(lbuf, n); + d = n; + } + *next = n; + } + sudo_lbuf_append(lbuf, "\n"); + + debug_return_bool(!sudo_lbuf_error(lbuf)); +} diff --git a/plugins/sudoers/gram.c b/plugins/sudoers/gram.c index dca7253ae..02f95300f 100644 --- a/plugins/sudoers/gram.c +++ b/plugins/sudoers/gram.c @@ -825,6 +825,7 @@ add_userspec(struct member *members, struct privilege *privs) u->file = rcstr_addref(sudoers); HLTQ_TO_TAILQ(&u->users, members, entries); HLTQ_TO_TAILQ(&u->privileges, privs, entries); + STAILQ_INIT(&u->comments); TAILQ_INSERT_TAIL(&userspecs, u, entries); debug_return_bool(true); @@ -929,12 +930,18 @@ void free_userspec(struct userspec *us) { struct privilege *priv; + struct comment *comment; free_members(&us->users); while ((priv = TAILQ_FIRST(&us->privileges)) != NULL) { TAILQ_REMOVE(&us->privileges, priv, entries); free_privilege(priv); } + while ((comment = STAILQ_FIRST(&us->comments)) != NULL) { + STAILQ_REMOVE_HEAD(&us->comments, entries); + free(comment->str); + free(comment); + } rcstr_delref(us->file); free(us); } @@ -1016,7 +1023,7 @@ init_options(struct command_options *opts) opts->limitprivs = NULL; #endif } -#line 967 "gram.c" +#line 974 "gram.c" /* allocate initial stack or double stack size, up to YYMAXDEPTH */ #if defined(__cplusplus) || defined(__STDC__) static int yygrowstack(void) @@ -2141,7 +2148,7 @@ case 116: } } break; -#line 2092 "gram.c" +#line 2099 "gram.c" } yyssp -= yym; yystate = *yyssp; diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y index 5bc1003d3..cc54e1013 100644 --- a/plugins/sudoers/gram.y +++ b/plugins/sudoers/gram.y @@ -1053,6 +1053,7 @@ add_userspec(struct member *members, struct privilege *privs) u->file = rcstr_addref(sudoers); HLTQ_TO_TAILQ(&u->users, members, entries); HLTQ_TO_TAILQ(&u->privileges, privs, entries); + STAILQ_INIT(&u->comments); TAILQ_INSERT_TAIL(&userspecs, u, entries); debug_return_bool(true); @@ -1157,12 +1158,18 @@ void free_userspec(struct userspec *us) { struct privilege *priv; + struct comment *comment; free_members(&us->users); while ((priv = TAILQ_FIRST(&us->privileges)) != NULL) { TAILQ_REMOVE(&us->privileges, priv, entries); free_privilege(priv); } + while ((comment = STAILQ_FIRST(&us->comments)) != NULL) { + STAILQ_REMOVE_HEAD(&us->comments, entries); + free(comment->str); + free(comment); + } rcstr_delref(us->file); free(us); } diff --git a/plugins/sudoers/ldap.c b/plugins/sudoers/ldap.c index b35f1ee1b..8857c0ce3 100644 --- a/plugins/sudoers/ldap.c +++ b/plugins/sudoers/ldap.c @@ -1519,6 +1519,7 @@ ldap_to_sudoers(LDAP *ld, struct ldap_result *lres) goto oom; TAILQ_INIT(&us->users); TAILQ_INIT(&us->privileges); + STAILQ_INIT(&us->comments); TAILQ_INSERT_TAIL(ldap_userspecs, us, entries); /* The user has already matched, use ALL as wildcard. */ diff --git a/plugins/sudoers/ldap_util.c b/plugins/sudoers/ldap_util.c index e7ace9469..c6d37e186 100644 --- a/plugins/sudoers/ldap_util.c +++ b/plugins/sudoers/ldap_util.c @@ -461,6 +461,7 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, handled = false; } if (!handled && warnings) { + /* XXX - callback to process unsupported options. */ if (val != NULL) { sudo_warnx(U_("unable to convert sudoOption: %s%s%s"), var, op == '+' ? "+=" : op == '-' ? "-=" : "=", val); } else { diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index d17886f77..31dfc7fac 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -150,6 +150,7 @@ TAILQ_HEAD(userspec_list, userspec); TAILQ_HEAD(member_list, member); TAILQ_HEAD(privilege_list, privilege); TAILQ_HEAD(cmndspec_list, cmndspec); +STAILQ_HEAD(comment_list, comment); /* * Structure describing a user specification and list thereof. @@ -158,6 +159,7 @@ struct userspec { TAILQ_ENTRY(userspec) entries; struct member_list users; /* list of users */ struct privilege_list privileges; /* list of privileges */ + struct comment_list comments; /* optional comments */ int lineno; char *file; }; @@ -209,6 +211,11 @@ struct runascontainer { struct member *runasgroups; }; +struct comment { + STAILQ_ENTRY(comment) entries; + char *str; +}; + /* * Generic structure to hold {User,Host,Runas,Cmnd}_Alias * Aliases are stored in a red-black tree, sorted by name and type. @@ -312,6 +319,7 @@ int sudo_display_userspecs(struct userspec_list *usl, struct passwd *pw, struct /* fmtsudoers.c */ bool sudoers_format_cmndspec(struct sudo_lbuf *lbuf, struct cmndspec *cs, struct cmndspec *prev_cs, bool expand_aliases); bool sudoers_format_default(struct sudo_lbuf *lbuf, struct defaults *d); +bool sudoers_format_default_line(struct sudo_lbuf *lbuf, struct defaults *d, struct defaults **next, bool expand_aliases); bool sudoers_format_member(struct sudo_lbuf *lbuf, struct member *m, const char *separator, int alias_type); bool sudoers_format_privilege(struct sudo_lbuf *lbuf, struct privilege *priv, bool expand_aliases); bool sudoers_format_userspec(struct sudo_lbuf *lbuf, struct userspec *us, bool expand_aliases); diff --git a/plugins/sudoers/regress/sudoers/test2.ldif.ok b/plugins/sudoers/regress/sudoers/test2.ldif.ok index 9823707a9..947148797 100644 --- a/plugins/sudoers/regress/sudoers/test2.ldif.ok +++ b/plugins/sudoers/regress/sudoers/test2.ldif.ok @@ -1,3 +1,30 @@ +# Unable to translate stdin:26 +# Defaults@somehost set_home + +# Unable to translate stdin:27 +# Defaults@quoted\" set_home + +# Unable to translate stdin:30 +# Defaults:you set_home + +# Unable to translate stdin:31 +# Defaults:us\" set_home + +# Unable to translate stdin:32 +# Defaults:%them set_home + +# Unable to translate stdin:33 +# Defaults:"%: non UNIX 0 c" set_home + +# Unable to translate stdin:34 +# Defaults:+net set_home + +# Unable to translate stdin:37 +# Defaults>someone set_home + +# Unable to translate stdin:38 +# Defaults>"some one" set_home + dn: cn=foo,ou=SUDOers,dc=sudo,dc=ws objectClass: top objectClass: sudoRole diff --git a/plugins/sudoers/regress/sudoers/test3.ldif.ok b/plugins/sudoers/regress/sudoers/test3.ldif.ok index e69de29bb..0aa54be86 100644 --- a/plugins/sudoers/regress/sudoers/test3.ldif.ok +++ b/plugins/sudoers/regress/sudoers/test3.ldif.ok @@ -0,0 +1,12 @@ +# Unable to translate stdin:3 +# Defaults:foo, bar env_reset + +# Unable to translate stdin:4 +# Defaults:foo, bar env_reset + +# Unable to translate stdin:5 +# Defaults:foo, " bar" env_reset + +# Unable to translate stdin:6 +# Defaults:foo, bar env_reset + diff --git a/plugins/sudoers/regress/sudoers/test6.ldif.ok b/plugins/sudoers/regress/sudoers/test6.ldif.ok index 5ca28d10a..c4e11e4ff 100644 --- a/plugins/sudoers/regress/sudoers/test6.ldif.ok +++ b/plugins/sudoers/regress/sudoers/test6.ldif.ok @@ -1,3 +1,15 @@ +# Unable to translate stdin:2 +# Defaults:#123 set_home + +# Unable to translate stdin:3 +# Defaults>#123 set_home + +# Unable to translate stdin:4 +# Defaults:#123 set_home + +# Unable to translate stdin:5 +# Defaults>#123 set_home + dn: cn=\#0,ou=SUDOers,dc=sudo,dc=ws objectClass: top objectClass: sudoRole diff --git a/plugins/sudoers/sssd.c b/plugins/sudoers/sssd.c index 8cee7e684..1994f9c1c 100644 --- a/plugins/sudoers/sssd.c +++ b/plugins/sudoers/sssd.c @@ -1436,6 +1436,7 @@ sss_to_sudoers(struct sudo_sss_handle *handle, struct sss_sudo_result *sss_resul goto oom; TAILQ_INIT(&us->users); TAILQ_INIT(&us->privileges); + STAILQ_INIT(&us->comments); TAILQ_INSERT_TAIL(sss_userspecs, us, entries); /* The user has already matched, use ALL as wildcard. */