Always use our own strtonum and implement sudo_strtoid in terms of it.
This commit is contained in:
@@ -117,14 +117,15 @@ SHELL = @SHELL@
|
||||
LTOBJS = @DIGEST@ event.lo fatal.lo key_val.lo gethostname.lo gettime.lo \
|
||||
getgrouplist.lo gidlist.lo lbuf.lo locking.lo parseln.lo progname.lo \
|
||||
secure_path.lo setgroups.lo strsplit.lo strtobool.lo strtoid.lo \
|
||||
strtomode.lo sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo \
|
||||
ttyname_dev.lo ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@
|
||||
strtomode.lo strtonum.lo sudo_conf.lo sudo_debug.lo sudo_dso.lo \
|
||||
term.lo ttyname_dev.lo ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@
|
||||
|
||||
IOBJS = $(LTOBJS:.lo=.i)
|
||||
|
||||
POBJS = $(IOBJS:.i=.plog)
|
||||
|
||||
ATOFOO_TEST_OBJS = atofoo_test.lo strtobool.lo strtoid.lo strtomode.lo
|
||||
ATOFOO_TEST_OBJS = atofoo_test.lo strtobool.lo strtoid.lo strtomode.lo \
|
||||
strtonum.lo
|
||||
|
||||
MKTEMP_TEST_OBJS = mktemp_test.lo mktemp.lo
|
||||
|
||||
|
@@ -48,6 +48,9 @@
|
||||
#include "sudo_debug.h"
|
||||
#include "sudo_util.h"
|
||||
|
||||
/* strtoid.c (not exported) */
|
||||
long long sudo_strtonumx(const char *str, long long minval, long long maxval, char **ep, const char **errstrp);
|
||||
|
||||
/*
|
||||
* Make sure that the ID ends with a valid separator char.
|
||||
*/
|
||||
@@ -55,7 +58,6 @@ 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') */
|
||||
@@ -66,7 +68,7 @@ valid_separator(const char *p, const char *ep, const char *sep)
|
||||
valid = true;
|
||||
} while (*sep++ != '\0');
|
||||
}
|
||||
debug_return_bool(valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -76,109 +78,29 @@ valid_separator(const char *p, const char *ep, const char *sep)
|
||||
* On success, returns the parsed ID and clears errstr.
|
||||
* On error, returns 0 and sets errstr.
|
||||
*/
|
||||
#if SIZEOF_ID_T == SIZEOF_LONG_LONG
|
||||
id_t
|
||||
sudo_strtoid_v1(const char *p, const char *sep, char **endp, const char **errstr)
|
||||
sudo_strtoid_v1(const char *p, const char *sep, char **endp, const char **errstrp)
|
||||
{
|
||||
const char *errstr;
|
||||
char *ep;
|
||||
id_t ret = 0;
|
||||
long long llval;
|
||||
id_t ret;
|
||||
debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL)
|
||||
|
||||
/* skip leading space so we can pick up the sign, if any */
|
||||
while (isspace((unsigned char)*p))
|
||||
p++;
|
||||
|
||||
/* While id_t may be 64-bit signed, uid_t and gid_t are 32-bit unsigned. */
|
||||
errno = 0;
|
||||
llval = strtoll(p, &ep, 10);
|
||||
if ((errno == ERANGE && llval == LLONG_MAX) || llval > (id_t)UINT_MAX) {
|
||||
errno = ERANGE;
|
||||
if (errstr != NULL)
|
||||
*errstr = N_("value too large");
|
||||
goto done;
|
||||
ret = sudo_strtonumx(p, INT_MIN, UINT_MAX, &ep, &errstr);
|
||||
if (errstr == NULL) {
|
||||
/*
|
||||
* Disallow id -1 (UINT_MAX), which means "no change"
|
||||
* and check for a valid separator (if specified).
|
||||
*/
|
||||
if (ret == (id_t)-1 || ret == (id_t)UINT_MAX || !valid_separator(p, ep, sep)) {
|
||||
errstr = N_("invalid value");
|
||||
errno = EINVAL;
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
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)
|
||||
*errstr = N_("invalid value");
|
||||
errno = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
ret = (id_t)llval;
|
||||
if (errstr != NULL)
|
||||
*errstr = NULL;
|
||||
if (errstrp != NULL)
|
||||
*errstrp = errstr;
|
||||
if (endp != NULL)
|
||||
*endp = ep;
|
||||
done:
|
||||
debug_return_id_t(ret);
|
||||
}
|
||||
#else
|
||||
id_t
|
||||
sudo_strtoid_v1(const char *p, const char *sep, char **endp, const char **errstr)
|
||||
{
|
||||
char *ep;
|
||||
id_t ret = 0;
|
||||
debug_decl(sudo_strtoid, SUDO_DEBUG_UTIL)
|
||||
|
||||
/* skip leading space so we can pick up the sign, if any */
|
||||
while (isspace((unsigned char)*p))
|
||||
p++;
|
||||
|
||||
errno = 0;
|
||||
if (*p == '-') {
|
||||
long lval = strtol(p, &ep, 10);
|
||||
if ((errno == ERANGE && lval == LONG_MAX) || lval > INT_MAX) {
|
||||
errno = ERANGE;
|
||||
if (errstr != NULL)
|
||||
*errstr = N_("value too large");
|
||||
goto done;
|
||||
}
|
||||
if ((errno == ERANGE && lval == LONG_MIN) || lval < 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) || lval == -1) {
|
||||
if (errstr != NULL)
|
||||
*errstr = N_("invalid value");
|
||||
errno = EINVAL;
|
||||
goto done;
|
||||
}
|
||||
ret = (id_t)lval;
|
||||
} else {
|
||||
unsigned long ulval = strtoul(p, &ep, 10);
|
||||
if ((errno == ERANGE && ulval == ULONG_MAX) || ulval > UINT_MAX) {
|
||||
errno = ERANGE;
|
||||
if (errstr != NULL)
|
||||
*errstr = N_("value too large");
|
||||
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;
|
||||
}
|
||||
if (errstr != NULL)
|
||||
*errstr = NULL;
|
||||
if (endp != NULL)
|
||||
*endp = ep;
|
||||
done:
|
||||
debug_return_id_t(ret);
|
||||
}
|
||||
#endif /* SIZEOF_ID_T == 8 */
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: ISC
|
||||
*
|
||||
* Copyright (c) 2013-2014 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
* Copyright (c) 2013-2015, 2019 Todd C. Miller <Todd.Miller@sudo.ws>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -42,40 +42,8 @@
|
||||
|
||||
#include "sudo_compat.h"
|
||||
|
||||
#ifdef HAVE_STRTONUM
|
||||
|
||||
/*
|
||||
* The OpenBSD strtonum error string too short to be translated sensibly.
|
||||
* This wrapper just changes errstr as follows:
|
||||
* invalid -> invalid value
|
||||
* too large -> value too large
|
||||
* too small -> value too small
|
||||
*/
|
||||
long long
|
||||
sudo_strtonum(const char *str, long long minval, long long maxval,
|
||||
const char **errstrp)
|
||||
{
|
||||
long long retval;
|
||||
const char *errstr;
|
||||
|
||||
# undef strtonum
|
||||
retval = strtonum(str, minval, maxval, &errstr);
|
||||
if (errstr != NULL) {
|
||||
if (errno == EINVAL) {
|
||||
errstr = N_("invalid value");
|
||||
} else if (errno == ERANGE) {
|
||||
errstr = strcmp(errstr, "too large") == 0 ?
|
||||
N_("value too large") : N_("value too small");
|
||||
}
|
||||
}
|
||||
if (errstrp != NULL)
|
||||
*errstrp = errstr;
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
enum strtonum_err {
|
||||
STN_INITIAL,
|
||||
STN_VALID,
|
||||
STN_INVALID,
|
||||
STN_TOOSMALL,
|
||||
@@ -84,16 +52,18 @@ enum strtonum_err {
|
||||
|
||||
/*
|
||||
* Convert a string to a number in the range [minval, maxval]
|
||||
* Unlike strtonum(), this returns the first non-digit in endp (if not NULL).
|
||||
*/
|
||||
long long
|
||||
sudo_strtonum(const char *str, long long minval, long long maxval,
|
||||
sudo_strtonumx(const char *str, long long minval, long long maxval, char **endp,
|
||||
const char **errstrp)
|
||||
{
|
||||
const unsigned char *ustr = (const unsigned char *)str;
|
||||
enum strtonum_err errval = STN_VALID;
|
||||
enum strtonum_err errval = STN_INITIAL;
|
||||
long long lastval, result = 0;
|
||||
unsigned char dig, sign;
|
||||
const char *cp = str;
|
||||
unsigned char ch;
|
||||
int remainder;
|
||||
char sign;
|
||||
|
||||
if (minval > maxval) {
|
||||
errval = STN_INVALID;
|
||||
@@ -101,16 +71,16 @@ sudo_strtonum(const char *str, long long minval, long long maxval,
|
||||
}
|
||||
|
||||
/* Trim leading space and check sign, if any. */
|
||||
while (isspace(*ustr)) {
|
||||
ustr++;
|
||||
}
|
||||
switch (*ustr) {
|
||||
do {
|
||||
ch = *cp++;
|
||||
} while (isspace(ch));
|
||||
switch (ch) {
|
||||
case '-':
|
||||
sign = '-';
|
||||
ustr++;
|
||||
ch = *cp++;
|
||||
break;
|
||||
case '+':
|
||||
ustr++;
|
||||
ch = *cp++;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
sign = '+';
|
||||
@@ -133,18 +103,17 @@ sudo_strtonum(const char *str, long long minval, long long maxval,
|
||||
lastval += 1;
|
||||
remainder += 10;
|
||||
}
|
||||
while ((dig = *ustr++) != '\0') {
|
||||
if (!isdigit(dig)) {
|
||||
errval = STN_INVALID;
|
||||
for (;; ch = *cp++) {
|
||||
if (!isdigit(ch))
|
||||
break;
|
||||
}
|
||||
dig -= '0';
|
||||
if (result < lastval || (result == lastval && dig > remainder)) {
|
||||
ch -= '0';
|
||||
if (result < lastval || (result == lastval && ch > remainder)) {
|
||||
errval = STN_TOOSMALL;
|
||||
break;
|
||||
} else {
|
||||
errval = STN_VALID;
|
||||
result *= 10;
|
||||
result -= dig;
|
||||
result -= ch;
|
||||
}
|
||||
}
|
||||
if (result > maxval)
|
||||
@@ -152,18 +121,17 @@ sudo_strtonum(const char *str, long long minval, long long maxval,
|
||||
} else {
|
||||
lastval = maxval / 10;
|
||||
remainder = maxval % 10;
|
||||
while ((dig = *ustr++) != '\0') {
|
||||
if (!isdigit(dig)) {
|
||||
errval = STN_INVALID;
|
||||
for (;; ch = *cp++) {
|
||||
if (!isdigit(ch))
|
||||
break;
|
||||
}
|
||||
dig -= '0';
|
||||
if (result > lastval || (result == lastval && dig > remainder)) {
|
||||
ch -= '0';
|
||||
if (result > lastval || (result == lastval && ch > remainder)) {
|
||||
errval = STN_TOOBIG;
|
||||
break;
|
||||
} else {
|
||||
errval = STN_VALID;
|
||||
result *= 10;
|
||||
result += dig;
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
if (result < minval)
|
||||
@@ -172,6 +140,7 @@ sudo_strtonum(const char *str, long long minval, long long maxval,
|
||||
|
||||
done:
|
||||
switch (errval) {
|
||||
case STN_INITIAL:
|
||||
case STN_VALID:
|
||||
if (errstrp != NULL)
|
||||
*errstrp = NULL;
|
||||
@@ -183,18 +152,48 @@ done:
|
||||
*errstrp = N_("invalid value");
|
||||
break;
|
||||
case STN_TOOSMALL:
|
||||
/* Skip remaining digits. */
|
||||
while (isdigit(ch))
|
||||
ch = *cp++;
|
||||
result = 0;
|
||||
errno = ERANGE;
|
||||
if (errstrp != NULL)
|
||||
*errstrp = N_("value too small");
|
||||
break;
|
||||
case STN_TOOBIG:
|
||||
/* Skip remaining digits. */
|
||||
while (isdigit(ch))
|
||||
ch = *cp++;
|
||||
result = 0;
|
||||
errno = ERANGE;
|
||||
if (errstrp != NULL)
|
||||
*errstrp = N_("value too large");
|
||||
break;
|
||||
}
|
||||
if (endp != NULL)
|
||||
*endp = (char *)(errval == STN_INITIAL ? str : cp - 1);
|
||||
return result;
|
||||
}
|
||||
#endif /* HAVE_STRTONUM */
|
||||
|
||||
/*
|
||||
* Convert a string to a number in the range [minval, maxval]
|
||||
*/
|
||||
long long
|
||||
sudo_strtonum(const char *str, long long minval, long long maxval,
|
||||
const char **errstrp)
|
||||
{
|
||||
const char *errstr;
|
||||
char *ep;
|
||||
long long ret;
|
||||
|
||||
ret = sudo_strtonumx(str, minval, maxval, &ep, &errstr);
|
||||
/* Check for empty string and terminating NUL. */
|
||||
if (str == ep || *ep != '\0') {
|
||||
errno = EINVAL;
|
||||
errstr = N_("invalid value");
|
||||
ret = 0;
|
||||
}
|
||||
if (errstrp != NULL)
|
||||
*errstrp = errstr;
|
||||
return ret;
|
||||
}
|
||||
|
@@ -99,6 +99,7 @@ sudo_strsplit_v1
|
||||
sudo_strtobool_v1
|
||||
sudo_strtoid_v1
|
||||
sudo_strtomode_v1
|
||||
sudo_strtonum
|
||||
sudo_term_cbreak_v1
|
||||
sudo_term_copy_v1
|
||||
sudo_term_eof
|
||||
|
Reference in New Issue
Block a user