From: Todd C. Miller Date: Sun, 20 May 2018 13:01:26 +0000 (-0600) Subject: Add support for base64-encoding non-safe strings in LDIF output. X-Git-Tag: SUDO_1_8_24^2~66 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1bc8e9abfde1f117446ea62b35cdcfdebc3424af;p=sudo Add support for base64-encoding non-safe strings in LDIF output. --- diff --git a/MANIFEST b/MANIFEST index 49e5f573b..bde1f83ce 100644 --- a/MANIFEST +++ b/MANIFEST @@ -431,6 +431,8 @@ plugins/sudoers/regress/cvtsudoers/test24.out.ok plugins/sudoers/regress/cvtsudoers/test24.sh plugins/sudoers/regress/cvtsudoers/test25.out.ok plugins/sudoers/regress/cvtsudoers/test25.sh +plugins/sudoers/regress/cvtsudoers/test26.out.ok +plugins/sudoers/regress/cvtsudoers/test26.sh plugins/sudoers/regress/cvtsudoers/test3.out.ok plugins/sudoers/regress/cvtsudoers/test3.sh plugins/sudoers/regress/cvtsudoers/test4.out.ok diff --git a/plugins/sudoers/cvtsudoers_ldif.c b/plugins/sudoers/cvtsudoers_ldif.c index f56c11a02..e30fb6daf 100644 --- a/plugins/sudoers/cvtsudoers_ldif.c +++ b/plugins/sudoers/cvtsudoers_ldif.c @@ -60,6 +60,61 @@ seen_user_free(void *v) free(su); } +static bool +safe_string(const char *str) +{ + unsigned int ch = *str++; + debug_decl(safe_string, SUDOERS_DEBUG_UTIL) + + /* Initial char must be <= 127 and not LF, CR, SPACE, ':', '<' */ + switch (ch) { + case '\n': + case '\r': + case ' ': + case ':': + case '<': + debug_return_bool(false); + default: + if (ch > 127) + debug_return_bool(false); + } + + /* Any value <= 127 decimal except NUL, LF, and CR is safe */ + while ((ch = *str++) != '\0') { + if (ch > 127 || ch == '\n' || ch == '\r') + debug_return_bool(false); + } + + debug_return_bool(true); +} + +static bool +print_attribute_ldif(FILE *fp, const char *name, const char *value) +{ + char *encoded = NULL; + size_t esize; + debug_decl(print_attribute_ldif, SUDOERS_DEBUG_UTIL) + + if (!safe_string(value)) { + const size_t vlen = strlen(value); + esize = ((vlen + 2) / 3 * 4) + 1; + if ((encoded = malloc(esize)) == NULL) + debug_return_bool(false); + if (base64_encode(value, vlen, encoded, esize) == (size_t)-1) { + free(encoded); + debug_return_bool(false); + } + fprintf(fp, "%s:: %s\n", name, encoded); + free(encoded); + } else if (*value != '\0') { + fprintf(fp, "%s: %s\n", name, value); + } else { + fprintf(fp, "%s:\n", name); + } + + debug_return_bool(true); +} + /* * Print sudoOptions from a defaults_list. */ @@ -67,6 +122,8 @@ static bool print_options_ldif(FILE *fp, struct defaults_list *options) { struct defaults *opt; + char *attr_val; + int len; debug_decl(print_options_ldif, SUDOERS_DEBUG_UTIL) TAILQ_FOREACH(opt, options, entries) { @@ -75,13 +132,19 @@ print_options_ldif(FILE *fp, struct defaults_list *options) if (opt->val != NULL) { /* There is no need to double quote values here. */ - fprintf(fp, "sudoOption: %s%s%s\n", opt->var, + len = asprintf(&attr_val, "%s%s%s", opt->var, opt->op == '+' ? "+=" : opt->op == '-' ? "-=" : "=", opt->val); } else { /* Boolean flag. */ - fprintf(fp, "sudoOption: %s%s\n", opt->op == false ? "!" : "", - opt->var); + len = asprintf(&attr_val, "%s%s", + opt->op == false ? "!" : "", opt->var); } + if (len == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "sudoOption", attr_val); + free(attr_val); } debug_return_bool(!ferror(fp)); @@ -96,6 +159,7 @@ print_global_defaults_ldif(FILE *fp, const char *base) unsigned int count = 0; struct sudo_lbuf lbuf; struct defaults *opt; + char *dn; debug_decl(print_global_defaults_ldif, SUDOERS_DEBUG_UTIL) sudo_lbuf_init(&lbuf, NULL, 0, NULL, 80); @@ -117,11 +181,16 @@ print_global_defaults_ldif(FILE *fp, const char *base) if (count == 0) debug_return_bool(true); - fprintf(fp, "dn: cn=defaults,%s\n", base); - fputs("objectClass: top\n", fp); - fputs("objectClass: sudoRole\n", fp); - fputs("cn: defaults\n", fp); - fputs("description: Default sudoOption's go here\n", fp); + if (asprintf(&dn, "cn=defaults,%s", base) == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "dn", dn); + free(dn); + print_attribute_ldif(fp, "objectClass", "top"); + print_attribute_ldif(fp, "objectClass", "sudoRole"); + print_attribute_ldif(fp, "cn", "defaults"); + print_attribute_ldif(fp, "description", "Default sudoOption's go here"); print_options_ldif(fp, &defaults); putc('\n', fp); @@ -130,50 +199,60 @@ print_global_defaults_ldif(FILE *fp, const char *base) } /* - * Print struct member in LDIF format, with specified prefix. + * Print struct member in LDIF format as the specified attribute. * See print_member_int() in parse.c. */ static void print_member_ldif(FILE *fp, char *name, int type, bool negated, - int alias_type, const char *prefix) + int alias_type, const char *attr_name) { struct alias *a; struct member *m; struct sudo_command *c; + char *attr_val; + int len; debug_decl(print_member_ldif, SUDOERS_DEBUG_UTIL) switch (type) { case ALL: - fprintf(fp, "%s: %sALL\n", prefix, negated ? "!" : ""); + print_attribute_ldif(fp, attr_name, negated ? "!ALL" : "ALL"); break; case MYSELF: /* Only valid for sudoRunasUser */ - fprintf(fp, "%s:\n", prefix); + print_attribute_ldif(fp, attr_name, ""); break; case COMMAND: c = (struct sudo_command *)name; - fprintf(fp, "%s: ", prefix); - if (c->digest != NULL) { - fprintf(fp, "%s:%s ", digest_type_to_name(c->digest->digest_type), - c->digest->digest_str); + len = asprintf(&attr_val, "%s%s%s%s%s%s%s%s", + c->digest ? digest_type_to_name(c->digest->digest_type) : "", + c->digest ? ":" : "", c->digest ? c->digest->digest_str : "", + c->digest ? " " : "", negated ? "!" : "", c->cmnd, + c->args ? " " : "", c->args ? c->args : ""); + if (len == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); } - fprintf(fp, "%s%s", negated ? "!" : "", c->cmnd); - if (c->args != NULL) - fprintf(fp, " %s", c->args); - putc('\n', fp); + print_attribute_ldif(fp, attr_name, attr_val); + free(attr_val); break; case ALIAS: if ((a = alias_get(name, alias_type)) != NULL) { TAILQ_FOREACH(m, &a->members, entries) { print_member_ldif(fp, m->name, m->type, - negated ? !m->negated : m->negated, alias_type, prefix); + negated ? !m->negated : m->negated, alias_type, attr_name); } alias_put(a); break; } /* FALLTHROUGH */ default: - fprintf(fp, "%s: %s%s\n", prefix, negated ? "!" : "", name); + len = asprintf(&attr_val, "%s%s", negated ? "!" : "", name); + if (len == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, attr_name, attr_val); + free(attr_val); break; } @@ -219,7 +298,7 @@ print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp, stru if (strftime(timebuf, sizeof(timebuf), "%Y%m%d%H%M%SZ", tp) == 0) { sudo_warnx(U_("unable to format timestamp")); } else { - fprintf(fp, "sudoNotBefore: %s\n", timebuf); + print_attribute_ldif(fp, "sudoNotBefore", timebuf); } } } @@ -230,14 +309,20 @@ print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp, stru if (strftime(timebuf, sizeof(timebuf), "%Y%m%d%H%M%SZ", tp) == 0) { sudo_warnx(U_("unable to format timestamp")); } else { - fprintf(fp, "sudoNotAfter: %s\n", timebuf); + print_attribute_ldif(fp, "sudoNotAfter", timebuf); } } } /* Print timeout as a sudoOption. */ if (cs->timeout > 0) { - fprintf(fp, "sudoOption: command_timeout=%d\n", cs->timeout); + char *attr_val; + if (asprintf(&attr_val, "command_timeout=%d", cs->timeout) == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "sudoOption", attr_val); + free(attr_val); } /* Print tags as sudoOption attributes */ @@ -245,31 +330,37 @@ print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp, stru struct cmndtag tag = cs->tags; if (tag.nopasswd != UNSPEC) { - fprintf(fp, "sudoOption: %sauthenticate\n", tag.nopasswd ? "!" : ""); + print_attribute_ldif(fp, "sudoOption", + tag.nopasswd ? "!authenticate" : "authenticate"); } if (tag.noexec != UNSPEC) { - fprintf(fp, "sudoOption: %snoexec\n", tag.noexec ? "" : "!"); + print_attribute_ldif(fp, "sudoOption", + tag.noexec ? "noexec" : "!noexec"); } if (tag.send_mail != UNSPEC) { if (tag.send_mail) { - fprintf(fp, "sudoOption: mail_all_cmnds\n"); + print_attribute_ldif(fp, "sudoOption", "mail_all_cmnds"); } else { - fprintf(fp, "sudoOption: !mail_all_cmnds\n"); - fprintf(fp, "sudoOption: !mail_always\n"); - fprintf(fp, "sudoOption: !mail_no_perms\n"); + print_attribute_ldif(fp, "sudoOption", "!mail_all_cmnds"); + print_attribute_ldif(fp, "sudoOption", "!mail_always"); + print_attribute_ldif(fp, "sudoOption", "!mail_no_perms"); } } if (tag.setenv != UNSPEC && tag.setenv != IMPLIED) { - fprintf(fp, "sudoOption: %ssetenv\n", tag.setenv ? "" : "!"); + print_attribute_ldif(fp, "sudoOption", + tag.setenv ? "setenv" : "!setenv"); } if (tag.follow != UNSPEC) { - fprintf(fp, "sudoOption: %ssudoedit_follow\n", tag.follow ? "" : "!"); + print_attribute_ldif(fp, "sudoOption", + tag.follow ? "sudoedit_follow" : "!sudoedit_follow"); } if (tag.log_input != UNSPEC) { - fprintf(fp, "sudoOption: %slog_input\n", tag.log_input ? "" : "!"); + print_attribute_ldif(fp, "sudoOption", + tag.log_input ? "log_input" : "!log_input"); } if (tag.log_output != UNSPEC) { - fprintf(fp, "sudoOption: %slog_output\n", tag.log_output ? "" : "!"); + print_attribute_ldif(fp, "sudoOption", + tag.log_output ? "log_output" : "!log_output"); } } print_options_ldif(fp, options); @@ -277,18 +368,49 @@ print_cmndspec_ldif(FILE *fp, struct cmndspec *cs, struct cmndspec **nextp, stru #ifdef HAVE_SELINUX /* Print SELinux role/type */ if (cs->role != NULL && cs->type != NULL) { - fprintf(fp, "sudoOption: role=%s\n", cs->role); - fprintf(fp, "sudoOption: type=%s\n", cs->type); + char *attr_val; + + len = asprintf(&attr_val, "role=%s", cs->role); + if (len == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "sudoOption", attr_val); + free(attr_val); + + len = asprintf(&attr_val, "type=%s", cs->type); + if (len == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "sudoOption", attr_val); + free(attr_val); } #endif /* HAVE_SELINUX */ #ifdef HAVE_PRIV_SET /* Print Solaris privs/limitprivs */ if (cs->privs != NULL || cs->limitprivs != NULL) { - if (cs->privs != NULL) - fprintf(fp, "sudoOption: privs=%s\n", cs->privs); - if (cs->limitprivs != NULL) - fprintf(fp, "sudoOption: limitprivs=%s\n", cs->limitprivs); + char *attr_val; + + if (cs->privs != NULL) { + len = asprintf(&attr_val, "privs=%s", cs->privs); + if (len == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "sudoOption", attr_val); + free(attr_val); + } + if (cs->limitprivs != NULL) { + len = asprintf(&attr_val, "limitprivs=%s", cs->limitprivs); + if (len == -1) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + print_attribute_ldif(fp, "sudoOption", attr_val); + free(attr_val); + } } #endif /* HAVE_PRIV_SET */ @@ -419,7 +541,8 @@ print_userspec_ldif(FILE *fp, struct userspec *us, struct cvtsudoers_config *con */ TAILQ_FOREACH(priv, &us->privileges, entries) { TAILQ_FOREACH_SAFE(cs, &priv->cmndlist, entries, next) { - char *cn; + const char *base = conf->sudoers_base; + char *cn, *dn; /* * Increment the number of times we have seen this user. @@ -427,16 +550,17 @@ print_userspec_ldif(FILE *fp, struct userspec *us, struct cvtsudoers_config *con */ m = TAILQ_FIRST(&us->users); cn = user_to_cn(m->type == ALL ? "ALL" : m->name); - if (cn == NULL) { + if (cn == NULL || asprintf(&dn, "cn=%s,%s", cn, base) == -1) { sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); } - fprintf(fp, "dn: cn=%s,%s\n", cn, conf->sudoers_base); - fprintf(fp, "objectClass: top\n"); - fprintf(fp, "objectClass: sudoRole\n"); - fprintf(fp, "cn: %s\n", cn); + print_attribute_ldif(fp, "dn", dn); + print_attribute_ldif(fp, "objectClass", "top"); + print_attribute_ldif(fp, "objectClass", "sudoRole"); + print_attribute_ldif(fp, "cn", cn); free(cn); + free(dn); TAILQ_FOREACH(m, &us->users, entries) { print_member_ldif(fp, m->name, m->type, m->negated, @@ -451,7 +575,10 @@ print_userspec_ldif(FILE *fp, struct userspec *us, struct cvtsudoers_config *con print_cmndspec_ldif(fp, cs, &next, &priv->defaults); if (conf->sudo_order != 0) { - fprintf(fp, "sudoOrder: %u\n\n", conf->sudo_order); + char numbuf[(((sizeof(conf->sudo_order) * 8) + 2) / 3) + 2]; + (void)snprintf(numbuf, sizeof(numbuf), "%u", conf->sudo_order); + print_attribute_ldif(fp, "sudoOrder", numbuf); + putc('\n', fp); conf->sudo_order += conf->order_increment; } } diff --git a/plugins/sudoers/regress/cvtsudoers/test26.out.ok b/plugins/sudoers/regress/cvtsudoers/test26.out.ok new file mode 100644 index 000000000..ab9c948ee --- /dev/null +++ b/plugins/sudoers/regress/cvtsudoers/test26.out.ok @@ -0,0 +1,16 @@ +dn:: Y249ZGVmYXVsdHMsb3U9U1VET2Vyc8KpLGRjPXN1ZG8sZGM9d3M= +objectClass: top +objectClass: sudoRole +cn: defaults +description: Default sudoOption's go here +sudoOption:: YmFkcGFzc19tZXNzYWdlPUJhZCBwYXNzd29yZMKh + +dn:: Y249cm9vdCxvdT1TVURPZXJzwqksZGM9c3VkbyxkYz13cw== +objectClass: top +objectClass: sudoRole +cn: root +sudoUser: root +sudoHost: ALL +sudoCommand: ALL +sudoOrder: 1 + diff --git a/plugins/sudoers/regress/cvtsudoers/test26.sh b/plugins/sudoers/regress/cvtsudoers/test26.sh new file mode 100755 index 000000000..afc29a880 --- /dev/null +++ b/plugins/sudoers/regress/cvtsudoers/test26.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# Test base64 encoding of non-safe strings +# + +exec 2>&1 +./cvtsudoers -c "" -b "ou=SUDOers©,dc=sudo,dc=ws" <