diff --git a/config.h.in b/config.h.in index 2ffb3c2db..9e8e14341 100644 --- a/config.h.in +++ b/config.h.in @@ -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 diff --git a/configure b/configure index 665caee88..94d774e7b 100755 --- a/configure +++ b/configure @@ -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 diff --git a/configure.ac b/configure.ac index 7c9d532d5..351c7676f 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/include/sudo_compat.h b/include/sudo_compat.h index 90327b043..344fd4122 100644 --- a/include/sudo_compat.h +++ b/include/sudo_compat.h @@ -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)) diff --git a/lib/util/Makefile.in b/lib/util/Makefile.in index 147cf6217..09f317577 100644 --- a/lib/util/Makefile.in +++ b/lib/util/Makefile.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 diff --git a/lib/util/strtoid.c b/lib/util/strtoid.c index 6b3916bfc..cefc021d9 100644 --- a/lib/util/strtoid.c +++ b/lib/util/strtoid.c @@ -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 */ diff --git a/lib/util/strtonum.c b/lib/util/strtonum.c index 4bc97012e..5bb291433 100644 --- a/lib/util/strtonum.c +++ b/lib/util/strtonum.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2013-2014 Todd C. Miller + * Copyright (c) 2013-2015, 2019 Todd C. Miller * * 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; +} diff --git a/lib/util/util.exp.in b/lib/util/util.exp.in index 66b5a0160..1f74b4f3f 100644 --- a/lib/util/util.exp.in +++ b/lib/util/util.exp.in @@ -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 diff --git a/mkdep.pl b/mkdep.pl index ca5705636..bc17631eb 100755 --- a/mkdep.pl +++ b/mkdep.pl @@ -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;