2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-22 01:49:11 +00:00

Move the file digest code out of match.c and into filedigest.c.

Inspired by RedHat changes that used libgcrypt.
Also add digest_type_to_name() to map a sudo digest type (int)
to a name (string) and use it.
This commit is contained in:
Todd C. Miller 2017-02-20 16:44:12 -07:00
parent 00b76afe46
commit b5e7b7bd2c
9 changed files with 258 additions and 132 deletions

View File

@ -260,8 +260,10 @@ plugins/sudoers/def_data.h
plugins/sudoers/def_data.in
plugins/sudoers/defaults.c
plugins/sudoers/defaults.h
plugins/sudoers/digestname.c
plugins/sudoers/editor.c
plugins/sudoers/env.c
plugins/sudoers/filedigest.c
plugins/sudoers/find_path.c
plugins/sudoers/gc.c
plugins/sudoers/gentime.c

View File

@ -150,11 +150,11 @@ TEST_PROGS = check_iolog_path check_fill check_wrap check_addr check_digest \
AUTH_OBJS = sudo_auth.lo @AUTH_OBJS@
LIBPARSESUDOERS_OBJS = alias.lo audit.lo base64.lo defaults.lo gentime.lo \
hexchar.lo gmtoff.lo gram.lo match.lo match_addr.lo \
pwutil.lo pwutil_impl.lo rcstr.lo redblack.lo \
sudoers_debug.lo timeout.lo timestr.lo toke.lo \
toke_util.lo
LIBPARSESUDOERS_OBJS = alias.lo audit.lo base64.lo defaults.lo digestname.lo \
filedigest.lo gentime.lo gmtoff.lo gram.lo hexchar.lo \
match.lo match_addr.lo pwutil.lo pwutil_impl.lo \
rcstr.lo redblack.lo sudoers_debug.lo timeout.lo \
timestr.lo toke.lo toke_util.lo
SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo find_path.lo \
gc.lo goodpath.lo group_plugin.lo interfaces.lo iolog.lo \
@ -656,6 +656,11 @@ defaults.lo: $(srcdir)/defaults.c $(devdir)/def_data.c $(devdir)/def_data.h \
$(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/defaults.c
digestname.lo: $(srcdir)/digestname.c $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_queue.h $(srcdir)/parse.h \
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/digestname.c
editor.lo: $(srcdir)/editor.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
@ -674,6 +679,17 @@ env.lo: $(srcdir)/env.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \
$(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/env.c
filedigest.lo: $(srcdir)/filedigest.c $(devdir)/def_data.h \
$(incdir)/compat/sha2.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/defaults.h $(srcdir)/logging.h $(srcdir)/parse.h \
$(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
$(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
$(top_builddir)/pathnames.h
$(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/filedigest.c
find_path.lo: $(srcdir)/find_path.c $(devdir)/def_data.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2017 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#include "sudo_compat.h"
#include "sudoers_debug.h"
#include "parse.h"
const char *
digest_type_to_name(int digest_type)
{
const char *digest_name;
debug_decl(digest_type_to_name, SUDOERS_DEBUG_UTIL)
switch (digest_type) {
case SUDO_DIGEST_SHA224:
digest_name = "sha224";
break;
case SUDO_DIGEST_SHA256:
digest_name = "sha256";
break;
case SUDO_DIGEST_SHA384:
digest_name = "sha384";
break;
case SUDO_DIGEST_SHA512:
digest_name = "sha512";
break;
default:
digest_name = "unknown digest";
break;
}
debug_return_const_str(digest_name);
}

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) 2017 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#if defined(HAVE_STDINT_H)
# include <stdint.h>
#elif defined(HAVE_INTTYPES_H)
# include <inttypes.h>
#endif
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include "sudoers.h"
#include "parse.h"
#ifdef HAVE_SHA224UPDATE
# include <sha2.h>
#else
# include "compat/sha2.h"
#endif
static struct digest_function {
const unsigned int digest_len;
void (*init)(SHA2_CTX *);
#ifdef SHA2_VOID_PTR
void (*update)(SHA2_CTX *, const void *, size_t);
void (*final)(void *, SHA2_CTX *);
#else
void (*update)(SHA2_CTX *, const unsigned char *, size_t);
void (*final)(unsigned char *, SHA2_CTX *);
#endif
} digest_functions[] = {
{
SHA224_DIGEST_LENGTH,
SHA224Init,
SHA224Update,
SHA224Final
}, {
SHA256_DIGEST_LENGTH,
SHA256Init,
SHA256Update,
SHA256Final
}, {
SHA384_DIGEST_LENGTH,
SHA384Init,
SHA384Update,
SHA384Final
}, {
SHA512_DIGEST_LENGTH,
SHA512Init,
SHA512Update,
SHA512Final
}, {
0
}
};
unsigned char *
sudo_filedigest(int fd, const char *file, int digest_type, size_t *digest_len)
{
struct digest_function *func = NULL;
unsigned char *file_digest = NULL;
unsigned char buf[32 * 1024];
size_t nread;
SHA2_CTX ctx;
int i, fd2;
FILE *fp = NULL;
debug_decl(sudo_filedigest, SUDOERS_DEBUG_UTIL)
for (i = 0; digest_functions[i].digest_len != 0; i++) {
if (digest_type == i) {
func = &digest_functions[i];
break;
}
}
if (func == NULL) {
sudo_warnx(U_("unsupported digest type %d for %s"), digest_type, file);
goto bad;
}
if ((fd2 = dup(fd)) == -1) {
sudo_debug_printf(SUDO_DEBUG_INFO, "unable to dup %s: %s",
file, strerror(errno));
goto bad;
}
if ((fp = fdopen(fd2, "r")) == NULL) {
sudo_debug_printf(SUDO_DEBUG_INFO, "unable to fdopen %s: %s",
file, strerror(errno));
close(fd2);
goto bad;
}
if ((file_digest = malloc(func->digest_len)) == NULL) {
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
goto bad;
}
func->init(&ctx);
while ((nread = fread(buf, 1, sizeof(buf), fp)) != 0) {
func->update(&ctx, buf, nread);
}
if (ferror(fp)) {
sudo_warnx(U_("%s: read error"), file);
goto bad;
}
func->final(file_digest, &ctx);
fclose(fp);
*digest_len = func->digest_len;
debug_return_ptr(file_digest);
bad:
free(file_digest);
if (fp != NULL)
fclose(fp);
debug_return_ptr(NULL);
}

View File

@ -968,10 +968,8 @@ sudo_ldap_extract_digest(char **cmnd, struct sudo_digest *digest)
cp++;
*cmnd = cp;
DPRINTF1("%s digest %s for %s",
digest_type == SUDO_DIGEST_SHA224 ? "sha224" :
digest_type == SUDO_DIGEST_SHA256 ? "sha256" :
digest_type == SUDO_DIGEST_SHA384 ? "sha384" :
"sha512", digest->digest_str, cp);
digest_type_to_name(digest_type),
digest->digest_str, cp);
debug_return_ptr(digest);
}
}

View File

@ -69,11 +69,6 @@
#else
# include "compat/fnmatch.h"
#endif /* HAVE_FNMATCH */
#ifdef HAVE_SHA224UPDATE
# include <sha2.h>
#else
# include "compat/sha2.h"
#endif
#if !defined(O_SEARCH) && defined(O_PATH)
# define O_SEARCH O_PATH
@ -723,127 +718,62 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const
}
#else /* !SUDOERS_NAME_MATCH */
static struct digest_function {
const char *digest_name;
const unsigned int digest_len;
void (*init)(SHA2_CTX *);
#ifdef SHA2_VOID_PTR
void (*update)(SHA2_CTX *, const void *, size_t);
void (*final)(void *, SHA2_CTX *);
#else
void (*update)(SHA2_CTX *, const unsigned char *, size_t);
void (*final)(unsigned char *, SHA2_CTX *);
#endif
} digest_functions[] = {
{
"SHA224",
SHA224_DIGEST_LENGTH,
SHA224Init,
SHA224Update,
SHA224Final
}, {
"SHA256",
SHA256_DIGEST_LENGTH,
SHA256Init,
SHA256Update,
SHA256Final
}, {
"SHA384",
SHA384_DIGEST_LENGTH,
SHA384Init,
SHA384Update,
SHA384Final
}, {
"SHA512",
SHA512_DIGEST_LENGTH,
SHA512Init,
SHA512Update,
SHA512Final
}, {
NULL
}
};
static bool
digest_matches(int fd, const char *file, const struct sudo_digest *sd)
{
unsigned char file_digest[SHA512_DIGEST_LENGTH];
unsigned char sudoers_digest[SHA512_DIGEST_LENGTH];
unsigned char buf[32 * 1024];
struct digest_function *func = NULL;
size_t nread;
SHA2_CTX ctx;
FILE *fp;
int fd2;
unsigned int i;
unsigned char *file_digest = NULL;
unsigned char *sudoers_digest = NULL;
bool matched = false;
size_t digest_len;
debug_decl(digest_matches, SUDOERS_DEBUG_MATCH)
for (i = 0; digest_functions[i].digest_name != NULL; i++) {
if (sd->digest_type == i) {
func = &digest_functions[i];
break;
file_digest = sudo_filedigest(fd, file, sd->digest_type, &digest_len);
if (file_digest == NULL) {
/* Warning (if any) printed by sudo_filedigest() */
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 (func == NULL) {
sudo_warnx(U_("unsupported digest type %d for %s"), sd->digest_type, file);
debug_return_bool(false);
}
if (strlen(sd->digest_str) == func->digest_len * 2) {
/* Convert the command digest from ascii hex to binary. */
for (i = 0; i < func->digest_len; i++) {
if (strlen(sd->digest_str) == digest_len * 2) {
/* Convert ascii hex to binary. */
unsigned int i;
for (i = 0; i < digest_len; i++) {
const int h = hexchar(&sd->digest_str[i + i]);
if (h == -1)
goto bad_format;
sudoers_digest[i] = (unsigned char)h;
}
} else {
size_t len = base64_decode(sd->digest_str, sudoers_digest,
sizeof(sudoers_digest));
if (len != func->digest_len) {
/* Convert base64 to binary. */
size_t len = base64_decode(sd->digest_str, sudoers_digest, digest_len);
if (len != digest_len) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"incorrect length for digest, expected %u, got %zu",
func->digest_len, len);
"incorrect length for digest, expected %zu, got %zu",
digest_len, len);
goto bad_format;
}
}
if ((fd2 = dup(fd)) == -1) {
sudo_debug_printf(SUDO_DEBUG_INFO, "unable to dup %s: %s",
file, strerror(errno));
debug_return_bool(false);
}
if ((fp = fdopen(fd2, "r")) == NULL) {
sudo_debug_printf(SUDO_DEBUG_INFO, "unable to open %s: %s",
file, strerror(errno));
close(fd2);
debug_return_bool(false);
}
func->init(&ctx);
while ((nread = fread(buf, 1, sizeof(buf), fp)) != 0) {
func->update(&ctx, buf, nread);
}
if (ferror(fp)) {
sudo_warnx(U_("%s: read error"), file);
fclose(fp);
debug_return_bool(false);
}
func->final(file_digest, &ctx);
if (memcmp(file_digest, sudoers_digest, func->digest_len) != 0) {
fclose(fp);
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",
func->digest_name, file, sd->digest_str);
debug_return_bool(false);
digest_type_to_name(sd->digest_type), file, sd->digest_str);
}
goto done;
fclose(fp);
debug_return_bool(true);
bad_format:
sudo_warnx(U_("digest for %s (%s) is not in %s form"), file,
sd->digest_str, func->digest_name);
debug_return_bool(false);
sd->digest_str, digest_type_to_name(sd->digest_type));
done:
free(sudoers_digest);
free(file_digest);
debug_return_bool(matched);
}
static bool

View File

@ -290,4 +290,10 @@ long get_gmtoff(time_t *clock);
/* gentime.c */
time_t parse_gentime(const char *expstr);
/* filedigest.c */
unsigned char *sudo_filedigest(int fd, const char *file, int digest_type, size_t *digest_len);
/* digestname.c */
const char *digest_type_to_name(int digest_type);
#endif /* SUDOERS_PARSE_H */

View File

@ -1059,10 +1059,8 @@ sudo_sss_extract_digest(char **cmnd, struct sudo_digest *digest)
*cmnd = cp;
sudo_debug_printf(SUDO_DEBUG_INFO,
"%s digest %s for %s",
digest_type == SUDO_DIGEST_SHA224 ? "sha224" :
digest_type == SUDO_DIGEST_SHA256 ? "sha256" :
digest_type == SUDO_DIGEST_SHA384 ? "sha384" :
"sha512", digest->digest_str, cp);
digest_type_to_name(digest_type),
digest->digest_str, cp);
debug_return_ptr(digest);
}
}

View File

@ -254,23 +254,7 @@ print_command_json(FILE *fp, struct member *m, int indent, bool last_one)
/* Optional digest. */
if (c->digest != NULL) {
fputs(",\n", fp);
switch (c->digest->digest_type) {
case SUDO_DIGEST_SHA224:
digest_name = "sha224";
break;
case SUDO_DIGEST_SHA256:
digest_name = "sha256";
break;
case SUDO_DIGEST_SHA384:
digest_name = "sha384";
break;
case SUDO_DIGEST_SHA512:
digest_name = "sha512";
break;
default:
digest_name = "invalid digest";
break;
}
digest_name = digest_type_to_name(c->digest->digest_type);
value.type = JSON_STRING;
value.u.string = c->digest->digest_str;
print_pair_json(fp, NULL, digest_name, &value, NULL, indent);