From 4eca4432461d647d1efc16a20326ec7efdfad151 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Wed, 11 Mar 2020 11:17:52 -0600 Subject: [PATCH] Allow a list of digests to be specified for a command. --- doc/sudoers.man.in | 20 +- doc/sudoers.mdoc.in | 20 +- plugins/sudoers/cvtsudoers_json.c | 11 +- plugins/sudoers/cvtsudoers_ldif.c | 58 +- plugins/sudoers/fmtsudoers.c | 9 +- plugins/sudoers/gram.c | 645 +++++++++--------- plugins/sudoers/gram.y | 40 +- plugins/sudoers/ldap_util.c | 182 +++-- plugins/sudoers/match.c | 2 +- plugins/sudoers/match_command.c | 46 +- plugins/sudoers/match_digest.c | 90 ++- plugins/sudoers/parse.h | 30 +- plugins/sudoers/regress/sudoers/test14.in | 4 +- .../sudoers/regress/sudoers/test14.json.ok | 6 +- .../sudoers/regress/sudoers/test14.ldif.ok | 4 +- .../regress/sudoers/test14.ldif2sudo.ok | 6 +- plugins/sudoers/regress/sudoers/test14.out.ok | 4 +- .../sudoers/regress/sudoers/test14.toke.ok | 4 +- plugins/sudoers/sudo_ldap.h | 1 - 19 files changed, 662 insertions(+), 520 deletions(-) diff --git a/doc/sudoers.man.in b/doc/sudoers.man.in index 73469936b..dc2b0a237 100644 --- a/doc/sudoers.man.in +++ b/doc/sudoers.man.in @@ -887,6 +887,9 @@ Digest_Spec ::= "sha224" ':' digest | "sha384" ':' digest | "sha512" ':' digest +Digest_List ::= Digest_Spec | + Digest_Spec ',' Digest_List + Cmnd_List ::= Cmnd | Cmnd ',' Cmnd_List @@ -894,7 +897,7 @@ command name ::= file name | file name args | file name '""' -Cmnd ::= Digest_Spec? '!'* command name | +Cmnd ::= Digest_List? '!'* command name | '!'* directory | '!'* "sudoedit" | '!'* Cmnd_Alias @@ -967,12 +970,17 @@ A fully-qualified path for is treated as an error by \fBvisudo\fR. .PP -If a +A \fRcommand name\fR -is prefixed with a -\fRDigest_Spec\fR, -the command will only match successfully if it can be verified -using the specified SHA-2 digest. +may be preceded by a +\fRDigest_List\fR, +a comma-separated list of one or more +\fRDigest_Spec\fR +entries. +If a +\fRDigest_List\fR +is present, the command will only match successfully if it can be verified +using one of the SHA-2 digests in the list. The following digest formats are supported: sha224, sha256, sha384 and sha512. The string may be specified in either hex or base64 format (base64 is more compact). diff --git a/doc/sudoers.mdoc.in b/doc/sudoers.mdoc.in index 3ee0c4d0e..f00ee67c9 100644 --- a/doc/sudoers.mdoc.in +++ b/doc/sudoers.mdoc.in @@ -852,6 +852,9 @@ Digest_Spec ::= "sha224" ':' digest | "sha384" ':' digest | "sha512" ':' digest +Digest_List ::= Digest_Spec | + Digest_Spec ',' Digest_List + Cmnd_List ::= Cmnd | Cmnd ',' Cmnd_List @@ -859,7 +862,7 @@ command name ::= file name | file name args | file name '""' -Cmnd ::= Digest_Spec? '!'* command name | +Cmnd ::= Digest_List? '!'* command name | '!'* directory | '!'* "sudoedit" | '!'* Cmnd_Alias @@ -931,12 +934,17 @@ A fully-qualified path for is treated as an error by .Nm visudo . .Pp -If a +A .Li command name -is prefixed with a -.Li Digest_Spec , -the command will only match successfully if it can be verified -using the specified SHA-2 digest. +may be preceded by a +.Li Digest_List , +a comma-separated list of one or more +.Li Digest_Spec +entries. +If a +.Li Digest_List +is present, the command will only match successfully if it can be verified +using one of the SHA-2 digests in the list. The following digest formats are supported: sha224, sha256, sha384 and sha512. The string may be specified in either hex or base64 format (base64 is more compact). diff --git a/plugins/sudoers/cvtsudoers_json.c b/plugins/sudoers/cvtsudoers_json.c index cf1d33778..9c801374c 100644 --- a/plugins/sudoers/cvtsudoers_json.c +++ b/plugins/sudoers/cvtsudoers_json.c @@ -70,6 +70,7 @@ static void print_command_json(struct json_container *json, const char *name, bool negated) { struct sudo_command *c = (struct sudo_command *)name; + struct command_digest *digest; struct json_value value; char *cmnd = c->cmnd; const char *digest_name; @@ -85,7 +86,7 @@ print_command_json(struct json_container *json, const char *name, bool negated) value.type = JSON_STRING; value.u.string = cmnd; - if (!negated && c->digest == NULL) { + if (!negated && TAILQ_EMPTY(&c->digests)) { /* Print as { "command": "command and args" } */ sudo_json_add_value_as_object(json, "command", &value); } else { @@ -93,11 +94,11 @@ print_command_json(struct json_container *json, const char *name, bool negated) sudo_json_open_object(json, NULL); sudo_json_add_value(json, "command", &value); - /* Optional digest. */ - if (c->digest != NULL) { - digest_name = digest_type_to_name(c->digest->digest_type); + /* Optional digest list. */ + TAILQ_FOREACH(digest, &c->digests, entries) { + digest_name = digest_type_to_name(digest->digest_type); value.type = JSON_STRING; - value.u.string = c->digest->digest_str; + value.u.string = digest->digest_str; sudo_json_add_value(json, digest_name, &value); } diff --git a/plugins/sudoers/cvtsudoers_ldif.c b/plugins/sudoers/cvtsudoers_ldif.c index cbcdc2d72..4ed34287e 100644 --- a/plugins/sudoers/cvtsudoers_ldif.c +++ b/plugins/sudoers/cvtsudoers_ldif.c @@ -209,6 +209,52 @@ print_global_defaults_ldif(FILE *fp, struct sudoers_parse_tree *parse_tree, debug_return_bool(!ferror(fp)); } +/* + * Format a sudo_command as a string. + * Returns the formatted, dynamically allocated string or dies on error. + */ +static char * +format_cmnd(struct sudo_command *c, bool negated) +{ + struct command_digest *digest; + char *buf, *cp; + size_t bufsiz; + int len; + debug_decl(format_cmnd, SUDOERS_DEBUG_UTIL); + + bufsiz = negated + strlen(c->cmnd) + 1; + if (c->args != NULL) + bufsiz += 1 + strlen(c->args); + TAILQ_FOREACH(digest, &c->digests, entries) { + bufsiz += strlen(digest_type_to_name(digest->digest_type)) + 1 + + strlen(digest->digest_str) + 1; + if (TAILQ_NEXT(digest, entries) != NULL) + bufsiz += 2; + } + + if ((buf = malloc(bufsiz)) == NULL) { + sudo_fatalx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + } + + cp = buf; + TAILQ_FOREACH(digest, &c->digests, entries) { + len = snprintf(cp, bufsiz - (cp - buf), "%s:%s%s ", + digest_type_to_name(digest->digest_type), digest->digest_str, + TAILQ_NEXT(digest, entries) ? "," : ""); + if (len < 0 || len >= (int)bufsiz - (cp - buf)) + sudo_fatalx(U_("internal error, %s overflow"), __func__); + cp += len; + } + + len = snprintf(cp, bufsiz - (cp - buf), "%s%s%s%s", negated ? "!" : "", + c->cmnd, c->args ? " " : "", c->args ? c->args : ""); + if (len < 0 || len >= (int)bufsiz - (cp - buf)) + sudo_fatalx(U_("internal error, %s overflow"), __func__); + + debug_return_str(buf); +} + /* * Print struct member in LDIF format as the specified attribute. * See print_member_int() in parse.c. @@ -219,7 +265,6 @@ print_member_ldif(FILE *fp, struct sudoers_parse_tree *parse_tree, char *name, { struct alias *a; struct member *m; - struct sudo_command *c; char *attr_val; int len; debug_decl(print_member_ldif, SUDOERS_DEBUG_UTIL); @@ -233,16 +278,7 @@ print_member_ldif(FILE *fp, struct sudoers_parse_tree *parse_tree, char *name, print_attribute_ldif(fp, attr_name, ""); break; case COMMAND: - c = (struct sudo_command *)name; - 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")); - } + attr_val = format_cmnd((struct sudo_command *)name, negated); print_attribute_ldif(fp, attr_name, attr_val); free(attr_val); break; diff --git a/plugins/sudoers/fmtsudoers.c b/plugins/sudoers/fmtsudoers.c index 68e6a076e..46dd35212 100644 --- a/plugins/sudoers/fmtsudoers.c +++ b/plugins/sudoers/fmtsudoers.c @@ -51,6 +51,7 @@ sudoers_format_member_int(struct sudo_lbuf *lbuf, struct alias *a; struct member *m; struct sudo_command *c; + struct command_digest *digest; debug_decl(sudoers_format_member_int, SUDOERS_DEBUG_UTIL); switch (type) { @@ -63,10 +64,10 @@ sudoers_format_member_int(struct sudo_lbuf *lbuf, break; case COMMAND: c = (struct sudo_command *) name; - if (c->digest != NULL) { - sudo_lbuf_append(lbuf, "%s:%s ", - digest_type_to_name(c->digest->digest_type), - c->digest->digest_str); + TAILQ_FOREACH(digest, &c->digests, entries) { + sudo_lbuf_append(lbuf, "%s:%s%s ", + digest_type_to_name(digest->digest_type), + digest->digest_str, TAILQ_NEXT(digest, entries) ? "," : ""); } if (negated) sudo_lbuf_append(lbuf, "!"); diff --git a/plugins/sudoers/gram.c b/plugins/sudoers/gram.c index e5b6f140f..21f2d9610 100644 --- a/plugins/sudoers/gram.c +++ b/plugins/sudoers/gram.c @@ -45,7 +45,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2018 + * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2020 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -192,18 +192,18 @@ const short sudoerslhs[] = short sudoerslhs[] = #endif { -1, - 0, 0, 32, 32, 33, 33, 33, 33, 33, 33, - 33, 33, 33, 33, 33, 33, 4, 4, 3, 3, + 0, 0, 33, 33, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 4, 4, 3, 3, 3, 3, 3, 21, 21, 20, 11, 11, 9, 9, 9, 9, 9, 2, 2, 1, 31, 31, 31, 31, - 7, 7, 6, 6, 28, 29, 30, 24, 25, 26, - 27, 18, 18, 19, 19, 19, 19, 19, 23, 23, - 23, 23, 23, 23, 23, 23, 22, 22, 22, 22, + 32, 32, 7, 7, 6, 6, 28, 29, 30, 24, + 25, 26, 27, 18, 18, 19, 19, 19, 19, 19, + 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 5, 5, 5, 35, 35, 38, 10, 10, 36, - 36, 39, 8, 8, 37, 37, 40, 34, 34, 41, - 14, 14, 12, 12, 13, 13, 13, 13, 13, 17, - 17, 15, 15, 16, 16, 16, + 22, 22, 22, 5, 5, 5, 36, 36, 39, 10, + 10, 37, 37, 40, 8, 8, 38, 38, 41, 35, + 35, 42, 14, 14, 12, 12, 13, 13, 13, 13, + 13, 17, 17, 15, 15, 16, 16, 16, }; #if defined(__cplusplus) || defined(__STDC__) const short sudoerslen[] = @@ -215,14 +215,14 @@ short sudoerslen[] = 2, 2, 3, 3, 3, 3, 1, 3, 1, 2, 3, 3, 3, 1, 3, 3, 1, 2, 1, 1, 1, 1, 1, 1, 3, 4, 3, 3, 3, 3, - 1, 2, 1, 2, 3, 3, 3, 3, 3, 3, - 3, 0, 3, 0, 1, 3, 2, 1, 0, 2, - 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, + 1, 3, 1, 2, 1, 2, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 0, 1, 3, 2, 1, + 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, - 3, 3, 1, 3, 1, 3, 3, 1, 3, 3, - 1, 3, 1, 2, 1, 1, 1, 1, 1, 1, - 3, 1, 2, 1, 1, 1, + 2, 2, 2, 1, 1, 1, 1, 3, 3, 1, + 3, 1, 3, 3, 1, 3, 1, 3, 3, 1, + 3, 3, 1, 3, 1, 2, 1, 1, 1, 1, + 1, 1, 3, 1, 2, 1, 1, 1, }; #if defined(__cplusplus) || defined(__STDC__) const short sudoersdefred[] = @@ -230,25 +230,25 @@ const short sudoersdefred[] = short sudoersdefred[] = #endif { 0, - 0, 105, 107, 108, 109, 0, 0, 0, 0, 0, - 106, 5, 0, 0, 0, 0, 0, 0, 101, 103, + 0, 107, 109, 110, 111, 0, 0, 0, 0, 0, + 108, 5, 0, 0, 0, 0, 0, 0, 103, 105, 0, 0, 3, 6, 0, 0, 17, 0, 29, 32, - 31, 33, 30, 0, 27, 0, 88, 0, 0, 84, - 83, 82, 0, 0, 0, 0, 0, 43, 41, 93, - 0, 0, 0, 0, 85, 0, 0, 90, 0, 0, - 98, 0, 0, 95, 104, 0, 0, 24, 0, 4, - 0, 0, 0, 20, 0, 28, 0, 0, 0, 0, - 44, 0, 0, 0, 0, 0, 0, 42, 0, 0, - 0, 0, 0, 0, 0, 0, 102, 0, 0, 21, - 22, 23, 18, 89, 37, 38, 39, 40, 94, 0, - 86, 0, 91, 0, 99, 0, 96, 0, 34, 0, - 59, 25, 0, 0, 0, 0, 0, 114, 116, 115, - 0, 110, 112, 0, 0, 53, 35, 0, 0, 0, - 0, 0, 0, 0, 0, 63, 64, 65, 66, 62, - 60, 61, 113, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 80, 81, 78, 79, 36, 111, 49, 48, - 50, 51, 45, 46, 47, + 31, 33, 30, 0, 27, 0, 90, 0, 0, 86, + 85, 84, 0, 0, 0, 0, 0, 45, 43, 95, + 0, 41, 0, 0, 0, 87, 0, 0, 92, 0, + 0, 100, 0, 0, 97, 106, 0, 0, 24, 0, + 4, 0, 0, 0, 20, 0, 28, 0, 0, 0, + 0, 46, 0, 0, 0, 0, 0, 0, 0, 44, + 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, + 0, 21, 22, 23, 18, 91, 37, 38, 39, 40, + 96, 42, 0, 88, 0, 93, 0, 101, 0, 98, + 0, 34, 0, 61, 25, 0, 0, 0, 0, 0, + 116, 118, 117, 0, 112, 114, 0, 0, 55, 35, + 0, 0, 0, 0, 0, 0, 0, 0, 65, 66, + 67, 68, 64, 62, 63, 115, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 82, 83, 80, 81, 36, + 113, 51, 50, 52, 53, 47, 48, 49, }; #if defined(__cplusplus) || defined(__STDC__) const short sudoersdgoto[] = @@ -256,11 +256,11 @@ const short sudoersdgoto[] = short sudoersdgoto[] = #endif { 18, - 119, 120, 27, 28, 48, 49, 50, 51, 35, 67, - 37, 19, 20, 21, 132, 133, 134, 121, 125, 68, - 69, 145, 127, 146, 147, 148, 149, 150, 151, 152, - 52, 22, 23, 60, 54, 57, 63, 55, 58, 64, - 61, + 122, 123, 27, 28, 48, 49, 50, 51, 35, 68, + 37, 19, 20, 21, 135, 136, 137, 124, 128, 69, + 70, 148, 130, 149, 150, 151, 152, 153, 154, 155, + 52, 53, 22, 23, 61, 55, 58, 64, 56, 59, + 65, 62, }; #if defined(__cplusplus) || defined(__STDC__) const short sudoerssindex[] = @@ -269,60 +269,60 @@ short sudoerssindex[] = #endif { 512, -272, 0, 0, 0, 0, -23, 227, -19, -19, -5, - 0, 0, -239, -236, -234, -232, -231, 0, 0, 0, - -33, 512, 0, 0, -3, -220, 0, 3, 0, 0, - 0, 0, 0, -225, 0, -28, 0, -24, -24, 0, - 0, 0, -240, -15, -8, 2, 4, 0, 0, 0, - -21, -12, -9, 6, 0, 7, 12, 0, 10, 14, - 0, 13, 25, 0, 0, -19, -36, 0, 26, 0, - -208, -202, -198, 0, -23, 0, 227, 3, 3, 3, - 0, -179, -178, -174, -173, -5, 3, 0, 227, -239, - -5, -236, -19, -234, -19, -232, 0, 52, 227, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, - 0, 51, 0, 54, 0, 54, 0, -29, 0, 55, - 0, 0, 289, -7, 59, 52, -216, 0, 0, 0, - -217, 0, 0, 57, 289, 0, 0, 32, 41, 42, - 43, 44, 45, 47, 450, 0, 0, 0, 0, 0, - 0, 0, 0, 289, 57, -154, -153, -150, -149, -148, - -147, -146, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, -239, -234, -231, -227, -225, 0, 0, 0, + -33, 512, 0, 0, -4, -224, 0, -2, 0, 0, + 0, 0, 0, -208, 0, -28, 0, -24, -24, 0, + 0, 0, -240, -11, 2, 3, 8, 0, 0, 0, + -21, 0, -12, -15, 9, 0, -7, 16, 0, -3, + 17, 0, 18, 20, 0, 0, -19, -18, 0, 22, + 0, -223, -207, -182, 0, -23, 0, 227, -2, -2, + -2, 0, -181, -180, -179, -178, -5, -2, -230, 0, + 227, -239, -5, -234, -19, -231, -19, -227, 0, 47, + 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 44, 0, 48, 0, 50, 0, 50, 0, + -29, 0, 51, 0, 0, 289, -36, 52, 47, -193, + 0, 0, 0, -233, 0, 0, 61, 289, 0, 0, + 45, 53, 55, 56, 57, 58, 59, 450, 0, 0, + 0, 0, 0, 0, 0, 0, 289, 61, -155, -154, + -153, -151, -150, -141, -140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,}; + 0, 0, 0, 0, 0, 0, 0, 0,}; #if defined(__cplusplus) || defined(__STDC__) const short sudoersrindex[] = #else short sudoersrindex[] = #endif - { 118, + { 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 119, 0, 0, 1, 0, 0, 145, 0, 0, + 0, 125, 0, 0, 1, 0, 0, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 159, 0, 0, 193, 0, 0, 207, - 0, 0, 241, 0, 0, 0, 0, 0, 275, 0, - 0, 0, 0, 0, 0, 0, 0, 309, 323, 357, - 0, 0, 0, 0, 0, 0, 371, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 404, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, - 0, 49, 0, 63, 0, 97, 0, 79, 0, 111, - 0, 0, 81, 82, 0, 404, 483, 0, 0, 0, - 0, 0, 0, 83, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 159, 0, 0, 193, 0, 0, + 207, 0, 0, 241, 0, 0, 0, 0, 0, 275, + 0, 0, 0, 0, 0, 0, 0, 0, 309, 323, + 357, 0, 0, 0, 0, 0, 0, 371, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 404, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 49, 0, 63, 0, 97, 0, + 85, 0, 111, 0, 0, 86, 87, 0, 404, 483, + 0, 0, 0, 0, 0, 0, 88, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,}; + 0, 0, 0, 0, 0, 0, 0, 0,}; #if defined(__cplusplus) || defined(__STDC__) const short sudoersgindex[] = #else short sudoersgindex[] = #endif { 0, - 5, 0, 53, 18, 86, 74, -79, 36, 98, -1, - 56, 68, 120, -6, -18, 8, 11, 0, 0, 39, + 4, 0, 60, 26, 89, 81, -80, 42, 103, -1, + 62, 71, 122, -6, -16, 12, 5, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 113, 0, 0, 0, 0, 58, 48, 46, - 60, + 64, 0, 0, 126, 0, 0, 0, 0, 65, 66, + 54, 67, }; #define YYTABLESIZE 801 #if defined(__cplusplus) || defined(__STDC__) @@ -331,23 +331,23 @@ const short sudoerstable[] = short sudoerstable[] = #endif { 34, - 19, 38, 39, 17, 26, 36, 109, 77, 26, 26, - 66, 26, 24, 17, 87, 77, 40, 41, 53, 66, - 43, 56, 86, 59, 98, 62, 2, 43, 123, 3, - 4, 5, 29, 19, 30, 31, 66, 32, 74, 72, - 128, 73, 82, 42, 19, 129, 75, 87, 92, 83, - 135, 89, 11, 78, 100, 79, 80, 71, 33, 84, - 101, 85, 100, 90, 102, 177, 130, 91, 87, 92, - 93, 94, 87, 95, 138, 139, 140, 141, 142, 143, - 144, 92, 96, 99, 105, 106, 114, 110, 116, 107, - 108, 118, 156, 77, 86, 100, 97, 66, 126, 136, - 154, 157, 158, 159, 160, 161, 92, 162, 179, 180, - 26, 124, 181, 182, 183, 184, 185, 1, 2, 54, - 100, 58, 55, 57, 56, 88, 112, 103, 81, 97, - 137, 76, 104, 97, 70, 178, 65, 122, 153, 113, - 0, 117, 0, 26, 12, 155, 0, 111, 0, 0, - 0, 0, 0, 115, 97, 0, 0, 0, 9, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, + 19, 38, 39, 17, 26, 36, 111, 67, 26, 26, + 67, 26, 24, 17, 89, 78, 40, 41, 54, 67, + 43, 138, 87, 57, 131, 78, 60, 43, 126, 132, + 63, 89, 2, 19, 75, 3, 4, 5, 73, 102, + 74, 76, 100, 42, 19, 91, 83, 89, 94, 29, + 133, 30, 31, 93, 32, 103, 72, 95, 11, 84, + 85, 79, 102, 80, 81, 86, 92, 180, 44, 45, + 46, 47, 89, 94, 96, 33, 88, 98, 97, 101, + 104, 94, 107, 108, 109, 110, 121, 78, 117, 113, + 119, 87, 139, 67, 129, 102, 99, 141, 142, 143, + 144, 145, 146, 147, 157, 159, 94, 182, 183, 184, + 26, 185, 186, 160, 127, 161, 162, 163, 164, 165, + 102, 187, 188, 1, 2, 56, 60, 57, 59, 99, + 58, 82, 140, 90, 115, 105, 77, 99, 66, 106, + 181, 125, 158, 26, 12, 156, 0, 71, 0, 0, + 0, 120, 112, 0, 99, 0, 114, 0, 9, 116, + 0, 0, 118, 0, 0, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 0, 0, 0, @@ -358,23 +358,23 @@ short sudoerstable[] = 11, 3, 4, 5, 40, 41, 0, 0, 0, 0, 33, 40, 41, 0, 11, 0, 19, 0, 19, 34, 0, 19, 19, 19, 11, 19, 19, 19, 19, 19, - 87, 42, 87, 11, 7, 87, 87, 87, 42, 87, - 87, 87, 87, 87, 19, 19, 19, 19, 19, 19, - 0, 0, 0, 44, 45, 46, 47, 0, 87, 87, - 87, 87, 87, 87, 92, 0, 92, 7, 15, 92, - 92, 92, 0, 92, 92, 92, 92, 92, 100, 0, - 100, 131, 13, 100, 100, 100, 0, 100, 100, 100, - 100, 100, 92, 92, 92, 92, 92, 92, 0, 0, - 0, 15, 0, 0, 0, 0, 100, 100, 100, 100, - 100, 100, 97, 0, 97, 13, 14, 97, 97, 97, - 0, 97, 97, 97, 97, 97, 26, 0, 26, 0, + 89, 42, 89, 11, 7, 89, 89, 89, 42, 89, + 89, 89, 89, 89, 19, 19, 19, 19, 19, 19, + 0, 0, 0, 44, 45, 46, 47, 0, 89, 89, + 89, 89, 89, 89, 94, 0, 94, 7, 15, 94, + 94, 94, 0, 94, 94, 94, 94, 94, 102, 0, + 102, 134, 13, 102, 102, 102, 0, 102, 102, 102, + 102, 102, 94, 94, 94, 94, 94, 94, 0, 0, + 0, 15, 0, 0, 0, 0, 102, 102, 102, 102, + 102, 102, 99, 0, 99, 13, 14, 99, 99, 99, + 0, 99, 99, 99, 99, 99, 26, 0, 26, 0, 16, 26, 26, 26, 0, 26, 26, 26, 26, 26, - 97, 97, 97, 97, 97, 97, 0, 0, 0, 14, + 99, 99, 99, 99, 99, 99, 0, 0, 0, 14, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 12, 0, 12, 16, 0, 12, 12, 12, 0, 12, 12, 12, 12, 12, 9, 0, 9, 0, 0, 9, 9, 9, 0, 9, 9, 9, 9, 9, 12, 12, - 12, 12, 12, 12, 0, 0, 52, 0, 0, 0, + 12, 12, 12, 12, 0, 0, 54, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 10, 0, 10, 0, 0, 10, 10, 10, 0, 10, 10, 10, 10, 10, 8, 0, 8, 0, 0, 8, 8, 8, @@ -382,13 +382,13 @@ short sudoerstable[] = 10, 10, 43, 0, 29, 0, 30, 31, 0, 32, 8, 8, 8, 8, 8, 8, 11, 0, 11, 0, 0, 11, 11, 11, 0, 11, 11, 11, 11, 11, - 33, 0, 0, 0, 0, 67, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 7, 0, 7, 0, 0, 7, 7, 7, 0, 7, - 7, 7, 7, 7, 17, 0, 128, 0, 0, 0, - 0, 129, 0, 0, 0, 0, 0, 0, 7, 7, + 7, 7, 7, 7, 17, 0, 131, 0, 0, 0, + 0, 132, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 7, 7, 15, 0, 15, 0, 0, 15, - 15, 15, 130, 15, 15, 15, 15, 15, 13, 0, + 15, 15, 133, 15, 15, 15, 15, 15, 13, 0, 13, 0, 0, 13, 13, 13, 0, 13, 13, 13, 13, 13, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, @@ -397,19 +397,19 @@ short sudoerstable[] = 0, 16, 16, 16, 0, 16, 16, 16, 16, 16, 14, 14, 14, 14, 14, 14, 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, - 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 0, 0, - 0, 0, 0, 0, 52, 52, 52, 52, 52, 52, - 52, 0, 52, 52, 52, 52, 40, 41, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, - 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, - 174, 175, 176, 42, 0, 0, 0, 0, 0, 67, - 67, 0, 0, 0, 0, 0, 0, 0, 44, 45, - 46, 47, 67, 67, 67, 67, 67, 67, 67, 67, - 67, 67, 67, 67, 67, 67, 67, 1, 0, 2, + 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, + 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, + 54, 0, 54, 54, 54, 54, 40, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 42, 0, 0, 0, 0, 0, 69, + 69, 0, 0, 0, 0, 0, 0, 0, 44, 45, + 46, 47, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 1, 0, 2, 0, 0, 3, 4, 5, 0, 6, 7, 8, 9, - 10, 67, 67, 67, 67, 0, 0, 0, 0, 0, + 10, 69, 69, 69, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 16, }; @@ -419,23 +419,23 @@ const short sudoerscheck[] = short sudoerscheck[] = #endif { 33, - 0, 8, 9, 33, 33, 7, 86, 44, 33, 33, + 0, 8, 9, 33, 33, 7, 87, 44, 33, 33, 44, 33, 285, 33, 0, 44, 257, 258, 258, 44, - 33, 258, 44, 258, 61, 258, 258, 33, 58, 261, - 262, 263, 258, 33, 260, 261, 44, 263, 259, 43, - 258, 45, 58, 284, 44, 263, 44, 33, 0, 58, - 58, 61, 284, 36, 263, 38, 39, 61, 284, 58, - 263, 58, 0, 58, 263, 145, 284, 61, 51, 58, - 61, 58, 58, 61, 291, 292, 293, 294, 295, 296, - 297, 33, 58, 58, 264, 264, 93, 89, 95, 264, - 264, 40, 61, 44, 44, 33, 0, 44, 44, 41, - 44, 61, 61, 61, 61, 61, 58, 61, 263, 263, - 0, 118, 263, 263, 263, 263, 263, 0, 0, 41, - 58, 41, 41, 41, 41, 52, 91, 75, 43, 33, - 126, 34, 77, 66, 22, 154, 17, 99, 131, 92, - -1, 96, -1, 33, 0, 135, -1, 90, -1, -1, - -1, -1, -1, 94, 58, -1, -1, -1, 0, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 58, -1, + 33, 58, 44, 258, 258, 44, 258, 33, 58, 263, + 258, 44, 258, 33, 259, 261, 262, 263, 43, 263, + 45, 44, 61, 284, 44, 61, 58, 33, 0, 258, + 284, 260, 261, 61, 263, 263, 61, 61, 284, 58, + 58, 36, 0, 38, 39, 58, 58, 148, 299, 300, + 301, 302, 58, 58, 58, 284, 51, 58, 61, 58, + 263, 33, 264, 264, 264, 264, 40, 44, 95, 91, + 97, 44, 41, 44, 44, 33, 0, 291, 292, 293, + 294, 295, 296, 297, 44, 61, 58, 263, 263, 263, + 0, 263, 263, 61, 121, 61, 61, 61, 61, 61, + 58, 263, 263, 0, 0, 41, 41, 41, 41, 33, + 41, 43, 129, 53, 93, 76, 34, 67, 17, 78, + 157, 101, 138, 33, 0, 134, -1, 22, -1, -1, + -1, 98, 89, -1, 58, -1, 92, -1, 0, 94, + -1, -1, 96, -1, -1, -1, -1, -1, 58, -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, 0, -1, -1, -1, -1, -1, -1, -1, @@ -570,12 +570,14 @@ char *sudoersrule[] = "cmndspeclist : cmndspec", "cmndspeclist : cmndspeclist ',' cmndspec", "cmndspec : runasspec options cmndtag digcmnd", -"digest : SHA224_TOK ':' DIGEST", -"digest : SHA256_TOK ':' DIGEST", -"digest : SHA384_TOK ':' DIGEST", -"digest : SHA512_TOK ':' DIGEST", +"digestspec : SHA224_TOK ':' DIGEST", +"digestspec : SHA256_TOK ':' DIGEST", +"digestspec : SHA384_TOK ':' DIGEST", +"digestspec : SHA512_TOK ':' DIGEST", +"digestlist : digestspec", +"digestlist : digestlist ',' digestspec", "digcmnd : opcmnd", -"digcmnd : digest opcmnd", +"digcmnd : digestlist opcmnd", "opcmnd : cmnd", "opcmnd : '!' cmnd", "timeoutspec : CMND_TIMEOUT '=' WORD", @@ -678,7 +680,7 @@ short *yysslim; YYSTYPE *yyvs; unsigned int yystacksize; int yyparse(void); -#line 911 "gram.y" +#line 922 "gram.y" void sudoerserror(const char *s) { @@ -763,6 +765,7 @@ new_digest(int digest_type, char *digest_str) debug_return_ptr(NULL); } + HLTQ_INIT(digest, entries); digest->digest_type = digest_type; digest->digest_str = digest_str; if (digest->digest_str == NULL) { @@ -851,13 +854,15 @@ free_member(struct member *m) debug_decl(free_member, SUDOERS_DEBUG_PARSER); if (m->type == COMMAND) { - struct sudo_command *c = (struct sudo_command *)m->name; - free(c->cmnd); - free(c->args); - if (c->digest != NULL) { - free(c->digest->digest_str); - free(c->digest); - } + struct command_digest *digest; + struct sudo_command *c = (struct sudo_command *)m->name; + free(c->cmnd); + free(c->args); + while ((digest = TAILQ_FIRST(&c->digests)) != NULL) { + TAILQ_REMOVE(&c->digests, digest, entries); + free(digest->digest_str); + free(digest); + } } free(m->name); free(m); @@ -1107,7 +1112,7 @@ init_options(struct command_options *opts) opts->limitprivs = NULL; #endif } -#line 1053 "gram.c" +#line 1058 "gram.c" /* allocate initial stack or double stack size, up to YYMAXDEPTH */ #if defined(__cplusplus) || defined(__STDC__) static int yygrowstack(void) @@ -1316,23 +1321,23 @@ yyreduce: switch (yyn) { case 1: -#line 181 "gram.y" +#line 182 "gram.y" { ; } break; case 5: -#line 189 "gram.y" +#line 190 "gram.y" { ; } break; case 6: -#line 192 "gram.y" +#line 193 "gram.y" { yyerrok; } break; case 7: -#line 195 "gram.y" +#line 196 "gram.y" { if (!add_userspec(yyvsp[-1].member, yyvsp[0].privilege)) { sudoerserror(N_("unable to allocate memory")); @@ -1341,73 +1346,73 @@ case 7: } break; case 8: -#line 201 "gram.y" +#line 202 "gram.y" { ; } break; case 9: -#line 204 "gram.y" +#line 205 "gram.y" { ; } break; case 10: -#line 207 "gram.y" +#line 208 "gram.y" { ; } break; case 11: -#line 210 "gram.y" +#line 211 "gram.y" { ; } break; case 12: -#line 213 "gram.y" +#line 214 "gram.y" { if (!add_defaults(DEFAULTS, NULL, yyvsp[0].defaults)) YYERROR; } break; case 13: -#line 217 "gram.y" +#line 218 "gram.y" { if (!add_defaults(DEFAULTS_USER, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 14: -#line 221 "gram.y" +#line 222 "gram.y" { if (!add_defaults(DEFAULTS_RUNAS, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 15: -#line 225 "gram.y" +#line 226 "gram.y" { if (!add_defaults(DEFAULTS_HOST, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 16: -#line 229 "gram.y" +#line 230 "gram.y" { if (!add_defaults(DEFAULTS_CMND, yyvsp[-1].member, yyvsp[0].defaults)) YYERROR; } break; case 18: -#line 236 "gram.y" +#line 237 "gram.y" { HLTQ_CONCAT(yyvsp[-2].defaults, yyvsp[0].defaults, entries); yyval.defaults = yyvsp[-2].defaults; } break; case 19: -#line 242 "gram.y" +#line 243 "gram.y" { yyval.defaults = new_default(yyvsp[0].string, NULL, true); if (yyval.defaults == NULL) { @@ -1417,7 +1422,7 @@ case 19: } break; case 20: -#line 249 "gram.y" +#line 250 "gram.y" { yyval.defaults = new_default(yyvsp[0].string, NULL, false); if (yyval.defaults == NULL) { @@ -1427,7 +1432,7 @@ case 20: } break; case 21: -#line 256 "gram.y" +#line 257 "gram.y" { yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, true); if (yyval.defaults == NULL) { @@ -1437,7 +1442,7 @@ case 21: } break; case 22: -#line 263 "gram.y" +#line 264 "gram.y" { yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '+'); if (yyval.defaults == NULL) { @@ -1447,7 +1452,7 @@ case 22: } break; case 23: -#line 270 "gram.y" +#line 271 "gram.y" { yyval.defaults = new_default(yyvsp[-2].string, yyvsp[0].string, '-'); if (yyval.defaults == NULL) { @@ -1457,14 +1462,14 @@ case 23: } break; case 25: -#line 280 "gram.y" +#line 281 "gram.y" { HLTQ_CONCAT(yyvsp[-2].privilege, yyvsp[0].privilege, entries); yyval.privilege = yyvsp[-2].privilege; } break; case 26: -#line 286 "gram.y" +#line 287 "gram.y" { struct privilege *p = calloc(1, sizeof(*p)); if (p == NULL) { @@ -1479,21 +1484,21 @@ case 26: } break; case 27: -#line 300 "gram.y" +#line 301 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = false; } break; case 28: -#line 304 "gram.y" +#line 305 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = true; } break; case 29: -#line 310 "gram.y" +#line 311 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -1503,7 +1508,7 @@ case 29: } break; case 30: -#line 317 "gram.y" +#line 318 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -1513,7 +1518,7 @@ case 30: } break; case 31: -#line 324 "gram.y" +#line 325 "gram.y" { yyval.member = new_member(yyvsp[0].string, NETGROUP); if (yyval.member == NULL) { @@ -1523,7 +1528,7 @@ case 31: } break; case 32: -#line 331 "gram.y" +#line 332 "gram.y" { yyval.member = new_member(yyvsp[0].string, NTWKADDR); if (yyval.member == NULL) { @@ -1533,7 +1538,7 @@ case 32: } break; case 33: -#line 338 "gram.y" +#line 339 "gram.y" { yyval.member = new_member(yyvsp[0].string, WORD); if (yyval.member == NULL) { @@ -1543,7 +1548,7 @@ case 33: } break; case 35: -#line 348 "gram.y" +#line 349 "gram.y" { struct cmndspec *prev; prev = HLTQ_LAST(yyvsp[-2].cmndspec, cmndspec, entries); @@ -1597,7 +1602,7 @@ case 35: } break; case 36: -#line 401 "gram.y" +#line 402 "gram.y" { struct cmndspec *cs = calloc(1, sizeof(*cs)); if (cs == NULL) { @@ -1651,7 +1656,7 @@ case 36: } break; case 37: -#line 454 "gram.y" +#line 455 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA224, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1661,7 +1666,7 @@ case 37: } break; case 38: -#line 461 "gram.y" +#line 462 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA256, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1671,7 +1676,7 @@ case 38: } break; case 39: -#line 468 "gram.y" +#line 469 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA384, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1681,7 +1686,7 @@ case 39: } break; case 40: -#line 475 "gram.y" +#line 476 "gram.y" { yyval.digest = new_digest(SUDO_DIGEST_SHA512, yyvsp[0].string); if (yyval.digest == NULL) { @@ -1690,58 +1695,55 @@ case 40: } } break; -case 41: -#line 484 "gram.y" +case 42: +#line 486 "gram.y" +{ + HLTQ_CONCAT(yyvsp[-2].digest, yyvsp[0].digest, entries); + yyval.digest = yyvsp[-2].digest; + } +break; +case 43: +#line 492 "gram.y" { yyval.member = yyvsp[0].member; } break; -case 42: -#line 487 "gram.y" +case 44: +#line 495 "gram.y" { + struct sudo_command *c = + (struct sudo_command *) yyvsp[0].member->name; + if (yyvsp[0].member->type != COMMAND) { sudoerserror(N_("a digest requires a path name")); YYERROR; } - /* XXX - yuck */ - ((struct sudo_command *) yyvsp[0].member->name)->digest = yyvsp[-1].digest; + HLTQ_TO_TAILQ(&c->digests, yyvsp[-1].digest, entries); yyval.member = yyvsp[0].member; } break; -case 43: -#line 498 "gram.y" -{ - yyval.member = yyvsp[0].member; - yyval.member->negated = false; - } -break; -case 44: -#line 502 "gram.y" -{ - yyval.member = yyvsp[0].member; - yyval.member->negated = true; - } -break; case 45: #line 508 "gram.y" { - yyval.string = yyvsp[0].string; + yyval.member = yyvsp[0].member; + yyval.member->negated = false; } break; case 46: -#line 513 "gram.y" +#line 512 "gram.y" { - yyval.string = yyvsp[0].string; + yyval.member = yyvsp[0].member; + yyval.member->negated = true; } break; case 47: -#line 517 "gram.y" +#line 518 "gram.y" { yyval.string = yyvsp[0].string; } break; case 48: -#line 522 "gram.y" +#line 523 "gram.y" { yyval.string = yyvsp[0].string; } @@ -1759,25 +1761,37 @@ case 50: } break; case 51: -#line 536 "gram.y" +#line 537 "gram.y" { yyval.string = yyvsp[0].string; } break; case 52: -#line 541 "gram.y" +#line 542 "gram.y" +{ + yyval.string = yyvsp[0].string; + } +break; +case 53: +#line 546 "gram.y" +{ + yyval.string = yyvsp[0].string; + } +break; +case 54: +#line 551 "gram.y" { yyval.runas = NULL; } break; -case 53: -#line 544 "gram.y" +case 55: +#line 554 "gram.y" { yyval.runas = yyvsp[-1].runas; } break; -case 54: -#line 549 "gram.y" +case 56: +#line 559 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas != NULL) { @@ -1794,8 +1808,8 @@ case 54: } } break; -case 55: -#line 564 "gram.y" +case 57: +#line 574 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas == NULL) { @@ -1806,8 +1820,8 @@ case 55: /* $$->runasgroups = NULL; */ } break; -case 56: -#line 573 "gram.y" +case 58: +#line 583 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas == NULL) { @@ -1818,8 +1832,8 @@ case 56: yyval.runas->runasgroups = yyvsp[0].member; } break; -case 57: -#line 582 "gram.y" +case 59: +#line 592 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas == NULL) { @@ -1830,8 +1844,8 @@ case 57: yyval.runas->runasgroups = yyvsp[0].member; } break; -case 58: -#line 591 "gram.y" +case 60: +#line 601 "gram.y" { yyval.runas = calloc(1, sizeof(struct runascontainer)); if (yyval.runas != NULL) { @@ -1848,14 +1862,14 @@ case 58: } } break; -case 59: -#line 608 "gram.y" +case 61: +#line 618 "gram.y" { init_options(&yyval.options); } break; -case 60: -#line 611 "gram.y" +case 62: +#line 621 "gram.y" { yyval.options.notbefore = parse_gentime(yyvsp[0].string); free(yyvsp[0].string); @@ -1865,8 +1879,8 @@ case 60: } } break; -case 61: -#line 619 "gram.y" +case 63: +#line 629 "gram.y" { yyval.options.notafter = parse_gentime(yyvsp[0].string); free(yyvsp[0].string); @@ -1876,8 +1890,8 @@ case 61: } } break; -case 62: -#line 627 "gram.y" +case 64: +#line 637 "gram.y" { yyval.options.timeout = parse_timeout(yyvsp[0].string); free(yyvsp[0].string); @@ -1890,8 +1904,8 @@ case 62: } } break; -case 63: -#line 638 "gram.y" +case 65: +#line 648 "gram.y" { #ifdef HAVE_SELINUX free(yyval.options.role); @@ -1899,8 +1913,8 @@ case 63: #endif } break; -case 64: -#line 644 "gram.y" +case 66: +#line 654 "gram.y" { #ifdef HAVE_SELINUX free(yyval.options.type); @@ -1908,8 +1922,8 @@ case 64: #endif } break; -case 65: -#line 650 "gram.y" +case 67: +#line 660 "gram.y" { #ifdef HAVE_PRIV_SET free(yyval.options.privs); @@ -1917,8 +1931,8 @@ case 65: #endif } break; -case 66: -#line 656 "gram.y" +case 68: +#line 666 "gram.y" { #ifdef HAVE_PRIV_SET free(yyval.options.limitprivs); @@ -1926,98 +1940,98 @@ case 66: #endif } break; -case 67: -#line 664 "gram.y" +case 69: +#line 674 "gram.y" { TAGS_INIT(yyval.tag); } break; -case 68: -#line 667 "gram.y" +case 70: +#line 677 "gram.y" { yyval.tag.nopasswd = true; } break; -case 69: -#line 670 "gram.y" +case 71: +#line 680 "gram.y" { yyval.tag.nopasswd = false; } break; -case 70: -#line 673 "gram.y" +case 72: +#line 683 "gram.y" { yyval.tag.noexec = true; } break; -case 71: -#line 676 "gram.y" +case 73: +#line 686 "gram.y" { yyval.tag.noexec = false; } break; -case 72: -#line 679 "gram.y" +case 74: +#line 689 "gram.y" { yyval.tag.setenv = true; } break; -case 73: -#line 682 "gram.y" +case 75: +#line 692 "gram.y" { yyval.tag.setenv = false; } break; -case 74: -#line 685 "gram.y" +case 76: +#line 695 "gram.y" { yyval.tag.log_input = true; } break; -case 75: -#line 688 "gram.y" +case 77: +#line 698 "gram.y" { yyval.tag.log_input = false; } break; -case 76: -#line 691 "gram.y" +case 78: +#line 701 "gram.y" { yyval.tag.log_output = true; } break; -case 77: -#line 694 "gram.y" +case 79: +#line 704 "gram.y" { yyval.tag.log_output = false; } break; -case 78: -#line 697 "gram.y" +case 80: +#line 707 "gram.y" { yyval.tag.follow = true; } break; -case 79: -#line 700 "gram.y" +case 81: +#line 710 "gram.y" { yyval.tag.follow = false; } break; -case 80: -#line 703 "gram.y" +case 82: +#line 713 "gram.y" { yyval.tag.send_mail = true; } break; -case 81: -#line 706 "gram.y" +case 83: +#line 716 "gram.y" { yyval.tag.send_mail = false; } break; -case 82: -#line 711 "gram.y" +case 84: +#line 721 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -2026,8 +2040,8 @@ case 82: } } break; -case 83: -#line 718 "gram.y" +case 85: +#line 728 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -2036,8 +2050,8 @@ case 83: } } break; -case 84: -#line 725 "gram.y" +case 86: +#line 735 "gram.y" { struct sudo_command *c = calloc(1, sizeof(*c)); if (c == NULL) { @@ -2046,6 +2060,7 @@ case 84: } c->cmnd = yyvsp[0].command.cmnd; c->args = yyvsp[0].command.args; + TAILQ_INIT(&c->digests); yyval.member = new_member((char *)c, COMMAND); if (yyval.member == NULL) { free(c); @@ -2054,8 +2069,8 @@ case 84: } } break; -case 87: -#line 746 "gram.y" +case 89: +#line 757 "gram.y" { const char *s; s = alias_add(&parsed_policy, yyvsp[-2].string, HOSTALIAS, @@ -2066,15 +2081,15 @@ case 87: } } break; -case 89: -#line 758 "gram.y" +case 91: +#line 769 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; -case 92: -#line 768 "gram.y" +case 94: +#line 779 "gram.y" { const char *s; s = alias_add(&parsed_policy, yyvsp[-2].string, CMNDALIAS, @@ -2085,15 +2100,15 @@ case 92: } } break; -case 94: -#line 780 "gram.y" +case 96: +#line 791 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; -case 97: -#line 790 "gram.y" +case 99: +#line 801 "gram.y" { const char *s; s = alias_add(&parsed_policy, yyvsp[-2].string, RUNASALIAS, @@ -2104,8 +2119,8 @@ case 97: } } break; -case 100: -#line 805 "gram.y" +case 102: +#line 816 "gram.y" { const char *s; s = alias_add(&parsed_policy, yyvsp[-2].string, USERALIAS, @@ -2116,29 +2131,29 @@ case 100: } } break; -case 102: -#line 817 "gram.y" +case 104: +#line 828 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; -case 103: -#line 823 "gram.y" +case 105: +#line 834 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = false; } break; -case 104: -#line 827 "gram.y" +case 106: +#line 838 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = true; } break; -case 105: -#line 833 "gram.y" +case 107: +#line 844 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -2147,8 +2162,8 @@ case 105: } } break; -case 106: -#line 840 "gram.y" +case 108: +#line 851 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -2157,8 +2172,8 @@ case 106: } } break; -case 107: -#line 847 "gram.y" +case 109: +#line 858 "gram.y" { yyval.member = new_member(yyvsp[0].string, NETGROUP); if (yyval.member == NULL) { @@ -2167,8 +2182,8 @@ case 107: } } break; -case 108: -#line 854 "gram.y" +case 110: +#line 865 "gram.y" { yyval.member = new_member(yyvsp[0].string, USERGROUP); if (yyval.member == NULL) { @@ -2177,8 +2192,8 @@ case 108: } } break; -case 109: -#line 861 "gram.y" +case 111: +#line 872 "gram.y" { yyval.member = new_member(yyvsp[0].string, WORD); if (yyval.member == NULL) { @@ -2187,29 +2202,29 @@ case 109: } } break; -case 111: -#line 871 "gram.y" +case 113: +#line 882 "gram.y" { HLTQ_CONCAT(yyvsp[-2].member, yyvsp[0].member, entries); yyval.member = yyvsp[-2].member; } break; -case 112: -#line 877 "gram.y" +case 114: +#line 888 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = false; } break; -case 113: -#line 881 "gram.y" +case 115: +#line 892 "gram.y" { yyval.member = yyvsp[0].member; yyval.member->negated = true; } break; -case 114: -#line 887 "gram.y" +case 116: +#line 898 "gram.y" { yyval.member = new_member(yyvsp[0].string, ALIAS); if (yyval.member == NULL) { @@ -2218,8 +2233,8 @@ case 114: } } break; -case 115: -#line 894 "gram.y" +case 117: +#line 905 "gram.y" { yyval.member = new_member(NULL, ALL); if (yyval.member == NULL) { @@ -2228,8 +2243,8 @@ case 115: } } break; -case 116: -#line 901 "gram.y" +case 118: +#line 912 "gram.y" { yyval.member = new_member(yyvsp[0].string, WORD); if (yyval.member == NULL) { @@ -2238,7 +2253,7 @@ case 116: } } break; -#line 2184 "gram.c" +#line 2199 "gram.c" } yyssp -= yym; yystate = *yyssp; diff --git a/plugins/sudoers/gram.y b/plugins/sudoers/gram.y index 14c7f42ee..d9967b3e2 100644 --- a/plugins/sudoers/gram.y +++ b/plugins/sudoers/gram.y @@ -2,7 +2,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2018 + * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2020 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -174,7 +174,8 @@ static struct command_digest *new_digest(int, char *); %type timeoutspec %type notbeforespec %type notafterspec -%type digest +%type digestspec +%type digestlist %% @@ -451,7 +452,7 @@ cmndspec : runasspec options cmndtag digcmnd { } ; -digest : SHA224_TOK ':' DIGEST { +digestspec : SHA224_TOK ':' DIGEST { $$ = new_digest(SUDO_DIGEST_SHA224, $3); if ($$ == NULL) { sudoerserror(N_("unable to allocate memory")); @@ -481,16 +482,25 @@ digest : SHA224_TOK ':' DIGEST { } ; +digestlist : digestspec + | digestlist ',' digestspec { + HLTQ_CONCAT($1, $3, entries); + $$ = $1; + } + ; + digcmnd : opcmnd { $$ = $1; } - | digest opcmnd { + | digestlist opcmnd { + struct sudo_command *c = + (struct sudo_command *) $2->name; + if ($2->type != COMMAND) { sudoerserror(N_("a digest requires a path name")); YYERROR; } - /* XXX - yuck */ - ((struct sudo_command *) $2->name)->digest = $1; + HLTQ_TO_TAILQ(&c->digests, $1, entries); $$ = $2; } ; @@ -730,6 +740,7 @@ cmnd : ALL { } c->cmnd = $1.cmnd; c->args = $1.args; + TAILQ_INIT(&c->digests); $$ = new_member((char *)c, COMMAND); if ($$ == NULL) { free(c); @@ -992,6 +1003,7 @@ new_digest(int digest_type, char *digest_str) debug_return_ptr(NULL); } + HLTQ_INIT(digest, entries); digest->digest_type = digest_type; digest->digest_str = digest_str; if (digest->digest_str == NULL) { @@ -1080,13 +1092,15 @@ free_member(struct member *m) debug_decl(free_member, SUDOERS_DEBUG_PARSER); if (m->type == COMMAND) { - struct sudo_command *c = (struct sudo_command *)m->name; - free(c->cmnd); - free(c->args); - if (c->digest != NULL) { - free(c->digest->digest_str); - free(c->digest); - } + struct command_digest *digest; + struct sudo_command *c = (struct sudo_command *)m->name; + free(c->cmnd); + free(c->args); + while ((digest = TAILQ_FIRST(&c->digests)) != NULL) { + TAILQ_REMOVE(&c->digests, digest, entries); + free(digest->digest_str); + free(digest); + } } free(m->name); free(m); diff --git a/plugins/sudoers/ldap_util.c b/plugins/sudoers/ldap_util.c index 48ea3ab30..a374d81d6 100644 --- a/plugins/sudoers/ldap_util.c +++ b/plugins/sudoers/ldap_util.c @@ -288,6 +288,110 @@ oom: debug_return_bool(false); } +/* + * If a digest prefix is present, add it to struct command_digest_list + * and update cmnd to point to the command after the digest. + * Returns 1 if a digest was parsed, 0 if not and -1 on error. + */ +static int +sudo_ldap_extract_digest(char **cmnd, struct command_digest_list *digests) +{ + char *ep, *cp = *cmnd; + struct command_digest *digest; + int digest_type = SUDO_DIGEST_INVALID; + debug_decl(sudo_ldap_extract_digest, SUDOERS_DEBUG_LDAP); + + /* + * Check for and extract a digest prefix, e.g. + * sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls + */ + if (cp[0] == 's' && cp[1] == 'h' && cp[2] == 'a') { + switch (cp[3]) { + case '2': + if (cp[4] == '2' && cp[5] == '4') + digest_type = SUDO_DIGEST_SHA224; + else if (cp[4] == '5' && cp[5] == '6') + digest_type = SUDO_DIGEST_SHA256; + break; + case '3': + if (cp[4] == '8' && cp[5] == '4') + digest_type = SUDO_DIGEST_SHA384; + break; + case '5': + if (cp[4] == '1' && cp[5] == '2') + digest_type = SUDO_DIGEST_SHA512; + break; + } + if (digest_type != SUDO_DIGEST_INVALID) { + cp += 6; + while (isblank((unsigned char)*cp)) + cp++; + if (*cp == ':') { + cp++; + while (isblank((unsigned char)*cp)) + cp++; + ep = cp; + while (*ep != '\0' && !isblank((unsigned char)*ep) && *ep != ',') + ep++; + if (isblank((unsigned char)*ep) || *ep == ',') { + if ((digest = malloc(sizeof(*digest))) == NULL) { + sudo_warnx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + debug_return_int(-1); + } + digest->digest_type = digest_type; + digest->digest_str = strndup(cp, (size_t)(ep - cp)); + if (digest->digest_str == NULL) { + sudo_warnx(U_("%s: %s"), __func__, + U_("unable to allocate memory")); + free(digest); + debug_return_int(-1); + } + while (isblank((unsigned char)*ep)) + ep++; + *cmnd = ep; + sudo_debug_printf(SUDO_DEBUG_INFO, + "%s digest %s for %s", + digest_type_to_name(digest_type), + digest->digest_str, cp); + TAILQ_INSERT_TAIL(digests, digest, entries); + debug_return_int(1); + } + } + } + } + debug_return_int(0); +} + +/* + * If a digest list is present, fill in struct command_digest_list + * and update cmnd to point to the command after the digest. + * Returns false on error, else true. + */ +static bool +sudo_ldap_extract_digests(char **cmnd, struct command_digest_list *digests) +{ + char *cp = *cmnd; + int rc; + debug_decl(sudo_ldap_extract_digests, SUDOERS_DEBUG_LDAP); + + for (;;) { + rc = sudo_ldap_extract_digest(&cp, digests); + if (rc != 1) + break; + + /* Check for additional digestspecs, separated by a comma. */ + if (*cp != ',') + break; + do { + cp++; + } while (isblank((unsigned char)*cp)); + } + *cmnd = cp; + + debug_return_bool(rc != -1); +} + /* * Convert an LDAP sudoRole to a sudoers privilege. * Pass in struct berval ** for LDAP or char *** for SSSD. @@ -358,6 +462,7 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, goto oom; } m->name = (char *)c; + TAILQ_INIT(&c->digests); } /* Negated commands have precedence so insert them at the end. */ @@ -481,17 +586,13 @@ sudo_ldap_role_to_priv(const char *cn, void *hosts, void *runasusers, if (cmndspec->tags.setenv == UNSPEC) cmndspec->tags.setenv = IMPLIED; } else { - struct command_digest digest; char *args; m->type = COMMAND; - /* Fill in command with optional digest. */ - if (sudo_ldap_extract_digest(&cmnd, &digest) != NULL) { - if ((c->digest = malloc(sizeof(*c->digest))) == NULL) - goto oom; - *c->digest = digest; - } + /* Fill in command with optional digests. */ + if (!sudo_ldap_extract_digests(&cmnd, &c->digests)) + goto oom; if ((args = strpbrk(cmnd, " \t")) != NULL) { *args++ = '\0'; if ((c->args = strdup(args)) == NULL) @@ -515,70 +616,3 @@ oom: } debug_return_ptr(NULL); } - -/* - * If a digest prefix is present, fills in struct command_digest - * and returns a pointer to it, updating cmnd to point to the - * command after the digest. - */ -struct command_digest * -sudo_ldap_extract_digest(char **cmnd, struct command_digest *digest) -{ - char *ep, *cp = *cmnd; - int digest_type = SUDO_DIGEST_INVALID; - debug_decl(sudo_ldap_check_command, SUDOERS_DEBUG_LDAP); - - /* - * Check for and extract a digest prefix, e.g. - * sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls - */ - if (cp[0] == 's' && cp[1] == 'h' && cp[2] == 'a') { - switch (cp[3]) { - case '2': - if (cp[4] == '2' && cp[5] == '4') - digest_type = SUDO_DIGEST_SHA224; - else if (cp[4] == '5' && cp[5] == '6') - digest_type = SUDO_DIGEST_SHA256; - break; - case '3': - if (cp[4] == '8' && cp[5] == '4') - digest_type = SUDO_DIGEST_SHA384; - break; - case '5': - if (cp[4] == '1' && cp[5] == '2') - digest_type = SUDO_DIGEST_SHA512; - break; - } - if (digest_type != SUDO_DIGEST_INVALID) { - cp += 6; - while (isblank((unsigned char)*cp)) - cp++; - if (*cp == ':') { - cp++; - while (isblank((unsigned char)*cp)) - cp++; - ep = cp; - while (*ep != '\0' && !isblank((unsigned char)*ep)) - ep++; - if (*ep != '\0') { - digest->digest_type = digest_type; - digest->digest_str = strndup(cp, (size_t)(ep - cp)); - if (digest->digest_str == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - debug_return_ptr(NULL); - } - cp = ep + 1; - while (isblank((unsigned char)*cp)) - cp++; - *cmnd = cp; - sudo_debug_printf(SUDO_DEBUG_INFO, - "%s digest %s for %s", - digest_type_to_name(digest_type), - digest->digest_str, cp); - debug_return_ptr(digest); - } - } - } - } - debug_return_ptr(NULL); -} diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c index b6a6be761..7b512c2b8 100644 --- a/plugins/sudoers/match.c +++ b/plugins/sudoers/match.c @@ -411,7 +411,7 @@ cmnd_matches(struct sudoers_parse_tree *parse_tree, const struct member *m) break; case COMMAND: c = (struct sudo_command *)m->name; - if (command_matches(c->cmnd, c->args, c->digest)) + if (command_matches(c->cmnd, c->args, &c->digests)) matched = !m->negated; break; } diff --git a/plugins/sudoers/match_command.c b/plugins/sudoers/match_command.c index 6982c651c..9e330a70d 100644 --- a/plugins/sudoers/match_command.c +++ b/plugins/sudoers/match_command.c @@ -126,18 +126,18 @@ is_script(int fd) * Returns false on error, else true. */ static bool -open_cmnd(const char *path, const struct command_digest *digest, int *fdp) +open_cmnd(const char *path, const struct command_digest_list *digests, int *fdp) { int fd = -1; debug_decl(open_cmnd, SUDOERS_DEBUG_MATCH); /* Only open the file for fdexec or for digest matching. */ - if (def_fdexec != always && digest == NULL) + if (def_fdexec != always && TAILQ_EMPTY(digests)) debug_return_bool(true); fd = open(path, O_RDONLY|O_NONBLOCK); # ifdef O_EXEC - if (fd == -1 && errno == EACCES && digest == NULL) { + if (fd == -1 && errno == EACCES && TAILQ_EMPTY(digests)) /* Try again with O_EXEC if no digest is specified. */ const int saved_errno = errno; if ((fd = open(path, O_EXEC)) == -1) @@ -197,7 +197,7 @@ set_cmnd_fd(int fd) */ static bool command_matches_dir(const char *sudoers_dir, size_t dlen, - const struct command_digest *digest) + const struct command_digest_list *digests) { struct stat sudoers_stat; struct dirent *dent; @@ -233,7 +233,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, continue; /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(buf, digest, &fd)) + if (!open_cmnd(buf, digests, &fd)) continue; if (!do_stat(fd, buf, &sudoers_stat)) continue; @@ -241,7 +241,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, if (user_stat == NULL || (user_stat->st_dev == sudoers_stat.st_dev && user_stat->st_ino == sudoers_stat.st_ino)) { - if (digest != NULL && !digest_matches(fd, buf, digest)) + if (!digest_matches(fd, buf, digests)) continue; free(safe_cmnd); if ((safe_cmnd = strdup(buf)) == NULL) { @@ -265,7 +265,7 @@ command_matches_dir(const char *sudoers_dir, size_t dlen, static bool command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, - const struct command_digest *digest) + const struct command_digest_list *digests) { struct stat sb; /* XXX - unused */ int fd = -1; @@ -282,12 +282,12 @@ command_matches_fnmatch(const char *sudoers_cmnd, const char *sudoers_args, debug_return_bool(false); if (command_args_match(sudoers_cmnd, sudoers_args)) { /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(user_cmnd, digest, &fd)) + if (!open_cmnd(user_cmnd, digests, &fd)) goto bad; if (!do_stat(fd, user_cmnd, &sb)) goto bad; /* Check digest of user_cmnd since sudoers_cmnd is a pattern. */ - if (digest != NULL && !digest_matches(fd, user_cmnd, digest)) + if (!digest_matches(fd, user_cmnd, digests)) goto bad; set_cmnd_fd(fd); @@ -305,7 +305,7 @@ bad: static bool command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, - const struct command_digest *digest) + const struct command_digest_list *digests) { struct stat sudoers_stat; bool bad_digest = false; @@ -349,7 +349,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, if (strcmp(cp, user_cmnd) != 0) continue; /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(cp, digest, &fd)) + if (!open_cmnd(cp, digests, &fd)) continue; if (!do_stat(fd, cp, &sudoers_stat)) continue; @@ -357,7 +357,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, (user_stat->st_dev == sudoers_stat.st_dev && user_stat->st_ino == sudoers_stat.st_ino)) { /* There could be multiple matches, check digest early. */ - if (digest != NULL && !digest_matches(fd, cp, digest)) { + if (!digest_matches(fd, cp, digests)) { bad_digest = true; continue; } @@ -385,7 +385,7 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, /* If it ends in '/' it is a directory spec. */ dlen = strlen(cp); if (cp[dlen - 1] == '/') { - if (command_matches_dir(cp, dlen, digest)) + if (command_matches_dir(cp, dlen, digests)) debug_return_bool(true); continue; } @@ -399,14 +399,14 @@ command_matches_glob(const char *sudoers_cmnd, const char *sudoers_args, continue; /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(cp, digest, &fd)) + if (!open_cmnd(cp, digests, &fd)) continue; if (!do_stat(fd, cp, &sudoers_stat)) continue; if (user_stat == NULL || (user_stat->st_dev == sudoers_stat.st_dev && user_stat->st_ino == sudoers_stat.st_ino)) { - if (digest != NULL && !digest_matches(fd, cp, digest)) + if (!digest_matches(fd, cp, digests)) continue; free(safe_cmnd); if ((safe_cmnd = strdup(cp)) == NULL) { @@ -433,7 +433,7 @@ done: } static bool -command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest *digest) +command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests) { struct stat sudoers_stat; const char *base; @@ -444,7 +444,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const /* If it ends in '/' it is a directory spec. */ dlen = strlen(sudoers_cmnd); if (sudoers_cmnd[dlen - 1] == '/') - debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digest)); + debug_return_bool(command_matches_dir(sudoers_cmnd, dlen, digests)); /* Only proceed if user_base and basename(sudoers_cmnd) match */ if ((base = strrchr(sudoers_cmnd, '/')) == NULL) @@ -455,7 +455,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const debug_return_bool(false); /* Open the file for fdexec or for digest matching. */ - if (!open_cmnd(sudoers_cmnd, digest, &fd)) + if (!open_cmnd(sudoers_cmnd, digests, &fd)) goto bad; /* @@ -476,7 +476,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const } if (!command_args_match(sudoers_cmnd, sudoers_args)) goto bad; - if (digest != NULL && !digest_matches(fd, sudoers_cmnd, digest)) { + if (!digest_matches(fd, sudoers_cmnd, digests)) { /* XXX - log functions not available but we should log very loudly */ goto bad; } @@ -498,7 +498,7 @@ bad: * otherwise, return true if user_cmnd names one of the inodes in path. */ bool -command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest *digest) +command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests) { bool rc = false; debug_decl(command_matches, SUDOERS_DEBUG_MATCH); @@ -526,11 +526,11 @@ command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct * use glob(3) and/or fnmatch(3) to do the matching. */ if (def_fast_glob) - rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digest); + rc = command_matches_fnmatch(sudoers_cmnd, sudoers_args, digests); else - rc = command_matches_glob(sudoers_cmnd, sudoers_args, digest); + rc = command_matches_glob(sudoers_cmnd, sudoers_args, digests); } else { - rc = command_matches_normal(sudoers_cmnd, sudoers_args, digest); + rc = command_matches_normal(sudoers_cmnd, sudoers_args, digests); } done: sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO, diff --git a/plugins/sudoers/match_digest.c b/plugins/sudoers/match_digest.c index 0b220d7ff..49e4c2b48 100644 --- a/plugins/sudoers/match_digest.c +++ b/plugins/sudoers/match_digest.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2005, 2007-2019 + * Copyright (c) 1996, 1998-2005, 2007-2020 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -41,61 +41,81 @@ #include #include "sudoers.h" +#include "sudo_digest.h" #include bool -digest_matches(int fd, const char *file, const struct command_digest *digest) +digest_matches(int fd, const char *file, const struct command_digest_list *digests) { + unsigned int digest_type = SUDO_DIGEST_INVALID; unsigned char *file_digest = NULL; unsigned char *sudoers_digest = NULL; + struct command_digest *digest; bool matched = false; size_t digest_len; debug_decl(digest_matches, SUDOERS_DEBUG_MATCH); - if (fd == -1) - goto done; - - file_digest = sudo_filedigest(fd, file, digest->digest_type, &digest_len); - if (lseek(fd, (off_t)0, SEEK_SET) == -1) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, - "unable to rewind digest fd"); + if (TAILQ_EMPTY(digests)) { + /* No digest, no problem. */ + debug_return_bool(true); } - if (file_digest == NULL) { - /* Warning (if any) printed by sudo_filedigest() */ + + if (fd == -1) { + /* No file, no match. */ goto done; } - /* Convert the command digest from ascii to binary. */ - if ((sudoers_digest = malloc(digest_len)) == NULL) { - sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); - goto done; - } - if (strlen(digest->digest_str) == digest_len * 2) { - /* Convert ascii hex to binary. */ - unsigned int i; - for (i = 0; i < digest_len; i++) { - const int h = hexchar(&digest->digest_str[i + i]); - if (h == -1) + TAILQ_FOREACH(digest, digests, entries) { + /* Compute file digest if needed. */ + if (digest->digest_type != digest_type) { + free(file_digest); + file_digest = sudo_filedigest(fd, file, digest->digest_type, + &digest_len); + if (lseek(fd, (off_t)0, SEEK_SET) == -1) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, + "unable to rewind digest fd"); + } + if (file_digest == NULL) { + /* Warning (if any) printed by sudo_filedigest() */ + goto done; + } + digest_type = digest->digest_type; + } + + /* Convert the command digest from ascii to binary. */ + if ((sudoers_digest = malloc(digest_len)) == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + goto done; + } + if (strlen(digest->digest_str) == digest_len * 2) { + /* Convert ascii hex to binary. */ + unsigned int i; + for (i = 0; i < digest_len; i++) { + const int h = hexchar(&digest->digest_str[i + i]); + if (h == -1) + goto bad_format; + sudoers_digest[i] = (unsigned char)h; + } + } else { + /* Convert base64 to binary. */ + size_t len = base64_decode(digest->digest_str, sudoers_digest, digest_len); + if (len != digest_len) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "incorrect length for digest, expected %zu, got %zu", + digest_len, len); goto bad_format; - sudoers_digest[i] = (unsigned char)h; + } } - } else { - /* Convert base64 to binary. */ - size_t len = base64_decode(digest->digest_str, sudoers_digest, digest_len); - if (len != digest_len) { - sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "incorrect length for digest, expected %zu, got %zu", - digest_len, len); - goto bad_format; + if (memcmp(file_digest, sudoers_digest, digest_len) == 0) { + matched = true; + break; } - } - if (memcmp(file_digest, sudoers_digest, digest_len) == 0) { - matched = true; - } else { sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO, "%s digest mismatch for %s, expecting %s", digest_type_to_name(digest->digest_type), file, digest->digest_str); + free(sudoers_digest); + sudoers_digest = NULL; } goto done; diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index 919297548..706468bf7 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 1996, 1998-2000, 2004, 2007-2018 + * Copyright (c) 1996, 1998-2000, 2004, 2007-2020 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -105,20 +105,11 @@ (cs1)->runasgrouplist != (cs2)->runasgrouplist) struct command_digest { + TAILQ_ENTRY(command_digest) entries; unsigned int digest_type; char *digest_str; }; -/* - * A command with option args and digest. - * XXX - merge into struct member - */ -struct sudo_command { - char *cmnd; - char *args; - struct command_digest *digest; -}; - /* * Tags associated with a command. * Possible values: true, false, IMPLIED, UNSPEC. @@ -164,13 +155,14 @@ struct command_options { */ /* - * Tail queue list head structure. + * Tail queue list head structures. */ TAILQ_HEAD(defaults_list, defaults); TAILQ_HEAD(userspec_list, userspec); TAILQ_HEAD(member_list, member); TAILQ_HEAD(privilege_list, privilege); TAILQ_HEAD(cmndspec_list, cmndspec); +TAILQ_HEAD(command_digest_list, command_digest); STAILQ_HEAD(comment_list, sudoers_comment); /* @@ -196,6 +188,16 @@ struct privilege { struct defaults_list defaults; /* list of sudoOptions */ }; +/* + * A command with option args and digest. + * XXX - merge into struct member + */ +struct sudo_command { + char *cmnd; + char *args; + struct command_digest_list digests; +}; + /* * Structure describing a linked list of Cmnd_Specs. * XXX - include struct command_options instad of its contents inline @@ -306,10 +308,10 @@ void reparent_parse_tree(struct sudoers_parse_tree *new_tree); bool addr_matches(char *n); /* match_command.c */ -bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest *digest); +bool command_matches(const char *sudoers_cmnd, const char *sudoers_args, const struct command_digest_list *digests); /* match_digest.c */ -bool digest_matches(int fd, const char *file, const struct command_digest *digest); +bool digest_matches(int fd, const char *file, const struct command_digest_list *digests); /* match.c */ struct group; diff --git a/plugins/sudoers/regress/sudoers/test14.in b/plugins/sudoers/regress/sudoers/test14.in index 05fafdac0..6a819e3a1 100644 --- a/plugins/sudoers/regress/sudoers/test14.in +++ b/plugins/sudoers/regress/sudoers/test14.in @@ -1,4 +1,4 @@ -Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls -Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh +Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls +Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh millert ALL = LS, SH, sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill diff --git a/plugins/sudoers/regress/sudoers/test14.json.ok b/plugins/sudoers/regress/sudoers/test14.json.ok index 46f8b21ff..6d1533a19 100644 --- a/plugins/sudoers/regress/sudoers/test14.json.ok +++ b/plugins/sudoers/regress/sudoers/test14.json.ok @@ -3,13 +3,15 @@ "LS": [ { "command": "/bin/ls", - "sha224": "d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1" + "sha224": "d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1", + "sha224": "d7910e1967342b4605cb73a550944044c631cd3514001900966962ac" } ], "SH": [ { "command": "/bin/sh", - "sha256": "hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=" + "sha256": "hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=", + "sha256": "1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4=" } ] }, diff --git a/plugins/sudoers/regress/sudoers/test14.ldif.ok b/plugins/sudoers/regress/sudoers/test14.ldif.ok index abb48867a..953ee9c9f 100644 --- a/plugins/sudoers/regress/sudoers/test14.ldif.ok +++ b/plugins/sudoers/regress/sudoers/test14.ldif.ok @@ -4,8 +4,8 @@ objectClass: sudoRole cn: millert sudoUser: millert sudoHost: ALL -sudoCommand: sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls -sudoCommand: sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh +sudoCommand: sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls +sudoCommand: sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh sudoCommand: sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill sudoOrder: 1 diff --git a/plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok b/plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok index 6bc0156f5..fa6c6435c 100644 --- a/plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok +++ b/plugins/sudoers/regress/sudoers/test14.ldif2sudo.ok @@ -1,5 +1,7 @@ # sudoRole millert -millert ALL = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1\ - /bin/ls, sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh,\ +millert ALL = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1,\ + sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls,\ + sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=,\ + sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh,\ sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw\ /bin/kill diff --git a/plugins/sudoers/regress/sudoers/test14.out.ok b/plugins/sudoers/regress/sudoers/test14.out.ok index bfcb661aa..b910ee680 100644 --- a/plugins/sudoers/regress/sudoers/test14.out.ok +++ b/plugins/sudoers/regress/sudoers/test14.out.ok @@ -1,6 +1,6 @@ Parses OK. -Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1 /bin/ls -Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM= /bin/sh +Cmnd_Alias LS = sha224:d06a2617c98d377c250edd470fd5e576327748d82915d6e33b5f8db1, sha224:d7910e1967342b4605cb73a550944044c631cd3514001900966962ac /bin/ls +Cmnd_Alias SH = sha256:hOtoe/iK6SlGg7w4BfZBBdSsXjUmTJ5+ts51yjh7vkM=, sha256:1IXHRCxXgSnIEnb+xBz4PAfWaPdXIBWKFF0QCwxJ5G4= /bin/sh millert ALL = LS, SH, sha512:srzYEQ2aqzm+it3f74opTMkIImZRLxBARVpb0g9RSouJYdLt7DTRMEY4Ry9NyaOiDoUIplpNjqYH0JMYPVdFnw /bin/kill diff --git a/plugins/sudoers/regress/sudoers/test14.toke.ok b/plugins/sudoers/regress/sudoers/test14.toke.ok index 7cb5aeafb..7ea94dc7d 100644 --- a/plugins/sudoers/regress/sudoers/test14.toke.ok +++ b/plugins/sudoers/regress/sudoers/test14.toke.ok @@ -1,4 +1,4 @@ -CMNDALIAS ALIAS = SHA224_TOK : DIGEST COMMAND -CMNDALIAS ALIAS = SHA256_TOK : DIGEST COMMAND +CMNDALIAS ALIAS = SHA224_TOK : DIGEST , SHA224_TOK : DIGEST COMMAND +CMNDALIAS ALIAS = SHA256_TOK : DIGEST , SHA256_TOK : DIGEST COMMAND WORD(5) ALL = ALIAS , ALIAS , SHA512_TOK : DIGEST COMMAND diff --git a/plugins/sudoers/sudo_ldap.h b/plugins/sudoers/sudo_ldap.h index a2162bc3a..f0a3d0b83 100644 --- a/plugins/sudoers/sudo_ldap.h +++ b/plugins/sudoers/sudo_ldap.h @@ -27,6 +27,5 @@ bool sudo_ldap_is_negated(char **valp); bool sudo_ldap_add_default(const char *var, const char *val, int op, char *source, struct defaults_list *defs); int sudo_ldap_parse_option(char *optstr, char **varp, char **valp); 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 command_digest *sudo_ldap_extract_digest(char **cmnd, struct command_digest *digest); #endif /* SUDOERS_LDAP_H */