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

@@ -742,9 +742,6 @@
/* Define to 1 if you have the `strsignal' function. */
#undef HAVE_STRSIGNAL
/* Define to 1 if you have the `strtonum' function. */
#undef HAVE_STRTONUM
/* Define to 1 if `d_namlen' is a member of `struct dirent'. */
#undef HAVE_STRUCT_DIRENT_D_NAMLEN

24
configure vendored
View File

@@ -21053,30 +21053,6 @@ else
done
fi
# We wrap OpenBSD's strtonum() to get translatable error strings.
for ac_func in strtonum
do :
ac_fn_c_check_func "$LINENO" "strtonum" "ac_cv_func_strtonum"
if test "x$ac_cv_func_strtonum" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_STRTONUM 1
_ACEOF
fi
done
case " $LIBOBJS " in
*" strtonum.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strtonum.$ac_objext"
;;
esac
for _sym in sudo_strtonum; do
COMPAT_EXP="${COMPAT_EXP}${_sym}
"
done
ac_fn_c_check_member "$LINENO" "struct tm" "tm_gmtoff" "ac_cv_member_struct_tm_tm_gmtoff" "
$ac_includes_default
#include <errno.h>

View File

@@ -2800,10 +2800,6 @@ else
# Missing or non-compliant v?snprintf(), assume missing/bad v?asprintf()
SUDO_APPEND_COMPAT_EXP(sudo_snprintf sudo_vsnprintf sudo_asprintf sudo_vasprintf)
fi
# We wrap OpenBSD's strtonum() to get translatable error strings.
AC_CHECK_FUNCS([strtonum])
AC_LIBOBJ(strtonum)
SUDO_APPEND_COMPAT_EXP(sudo_strtonum)
AC_CHECK_MEMBERS([struct tm.tm_gmtoff], [], [], [
AC_INCLUDES_DEFAULT
#include <errno.h>

View File

@@ -379,7 +379,7 @@ int getdomainname(char *, size_t);
# endif
#endif /* __hpux && !__LP64__ */
/* We wrap OpenBSD's strtonum() to get translatable error strings. */
/* We use our own strtonum() to get translatable error strings. */
__dso_public long long sudo_strtonum(const char *, long long, long long, const char **);
#undef strtonum
#define strtonum(_a, _b, _c, _d) sudo_strtonum((_a), (_b), (_c), (_d))

View File

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

View File

@@ -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;
}
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");
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;
goto done;
ret = 0;
}
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 */

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

View File

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

View File

@@ -116,7 +116,7 @@ sub mkdep {
# XXX - fill in AUTH_OBJS from contents of the auth dir instead
$makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:;
$makefile =~ s:\@DIGEST\@:digest.lo digest_openssl.lo digest_gcrypt.lo:;
$makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo glob.lo inet_ntop_lo inet_pton.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo strtonum.lo utimens.lo vsyslog.lo pipe2.lo:;
$makefile =~ s:\@LTLIBOBJS\@:arc4random.lo arc4random_uniform.lo closefrom.lo fnmatch.lo getaddrinfo.lo getcwd.lo getentropy.lo getgrouplist.lo getdelim.lo getopt_long.lo glob.lo inet_ntop_lo inet_pton.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo pw_dup.lo reallocarray.lo sha2.lo sig2str.lo siglist.lo signame.lo snprintf.lo str2sig.lo strlcat.lo strlcpy.lo strndup.lo strnlen.lo strsignal.lo utimens.lo vsyslog.lo pipe2.lo:;
# Parse OBJS lines
my %objs;