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

Treat an ID of -1 as invalid since that means "no change".

Fixes CVE-2019-14287.
Found by Joe Vennix from Apple Information Security.
This commit is contained in:
Todd C. Miller 2019-10-10 10:04:13 -06:00
parent fd5d0f511e
commit f752ae5cee
2 changed files with 57 additions and 47 deletions

4
NEWS
View File

@ -76,6 +76,10 @@ What's new in Sudo 1.8.28
* Fixed a bug where the terminal's file context was not restored * Fixed a bug where the terminal's file context was not restored
when using SELinux RBAC. Bug #898. when using SELinux RBAC. Bug #898.
* Fixed CVE-2019-14287, a bug where a sudo user may be able to
run a command as root when the Runas specification explicitly
disallows root access as long as the ALL keyword is listed first.
What's new in Sudo 1.8.27 What's new in Sudo 1.8.27
* On HP-UX, sudo will now update the utmps file when running a command * On HP-UX, sudo will now update the utmps file when running a command

View File

@ -1,7 +1,7 @@
/* /*
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* Copyright (c) 2013-2016 Todd C. Miller <Todd.Miller@sudo.ws> * Copyright (c) 2013-2019 Todd C. Miller <Todd.Miller@sudo.ws>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@ -48,6 +48,27 @@
#include "sudo_debug.h" #include "sudo_debug.h"
#include "sudo_util.h" #include "sudo_util.h"
/*
* Make sure that the ID ends with a valid separator char.
*/
static bool
valid_separator(const char *p, const char *ep, const char *sep)
{
bool valid = false;
debug_decl(valid_separator, SUDO_DEBUG_UTIL)
if (ep != p) {
/* check for valid separator (including '\0') */
if (sep == NULL)
sep = "";
do {
if (*ep == *sep)
valid = true;
} while (*sep++ != '\0');
}
debug_return_bool(valid);
}
/* /*
* Parse a uid/gid in string form. * Parse a uid/gid in string form.
* If sep is non-NULL, it contains valid separator characters (e.g. comma, space) * If sep is non-NULL, it contains valid separator characters (e.g. comma, space)
@ -62,38 +83,35 @@ sudo_strtoid_v1(const char *p, const char *sep, char **endp, const char **errstr
char *ep; char *ep;
id_t ret = 0; id_t ret = 0;
long long llval; long long llval;
bool valid = false;
debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL) debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL)
/* skip leading space so we can pick up the sign, if any */ /* skip leading space so we can pick up the sign, if any */
while (isspace((unsigned char)*p)) while (isspace((unsigned char)*p))
p++; p++;
if (sep == NULL)
sep = ""; /* While id_t may be 64-bit signed, uid_t and gid_t are 32-bit unsigned. */
errno = 0; errno = 0;
llval = strtoll(p, &ep, 10); llval = strtoll(p, &ep, 10);
if (ep != p) { if ((errno == ERANGE && llval == LLONG_MAX) || llval > (id_t)UINT_MAX) {
/* check for valid separator (including '\0') */ errno = ERANGE;
do { if (errstr != NULL)
if (*ep == *sep) *errstr = N_("value too large");
valid = true; goto done;
} while (*sep++ != '\0');
} }
if (!valid) { if ((errno == ERANGE && llval == LLONG_MIN) || llval < INT_MIN) {
errno = ERANGE;
if (errstr != NULL)
*errstr = N_("value too small");
goto done;
}
/* Disallow id -1, which means "no change". */
if (!valid_separator(p, ep, sep) || llval == -1 || llval == (id_t)UINT_MAX) {
if (errstr != NULL) if (errstr != NULL)
*errstr = N_("invalid value"); *errstr = N_("invalid value");
errno = EINVAL; errno = EINVAL;
goto done; goto done;
} }
if (errno == ERANGE) {
if (errstr != NULL) {
if (llval == LLONG_MAX)
*errstr = N_("value too large");
else
*errstr = N_("value too small");
}
goto done;
}
ret = (id_t)llval; ret = (id_t)llval;
if (errstr != NULL) if (errstr != NULL)
*errstr = NULL; *errstr = NULL;
@ -108,30 +126,15 @@ sudo_strtoid_v1(const char *p, const char *sep, char **endp, const char **errstr
{ {
char *ep; char *ep;
id_t ret = 0; id_t ret = 0;
bool valid = false;
debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL) debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL)
/* skip leading space so we can pick up the sign, if any */ /* skip leading space so we can pick up the sign, if any */
while (isspace((unsigned char)*p)) while (isspace((unsigned char)*p))
p++; p++;
if (sep == NULL)
sep = "";
errno = 0; errno = 0;
if (*p == '-') { if (*p == '-') {
long lval = strtol(p, &ep, 10); long lval = strtol(p, &ep, 10);
if (ep != p) {
/* check for valid separator (including '\0') */
do {
if (*ep == *sep)
valid = true;
} while (*sep++ != '\0');
}
if (!valid) {
if (errstr != NULL)
*errstr = N_("invalid value");
errno = EINVAL;
goto done;
}
if ((errno == ERANGE && lval == LONG_MAX) || lval > INT_MAX) { if ((errno == ERANGE && lval == LONG_MAX) || lval > INT_MAX) {
errno = ERANGE; errno = ERANGE;
if (errstr != NULL) if (errstr != NULL)
@ -144,28 +147,31 @@ sudo_strtoid_v1(const char *p, const char *sep, char **endp, const char **errstr
*errstr = N_("value too small"); *errstr = N_("value too small");
goto done; goto done;
} }
ret = (id_t)lval;
} else { /* Disallow id -1, which means "no change". */
unsigned long ulval = strtoul(p, &ep, 10); if (!valid_separator(p, ep, sep) || lval == -1) {
if (ep != p) {
/* check for valid separator (including '\0') */
do {
if (*ep == *sep)
valid = true;
} while (*sep++ != '\0');
}
if (!valid) {
if (errstr != NULL) if (errstr != NULL)
*errstr = N_("invalid value"); *errstr = N_("invalid value");
errno = EINVAL; errno = EINVAL;
goto done; goto done;
} }
ret = (id_t)lval;
} else {
unsigned long ulval = strtoul(p, &ep, 10);
if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > UINT_MAX) { if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > UINT_MAX) {
errno = ERANGE; errno = ERANGE;
if (errstr != NULL) if (errstr != NULL)
*errstr = N_("value too large"); *errstr = N_("value too large");
goto done; goto done;
} }
/* Disallow id -1, which means "no change". */
if (!valid_separator(p, ep, sep) || ulval == UINT_MAX) {
if (errstr != NULL)
*errstr = N_("invalid value");
errno = EINVAL;
goto done;
}
ret = (id_t)ulval; ret = (id_t)ulval;
} }
if (errstr != NULL) if (errstr != NULL)