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. */ /* Define to 1 if you have the `strsignal' function. */
#undef HAVE_STRSIGNAL #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'. */ /* Define to 1 if `d_namlen' is a member of `struct dirent'. */
#undef HAVE_STRUCT_DIRENT_D_NAMLEN #undef HAVE_STRUCT_DIRENT_D_NAMLEN

24
configure vendored
View File

@@ -21053,30 +21053,6 @@ else
done done
fi 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_fn_c_check_member "$LINENO" "struct tm" "tm_gmtoff" "ac_cv_member_struct_tm_tm_gmtoff" "
$ac_includes_default $ac_includes_default
#include <errno.h> #include <errno.h>

View File

@@ -2800,10 +2800,6 @@ else
# Missing or non-compliant v?snprintf(), assume missing/bad v?asprintf() # Missing or non-compliant v?snprintf(), assume missing/bad v?asprintf()
SUDO_APPEND_COMPAT_EXP(sudo_snprintf sudo_vsnprintf sudo_asprintf sudo_vasprintf) SUDO_APPEND_COMPAT_EXP(sudo_snprintf sudo_vsnprintf sudo_asprintf sudo_vasprintf)
fi 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_CHECK_MEMBERS([struct tm.tm_gmtoff], [], [], [
AC_INCLUDES_DEFAULT AC_INCLUDES_DEFAULT
#include <errno.h> #include <errno.h>

View File

@@ -379,7 +379,7 @@ int getdomainname(char *, size_t);
# endif # endif
#endif /* __hpux && !__LP64__ */ #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 **); __dso_public long long sudo_strtonum(const char *, long long, long long, const char **);
#undef strtonum #undef strtonum
#define strtonum(_a, _b, _c, _d) sudo_strtonum((_a), (_b), (_c), (_d)) #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 \ 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 \ getgrouplist.lo gidlist.lo lbuf.lo locking.lo parseln.lo progname.lo \
secure_path.lo setgroups.lo strsplit.lo strtobool.lo strtoid.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 \ strtomode.lo strtonum.lo sudo_conf.lo sudo_debug.lo sudo_dso.lo \
ttyname_dev.lo ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@ term.lo ttyname_dev.lo ttysize.lo @COMMON_OBJS@ @LTLIBOBJS@
IOBJS = $(LTOBJS:.lo=.i) IOBJS = $(LTOBJS:.lo=.i)
POBJS = $(IOBJS:.i=.plog) 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 MKTEMP_TEST_OBJS = mktemp_test.lo mktemp.lo

View File

@@ -48,6 +48,9 @@
#include "sudo_debug.h" #include "sudo_debug.h"
#include "sudo_util.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. * 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) valid_separator(const char *p, const char *ep, const char *sep)
{ {
bool valid = false; bool valid = false;
debug_decl(valid_separator, SUDO_DEBUG_UTIL)
if (ep != p) { if (ep != p) {
/* check for valid separator (including '\0') */ /* check for valid separator (including '\0') */
@@ -66,7 +68,7 @@ valid_separator(const char *p, const char *ep, const char *sep)
valid = true; valid = true;
} while (*sep++ != '\0'); } 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 success, returns the parsed ID and clears errstr.
* On error, returns 0 and sets errstr. * On error, returns 0 and sets errstr.
*/ */
#if SIZEOF_ID_T == SIZEOF_LONG_LONG
id_t 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; char *ep;
id_t ret = 0; id_t ret;
long long llval;
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 */ ret = sudo_strtonumx(p, INT_MIN, UINT_MAX, &ep, &errstr);
while (isspace((unsigned char)*p)) if (errstr == NULL) {
p++; /*
* Disallow id -1 (UINT_MAX), which means "no change"
/* While id_t may be 64-bit signed, uid_t and gid_t are 32-bit unsigned. */ * and check for a valid separator (if specified).
errno = 0; */
llval = strtoll(p, &ep, 10); if (ret == (id_t)-1 || ret == (id_t)UINT_MAX || !valid_separator(p, ep, sep)) {
if ((errno == ERANGE && llval == LLONG_MAX) || llval > (id_t)UINT_MAX) { errstr = N_("invalid value");
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");
errno = EINVAL; errno = EINVAL;
goto done; ret = 0;
} }
ret = (id_t)llval; }
if (errstr != NULL) if (errstrp != NULL)
*errstr = NULL; *errstrp = errstr;
if (endp != NULL) if (endp != NULL)
*endp = ep; *endp = ep;
done:
debug_return_id_t(ret); 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 * 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 * 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
@@ -42,40 +42,8 @@
#include "sudo_compat.h" #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 { enum strtonum_err {
STN_INITIAL,
STN_VALID, STN_VALID,
STN_INVALID, STN_INVALID,
STN_TOOSMALL, STN_TOOSMALL,
@@ -84,16 +52,18 @@ enum strtonum_err {
/* /*
* Convert a string to a number in the range [minval, maxval] * 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 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 char **errstrp)
{ {
const unsigned char *ustr = (const unsigned char *)str; enum strtonum_err errval = STN_INITIAL;
enum strtonum_err errval = STN_VALID;
long long lastval, result = 0; long long lastval, result = 0;
unsigned char dig, sign; const char *cp = str;
unsigned char ch;
int remainder; int remainder;
char sign;
if (minval > maxval) { if (minval > maxval) {
errval = STN_INVALID; 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. */ /* Trim leading space and check sign, if any. */
while (isspace(*ustr)) { do {
ustr++; ch = *cp++;
} } while (isspace(ch));
switch (*ustr) { switch (ch) {
case '-': case '-':
sign = '-'; sign = '-';
ustr++; ch = *cp++;
break; break;
case '+': case '+':
ustr++; ch = *cp++;
/* FALLTHROUGH */ /* FALLTHROUGH */
default: default:
sign = '+'; sign = '+';
@@ -133,18 +103,17 @@ sudo_strtonum(const char *str, long long minval, long long maxval,
lastval += 1; lastval += 1;
remainder += 10; remainder += 10;
} }
while ((dig = *ustr++) != '\0') { for (;; ch = *cp++) {
if (!isdigit(dig)) { if (!isdigit(ch))
errval = STN_INVALID;
break; break;
} ch -= '0';
dig -= '0'; if (result < lastval || (result == lastval && ch > remainder)) {
if (result < lastval || (result == lastval && dig > remainder)) {
errval = STN_TOOSMALL; errval = STN_TOOSMALL;
break; break;
} else { } else {
errval = STN_VALID;
result *= 10; result *= 10;
result -= dig; result -= ch;
} }
} }
if (result > maxval) if (result > maxval)
@@ -152,18 +121,17 @@ sudo_strtonum(const char *str, long long minval, long long maxval,
} else { } else {
lastval = maxval / 10; lastval = maxval / 10;
remainder = maxval % 10; remainder = maxval % 10;
while ((dig = *ustr++) != '\0') { for (;; ch = *cp++) {
if (!isdigit(dig)) { if (!isdigit(ch))
errval = STN_INVALID;
break; break;
} ch -= '0';
dig -= '0'; if (result > lastval || (result == lastval && ch > remainder)) {
if (result > lastval || (result == lastval && dig > remainder)) {
errval = STN_TOOBIG; errval = STN_TOOBIG;
break; break;
} else { } else {
errval = STN_VALID;
result *= 10; result *= 10;
result += dig; result += ch;
} }
} }
if (result < minval) if (result < minval)
@@ -172,6 +140,7 @@ sudo_strtonum(const char *str, long long minval, long long maxval,
done: done:
switch (errval) { switch (errval) {
case STN_INITIAL:
case STN_VALID: case STN_VALID:
if (errstrp != NULL) if (errstrp != NULL)
*errstrp = NULL; *errstrp = NULL;
@@ -183,18 +152,48 @@ done:
*errstrp = N_("invalid value"); *errstrp = N_("invalid value");
break; break;
case STN_TOOSMALL: case STN_TOOSMALL:
/* Skip remaining digits. */
while (isdigit(ch))
ch = *cp++;
result = 0; result = 0;
errno = ERANGE; errno = ERANGE;
if (errstrp != NULL) if (errstrp != NULL)
*errstrp = N_("value too small"); *errstrp = N_("value too small");
break; break;
case STN_TOOBIG: case STN_TOOBIG:
/* Skip remaining digits. */
while (isdigit(ch))
ch = *cp++;
result = 0; result = 0;
errno = ERANGE; errno = ERANGE;
if (errstrp != NULL) if (errstrp != NULL)
*errstrp = N_("value too large"); *errstrp = N_("value too large");
break; break;
} }
if (endp != NULL)
*endp = (char *)(errval == STN_INITIAL ? str : cp - 1);
return result; 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_strtobool_v1
sudo_strtoid_v1 sudo_strtoid_v1
sudo_strtomode_v1 sudo_strtomode_v1
sudo_strtonum
sudo_term_cbreak_v1 sudo_term_cbreak_v1
sudo_term_copy_v1 sudo_term_copy_v1
sudo_term_eof sudo_term_eof

View File

@@ -116,7 +116,7 @@ sub mkdep {
# XXX - fill in AUTH_OBJS from contents of the auth dir instead # 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:\@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:\@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 # Parse OBJS lines
my %objs; my %objs;