2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-31 14:25:15 +00:00

Always use our own strtonum and implement sudo_strtoid in terms of it.

This commit is contained in:
Todd C. Miller
2019-10-14 10:09:29 -06:00
parent 9d5867eaed
commit 04a17095be
9 changed files with 85 additions and 193 deletions

View File

@@ -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;
}