diff --git a/common/sudo_conf.c b/common/sudo_conf.c index 73dc73aa1..bde2c3384 100644 --- a/common/sudo_conf.c +++ b/common/sudo_conf.c @@ -182,16 +182,14 @@ set_var_group_source(const char *entry, const char *conf_file) static void set_var_max_groups(const char *entry, const char *conf_file) { - long lval; - char *ep; + int max_groups; - lval = strtol(entry, &ep, 10); - if (*entry == '\0' || *ep != '\0' || lval <= 0 || lval > INT_MAX || - (errno == ERANGE && lval == LONG_MAX)) { - warningx(U_("invalid max groups `%s' in %s, line %d"), entry, - conf_file, conf_lineno); + max_groups = strtonum(entry, 1, INT_MAX, NULL); + if (max_groups > 0) { + sudo_conf_data.max_groups = max_groups; } else { - sudo_conf_data.max_groups = (int)lval; + warningx(U_("invalid max groups `%s' in %s, line %d"), entry, + conf_file, conf_lineno); } } diff --git a/common/ttysize.c b/common/ttysize.c index c30643d58..13c5b5779 100644 --- a/common/ttysize.c +++ b/common/ttysize.c @@ -77,10 +77,14 @@ get_ttysize(int *rowp, int *colp) char *p; /* Fall back on $LINES and $COLUMNS. */ - if ((p = getenv("LINES")) == NULL || (*rowp = atoi(p)) <= 0) + if ((p = getenv("LINES")) == NULL || + (*rowp = strtonum(p, 1, INT_MAX, NULL)) <= 0) { *rowp = 24; - if ((p = getenv("COLUMNS")) == NULL || (*colp = atoi(p)) <= 0) + } + if ((p = getenv("COLUMNS")) == NULL || + (*colp = strtonum(p, 1, INT_MAX, NULL)) <= 0) { *colp = 80; + } } debug_return; diff --git a/compat/Makefile.in b/compat/Makefile.in index 874854b1e..843200e38 100644 --- a/compat/Makefile.in +++ b/compat/Makefile.in @@ -222,7 +222,8 @@ strlcpy.lo: $(srcdir)/strlcpy.c $(incdir)/missing.h $(top_builddir)/config.h strsignal.lo: $(srcdir)/strsignal.c $(incdir)/gettext.h $(incdir)/missing.h \ $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strsignal.c -strtonum.lo: $(srcdir)/strtonum.c $(incdir)/missing.h $(top_builddir)/config.h +strtonum.lo: $(srcdir)/strtonum.c $(incdir)/gettext.h $(incdir)/missing.h \ + $(top_builddir)/config.h $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/strtonum.c utimes.lo: $(srcdir)/utimes.c $(incdir)/missing.h $(top_builddir)/config.h \ $(top_srcdir)/compat/utime.h diff --git a/compat/closefrom.c b/compat/closefrom.c index da2870a46..c2fe77735 100644 --- a/compat/closefrom.c +++ b/compat/closefrom.c @@ -86,7 +86,7 @@ closefrom_fallback(int lowfd) for (fd = lowfd; fd < maxfd; fd++) { #ifdef __APPLE__ /* Avoid potential crash with libdispatch when we close its fds. */ - (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + (void) fcntl((int) fd, F_SETFD, FD_CLOEXEC); #else (void) close((int) fd); #endif @@ -123,17 +123,17 @@ void closefrom(int lowfd) { struct dirent *dent; + const char *errstr; DIR *dirp; - char *endp; - long fd; + int fd; /* Use /proc/self/fd directory if it exists. */ if ((dirp = opendir("/proc/self/fd")) != NULL) { while ((dent = readdir(dirp)) != NULL) { - fd = strtol(dent->d_name, &endp, 10); - if (dent->d_name != endp && *endp == '\0' && - fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) - (void) close((int) fd); + fd = strtonum(dent->d_name, lowfd, INT_MAX, &errstr); + if (errstr == NULL && fd != dirfd(dirp)) { + (void) close(fd); + } } (void) closedir(dirp); } else diff --git a/compat/getaddrinfo.c b/compat/getaddrinfo.c index ef514b5bc..2e3da84c5 100644 --- a/compat/getaddrinfo.c +++ b/compat/getaddrinfo.c @@ -187,29 +187,6 @@ freeaddrinfo(struct addrinfo *ai) } -/* - * Convert a numeric service string to a number with error checking, returning - * true if the number was parsed correctly and false otherwise. Stores the - * converted number in the second argument. Equivalent to calling strtol, but - * with the base always fixed at 10, with checking of errno, ensuring that all - * of the string is consumed, and checking that the resulting number is - * positive. - */ -static int -convert_service(const char *string, long *result) -{ - char *end; - - if (*string == '\0') - return 0; - errno = 0; - *result = strtol(string, &end, 10); - if (errno != 0 || *end != '\0' || *result < 0) - return 0; - return 1; -} - - /* * Allocate a new addrinfo struct, setting some defaults given that this * implementation is IPv4 only. Also allocates an attached sockaddr_in and @@ -268,12 +245,14 @@ gai_service(const char *servname, int flags, int *type, unsigned short *port) { struct servent *servent; const char *protocol; - long value; + const char *errstr; + unsigned short value; - if (convert_service(servname, &value)) { - if (value > (1L << 16) - 1) - return EAI_SERVICE; + value = strtonum(servname, 0, USHRT_MAX, &errstr); + if (errstr == NULL) { *port = value; + } else if (errno == ERANGE) { + return EAI_SERVICE; } else { if (flags & AI_NUMERICSERV) return EAI_NONAME; diff --git a/compat/strtonum.c b/compat/strtonum.c index d1b5e9180..7db673e28 100644 --- a/compat/strtonum.c +++ b/compat/strtonum.c @@ -28,6 +28,9 @@ #include "missing.h" +#define DEFAULT_TEXT_DOMAIN "sudo" +#include "gettext.h" + enum strtonum_err { STN_VALID, STN_INVALID, @@ -133,19 +136,19 @@ done: result = 0; errno = EINVAL; if (errstrp != NULL) - *errstrp = "invalid"; + *errstrp = N_("invalid"); break; case STN_TOOSMALL: result = 0; errno = ERANGE; if (errstrp != NULL) - *errstrp = "too small"; + *errstrp = N_("too small"); break; case STN_TOOBIG: result = 0; errno = ERANGE; if (errstrp != NULL) - *errstrp = "too large"; + *errstrp = N_("too large"); break; } return result; diff --git a/configure b/configure index f37737f86..35d91eb54 100755 --- a/configure +++ b/configure @@ -16297,7 +16297,7 @@ $as_echo "#define HAVE_LONG_LONG_INT 1" >>confdefs.h fi if test X"$ac_cv_type_long_long_int" != X"yes"; then - as_fn_error $? "\"C compiler does not appear have required long long support\"" "$LINENO" 5 + as_fn_error $? "\"C compiler does not appear to support the long long int type\"" "$LINENO" 5 fi # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects diff --git a/configure.ac b/configure.ac index c5bf464e8..5f11b943b 100644 --- a/configure.ac +++ b/configure.ac @@ -2196,7 +2196,7 @@ AC_CHECK_TYPES([struct in6_addr], [], [], [#include #include ]) AC_TYPE_LONG_LONG_INT if test X"$ac_cv_type_long_long_int" != X"yes"; then - AC_MSG_ERROR(["C compiler does not appear have required long long support"]) + AC_MSG_ERROR(["C compiler does not appear to support the long long int type"]) fi AC_CHECK_SIZEOF([long int]) AC_CHECK_TYPE(id_t, unsigned int) diff --git a/include/missing.h b/include/missing.h index a4051b3ad..6dfceead1 100644 --- a/include/missing.h +++ b/include/missing.h @@ -124,10 +124,38 @@ # define OPEN_MAX 256 #endif +#ifndef USHRT_MAX +# define USHRT_MAX 0xffff +#endif + #ifndef INT_MAX # define INT_MAX 0x7fffffff #endif +#ifndef INT_MIN +# define INT_MIN (-0x7fffffff-1) +#endif + +#ifndef UINT_MAX +# define UINT_MAX 0xffffffffU +#endif + +#ifndef LLONG_MAX +# if defined(QUAD_MAX) +# define LLONG_MAX QUAD_MAX +# else +# define LLONG_MAX 0x7fffffffffffffffLL +# endif +#endif + +#ifndef LLONG_MIN +# if defined(QUAD_MIN) +# define LLONG_MIN QUAD_MIN +# else +# define LLONG_MIN (-0x7fffffffffffffffLL-1) +# endif +#endif + #ifndef PATH_MAX # ifdef _POSIX_PATH_MAX # define PATH_MAX _POSIX_PATH_MAX diff --git a/plugins/sudoers/boottime.c b/plugins/sudoers/boottime.c index 68672c0f8..16fb376f0 100644 --- a/plugins/sudoers/boottime.c +++ b/plugins/sudoers/boottime.c @@ -74,21 +74,12 @@ get_boottime(struct timeval *tv) if (fp != NULL) { while ((len = getline(&line, &linesize, fp)) != -1) { if (strncmp(line, "btime ", 6) == 0) { -#ifdef HAVE_STRTOLL - long long llval = strtoll(line + 6, &ep, 10); - if (line[6] != '\0' && *ep == '\0' && (time_t)llval == llval) { + long long llval = strtonum(line + 6, 1, LLONG_MAX, NULL); + if (llval > 0) { tv->tv_sec = (time_t)llval; tv->tv_usec = 0; debug_return_bool(1); } -#else - long lval = strtol(line + 6, &ep, 10); - if (line[6] != '\0' && *ep == '\0' && (time_t)lval == lval) { - tv->tv_sec = (time_t)llval; - tv->tv_usec = 0; - debug_return_bool(1); - } -#endif } } fclose(fp); diff --git a/plugins/sudoers/defaults.c b/plugins/sudoers/defaults.c index 2e0ec7ab6..b6f7622c5 100644 --- a/plugins/sudoers/defaults.c +++ b/plugins/sudoers/defaults.c @@ -600,18 +600,20 @@ check_defaults(int what, bool quiet) static bool store_int(char *val, struct sudo_defs_types *def, int op) { - char *endp; - long l; + const char *errstr; + int i; debug_decl(store_int, SUDO_DEBUG_DEFAULTS) if (op == false) { def->sd_un.ival = 0; } else { - l = strtol(val, &endp, 10); - if (*endp != '\0') + i = strtonum(val, INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s is %s", val, errstr); debug_return_bool(false); - /* XXX - should check against INT_MAX */ - def->sd_un.ival = (int)l; + } + def->sd_un.ival = i; } if (def->callback) debug_return_bool(def->callback(val)); @@ -621,18 +623,21 @@ store_int(char *val, struct sudo_defs_types *def, int op) static bool store_uint(char *val, struct sudo_defs_types *def, int op) { - char *endp; - long l; + const char *errstr; + unsigned int u; debug_decl(store_uint, SUDO_DEBUG_DEFAULTS) if (op == false) { def->sd_un.ival = 0; } else { - l = strtol(val, &endp, 10); - if (*endp != '\0' || l < 0) + u = strtonum(val, 0, UINT_MAX, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s is %s", val, errstr); debug_return_bool(false); - /* XXX - should check against INT_MAX */ - def->sd_un.ival = (unsigned int)l; + } + /* XXX - should have uival */ + def->sd_un.ival = u; } if (def->callback) debug_return_bool(def->callback(val)); @@ -814,7 +819,7 @@ store_mode(char *val, struct sudo_defs_types *def, int op) def->sd_un.mode = (mode_t)0777; } else { l = strtol(val, &endp, 8); - if (*endp != '\0' || l < 0 || l > 0777) + if (endp == val || *endp != '\0' || l < 0 || l > 0777) debug_return_bool(false); def->sd_un.mode = (mode_t)l; } diff --git a/plugins/sudoers/iolog.c b/plugins/sudoers/iolog.c index 133218050..0b4ea23bb 100644 --- a/plugins/sudoers/iolog.c +++ b/plugins/sudoers/iolog.c @@ -136,17 +136,22 @@ io_mkdirs(char *path, mode_t mode, bool is_temp) int io_set_max_sessid(const char *maxval) { - unsigned long ulval; - char *ep; + const char *errstr; + unsigned int value; + debug_decl(io_set_max_sessid, SUDO_DEBUG_UTIL) - errno = 0; - ulval = strtoul(maxval, &ep, 0); - if (*maxval != '\0' && *ep == '\0' && - (errno != ERANGE || ulval != ULONG_MAX)) { - sessid_max = MIN((unsigned int)ulval, SESSID_MAX); - return true; + value = strtonum(maxval, 0, SESSID_MAX, &errstr); + if (errstr != NULL) { + if (errno != ERANGE) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "bad maxseq: %s is %s", maxval, errstr); + debug_return_bool(false); + } + /* Out of range, clamp to SESSID_MAX as documented. */ + value = SESSID_MAX; } - return false; + sessid_max = value; + debug_return_bool(true); } /* @@ -202,7 +207,7 @@ io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7]) nread = read(fd2, buf, sizeof(buf)); if (nread > 0) { id = strtoul(buf, &ep, 36); - if (buf == ep || id >= sessid_max) + if (ep == buf || *ep != '\0' || id >= sessid_max) id = 0; } close(fd2); @@ -217,7 +222,7 @@ io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7]) if (nread == -1) log_fatal(USE_ERRNO, N_("unable to read %s"), pathbuf); id = strtoul(buf, &ep, 36); - if (buf == ep || id >= sessid_max) + if (ep == buf || *ep != '\0' || id >= sessid_max) id = 0; } } @@ -237,7 +242,7 @@ io_nextid(char *iolog_dir, char *iolog_dir_fallback, char sessid[7]) memcpy(sessid, buf, 6); sessid[6] = '\0'; - /* Rewind and overwrite old seq file. */ + /* Rewind and overwrite old seq file, including the NUL byte. */ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7) log_fatal(USE_ERRNO, N_("unable to write to %s"), pathbuf); close(fd); @@ -332,7 +337,9 @@ iolog_deserialize_info(struct iolog_details *details, char * const user_info[], switch (**cur) { case 'c': if (strncmp(*cur, "cols=", sizeof("cols=") - 1) == 0) { - details->cols = atoi(*cur + sizeof("cols=") - 1); + int n = strtonum(*cur + sizeof("cols=") - 1, 1, INT_MAX, NULL); + if (n > 0) + details->cols = n; continue; } if (strncmp(*cur, "cwd=", sizeof("cwd=") - 1) == 0) { @@ -342,7 +349,9 @@ iolog_deserialize_info(struct iolog_details *details, char * const user_info[], break; case 'l': if (strncmp(*cur, "lines=", sizeof("lines=") - 1) == 0) { - details->lines = atoi(*cur + sizeof("lines=") - 1); + int n = strtonum(*cur + sizeof("lines=") - 1, 1, INT_MAX, NULL); + if (n > 0) + details->lines = n; continue; } break; diff --git a/plugins/sudoers/ldap.c b/plugins/sudoers/ldap.c index 4e5e69f70..82ad6c3e9 100644 --- a/plugins/sudoers/ldap.c +++ b/plugins/sudoers/ldap.c @@ -1398,6 +1398,7 @@ sudo_ldap_parse_keyword(const char *keyword, const char *value, struct ldap_config_table *table) { struct ldap_config_table *cur; + const char *errstr; debug_decl(sudo_ldap_parse_keyword, SUDO_DEBUG_LDAP) /* Look up keyword in config tables */ @@ -1418,7 +1419,11 @@ sudo_ldap_parse_keyword(const char *keyword, const char *value, *(int *)(cur->valp) = atobool(value) == true; break; case CONF_INT: - *(int *)(cur->valp) = atoi(value); + *(int *)(cur->valp) = strtonum(value, INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) { + warningx(U_("%s: %s: value %s out of range)"), + path_ldap_conf, keyword, value); + } break; case CONF_STR: efree(*(char **)(cur->valp)); diff --git a/plugins/sudoers/match_addr.c b/plugins/sudoers/match_addr.c index 620394e55..f3995f390 100644 --- a/plugins/sudoers/match_addr.c +++ b/plugins/sudoers/match_addr.c @@ -109,6 +109,7 @@ addr_matches_if_netmask(const char *n, const char *m) unsigned int j; #endif unsigned int family; + const char *errstr; debug_decl(addr_matches_if, SUDO_DEBUG_MATCH) #ifdef HAVE_STRUCT_IN6_ADDR @@ -125,7 +126,12 @@ addr_matches_if_netmask(const char *n, const char *m) if (strchr(m, '.')) { mask.ip4.s_addr = inet_addr(m); } else { - i = atoi(m); + i = strtonum(m, 0, 32, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "IPv4 netmask %s: %s", m, errstr); + debug_return_bool(false); + } if (i == 0) mask.ip4.s_addr = 0; else if (i == 32) @@ -139,7 +145,12 @@ addr_matches_if_netmask(const char *n, const char *m) #ifdef HAVE_STRUCT_IN6_ADDR else { if (inet_pton(AF_INET6, m, &mask.ip6) <= 0) { - j = atoi(m); + j = strtonum(m, 0, 128, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "IPv6 netmask %s: %s", m, errstr); + debug_return_bool(false); + } for (i = 0; i < sizeof(addr.ip6.s6_addr); i++) { if (j < i * 8) mask.ip6.s6_addr[i] = 0; diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index 6c9286ede..0824abac7 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -91,7 +91,6 @@ sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group) const char *debug_flags = NULL; const char *remhost = NULL; int flags = 0; - long lval; char *ep; debug_decl(sudoers_policy_deserialize_info, SUDO_DEBUG_PLUGIN) @@ -119,14 +118,16 @@ sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group) continue; } if (MATCHES(*cur, "sudoers_mode=")) { + long lval; errno = 0; p = *cur + sizeof("sudoers_mode=") - 1; lval = strtol(p, &ep, 8); - if (*p == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), *cur, U_("invalid value")); - if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) - || (lval > 0777 || lval < 0)) - fatalx(U_("%s: %s"), *cur, U_("value out of range")); + if (ep == p || *ep != '\0') + fatalx(U_("%s: %s"), *cur, U_("invalid")); + if (lval < 0) + fatalx(U_("%s: %s"), *cur, U_("too small")); + if (lval > 0777) + fatalx(U_("%s: %s"), *cur, U_("too large")); sudoers_mode = (mode_t) lval; continue; } @@ -147,13 +148,9 @@ sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group) if (MATCHES(*cur, "closefrom=")) { errno = 0; p = *cur + sizeof("closefrom=") - 1; - lval = strtol(p, &ep, 10); - if (*p == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), *cur, U_("invalid value")); - if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) - || (lval > INT_MAX || lval < 3)) - fatalx(U_("%s: %s"), *cur, U_("value out of range")); - user_closefrom = (int) lval; + user_closefrom = strtonum(p, 4, INT_MAX, &errstr); + if (user_closefrom == 0) + fatalx(U_("%s: %s"), *cur, U_(errstr)); continue; } if (MATCHES(*cur, "debug_flags=")) { @@ -265,13 +262,9 @@ sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group) if (MATCHES(*cur, "max_groups=")) { errno = 0; p = *cur + sizeof("max_groups=") - 1; - lval = strtol(p, &ep, 10); - if (*p == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), *cur, U_("invalid value")); - if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) - || (lval > INT_MAX || lval <= 0)) - fatalx(U_("%s: %s"), *cur, U_("value out of range")); - sudo_user.max_groups = (int) lval; + sudo_user.max_groups = strtonum(p, 1, INT_MAX, &errstr); + if (sudo_user.max_groups == 0) + fatalx(U_("%s: %s"), *cur, U_(errstr)); continue; } if (MATCHES(*cur, "remote_host=")) { @@ -322,25 +315,17 @@ sudoers_policy_deserialize_info(void *v, char **runas_user, char **runas_group) if (MATCHES(*cur, "lines=")) { errno = 0; p = *cur + sizeof("lines=") - 1; - lval = strtol(p, &ep, 10); - if (*p == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), *cur, U_("invalid value")); - if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) - || (lval > INT_MAX || lval <= 0)) - fatalx(U_("%s: %s"), *cur, U_("value out of range")); - sudo_user.lines = (int) lval; + sudo_user.lines = strtonum(p, 1, INT_MAX, &errstr); + if (sudo_user.lines == 0) + fatalx(U_("%s: %s"), *cur, U_(errstr)); continue; } if (MATCHES(*cur, "cols=")) { errno = 0; p = *cur + sizeof("cols=") - 1; - lval = strtol(p, &ep, 10); - if (*p == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), *cur, U_("invalid value")); - if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) - || (lval > INT_MAX || lval <= 0)) - fatalx(U_("%s: %s"), *cur, U_("value out of range")); - sudo_user.cols = (int) lval; + sudo_user.cols = strtonum(p, 1, INT_MAX, &errstr); + if (sudo_user.lines == 0) + fatalx(U_("%s: %s"), *cur, U_(errstr)); continue; } if (MATCHES(*cur, "sid=")) { diff --git a/plugins/sudoers/regress/logging/check_wrap.c b/plugins/sudoers/regress/logging/check_wrap.c index 3d5a8195e..056482711 100644 --- a/plugins/sudoers/regress/logging/check_wrap.c +++ b/plugins/sudoers/regress/logging/check_wrap.c @@ -59,6 +59,7 @@ main(int argc, char *argv[]) size_t len; FILE *fp; char *cp, *dash, *line, lines[2][2048]; + int lineno = 0; int which = 0; initprogname(argc > 0 ? argv[0] : "check_wrap"); @@ -83,13 +84,20 @@ main(int argc, char *argv[]) /* If we read the 2nd line, parse list of line lengths and check. */ if (which) { + lineno++; for (cp = strtok(lines[1], ","); cp != NULL; cp = strtok(NULL, ",")) { size_t maxlen; /* May be either a number or a range. */ - len = maxlen = atoi(cp); dash = strchr(cp, '-'); - if (dash) - maxlen = atoi(dash + 1); + if (dash != NULL) { + *dash = '\0'; + len = strtonum(cp, 1, INT_MAX, NULL); + maxlen = strtonum(dash + 1, 1, INT_MAX, NULL); + } else { + len = maxlen = strtonum(cp, 1, INT_MAX, NULL); + } + if (len == 0 || maxlen == 0) + fatalx("%s: invalid length on line %d\n", argv[1], lineno); while (len <= maxlen) { printf("# word wrap at %d characters\n", (int)len); writeln_wrap(stdout, lines[0], strlen(lines[0]), len); diff --git a/plugins/sudoers/regress/parser/check_addr.c b/plugins/sudoers/regress/parser/check_addr.c index 86576a2da..317f79264 100644 --- a/plugins/sudoers/regress/parser/check_addr.c +++ b/plugins/sudoers/regress/parser/check_addr.c @@ -54,6 +54,7 @@ static int check_addr(char *input) { int expected, matched; + const char *errstr; size_t len; char *cp; @@ -65,7 +66,9 @@ check_addr(char *input) cp = input + len; while (isspace((unsigned char)*cp)) cp++; - expected = atoi(cp); + expected = strtonum(cp, 0, 1, &errstr); + if (errstr != NULL) + fatalx("expecting 0 or 1, got %s", cp); input[len] = '\0'; matched = addr_matches(input); diff --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c index ce752dc2d..c3537cfd1 100644 --- a/plugins/sudoers/sudoreplay.c +++ b/plugins/sudoers/sudoreplay.c @@ -438,6 +438,7 @@ replay_session(const double max_wait, const char *decimal) bool need_nlcr = false; char last_char = '\0'; + buf[strcspn(buf, "\n")] = '\0'; if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes)) fatalx(U_("invalid timing file line: %s"), buf); @@ -802,13 +803,9 @@ parse_logfile(char *logfile) { FILE *fp; char *buf = NULL, *cp, *ep; + const char *errstr; size_t bufsize = 0, cwdsize = 0, cmdsize = 0; struct log_info *li = NULL; -#ifdef HAVE_STRTOLL - long long llval; -#else - long lval; -#endif debug_decl(parse_logfile, SUDO_DEBUG_UTIL) fp = fopen(logfile, "r"); @@ -837,26 +834,21 @@ parse_logfile(char *logfile) /* * Crack the log line (rows and cols not present in old versions). * timestamp:user:runas_user:runas_group:tty:rows:cols + * XXX - probably better to use strtok and switch on the state. */ buf[strcspn(buf, "\n")] = '\0'; + cp = buf; /* timestamp */ - errno = 0; -#ifdef HAVE_STRTOLL - llval = strtoll(buf, &ep, 10); - if (buf[0] == '\0' || *ep != ':') + if ((ep = strchr(cp, ':')) == NULL) goto bad; - if (errno == ERANGE && (llval == LLONG_MAX || llval == LLONG_MIN)) + *ep = '\0'; + li->tstamp = strtonum(cp, LLONG_MIN, LLONG_MAX, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s: timestamp %s is %s", logfile, cp, errstr); goto bad; - li->tstamp = (time_t)llval; -#else - lval = strtol(buf, &ep, 10); - if (buf[0] == '\0' || *ep != ':') - goto bad; - if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) - goto bad; - li->tstamp = (time_t)lval; -#endif /* HAVE_STRTOLL */ + } /* user */ cp = ep + 1; @@ -880,14 +872,28 @@ parse_logfile(char *logfile) /* tty, followed by optional rows + columns */ cp = ep + 1; if ((ep = strchr(cp, ':')) == NULL) { + /* just the tty */ li->tty = estrdup(cp); } else { + /* tty followed by rows + columns */ li->tty = estrndup(cp, (size_t)(ep - cp)); cp = ep + 1; - li->rows = atoi(cp); + /* need to NULL out separator to use strtonum() */ if ((ep = strchr(cp, ':')) != NULL) { + *ep = '\0'; + } + li->rows = strtonum(cp, 1, INT_MAX, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s: tty rows %s is %s", logfile, cp, errstr); + } + if (ep != NULL) { cp = ep + 1; - li->cols = atoi(cp); + li->cols = strtonum(cp, 1, INT_MAX, &errstr); + if (errstr != NULL) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s: tty cols %s is %s", logfile, cp, errstr); + } } } fclose(fp); @@ -1158,6 +1164,8 @@ parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, /* Parse index */ ul = strtoul(buf, &ep, 10); + if (ep == buf || !isspace((unsigned char) *ep)) + goto bad; if (ul >= IOFD_TIMING) { if (ul != 6) goto bad; @@ -1176,11 +1184,10 @@ parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, */ errno = 0; l = strtol(cp, &ep, 10); - if ((errno == ERANGE && (l == LONG_MAX || l == LONG_MIN)) || - l < 0 || l > INT_MAX || - (*ep != '.' && strncmp(ep, decimal, strlen(decimal)) != 0)) { + if (ep == cp || (*ep != '.' && strncmp(ep, decimal, strlen(decimal)) != 0)) + goto bad; + if (l < 0 || l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) goto bad; - } *seconds = (double)l; cp = ep + (*ep == '.' ? 1 : strlen(decimal)); d = 10.0; @@ -1195,7 +1202,7 @@ parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, errno = 0; ul = strtoul(cp, &ep, 10); - if (errno == ERANGE && ul == ULONG_MAX) + if (ep == cp || *ep != '\0' || (errno == ERANGE && ul == ULONG_MAX)) goto bad; *nbytes = (size_t)ul; diff --git a/plugins/system_group/system_group.c b/plugins/system_group/system_group.c index f53374ccc..c0cd563b0 100644 --- a/plugins/system_group/system_group.c +++ b/plugins/system_group/system_group.c @@ -73,6 +73,8 @@ static sysgroup_getgrgid_t sysgroup_getgrgid; static sysgroup_gr_delref_t sysgroup_gr_delref; static bool need_setent; +extern id_t atoid(const char *str, const char *sep, char **endp, const char **errstr); + static int sysgroup_init(int version, sudo_printf_t sudo_printf, char *const argv[]) { @@ -128,16 +130,15 @@ sysgroup_cleanup(void) static int sysgroup_query(const char *user, const char *group, const struct passwd *pwd) { - char **member, *ep = '\0'; + char **member; struct group *grp; grp = sysgroup_getgrnam(group); if (grp == NULL && group[0] == '#' && group[1] != '\0') { - long lval = strtol(group + 1, &ep, 10); - if (*ep == '\0') { - if ((lval != LONG_MAX && lval != LONG_MIN) || errno != ERANGE) - grp = sysgroup_getgrgid((gid_t)lval); - } + const char *errstr; + gid_t gid = atoid(group + 1, NULL, NULL, &errstr); + if (errstr == NULL) + grp = sysgroup_getgrgid(gid); } if (grp != NULL) { for (member = grp->gr_mem; *member != NULL; member++) { diff --git a/src/parse_args.c b/src/parse_args.c index 61d4647d9..99fb249f2 100644 --- a/src/parse_args.c +++ b/src/parse_args.c @@ -178,7 +178,7 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp, char *cp, **env_add, **settings; const char *runas_user = NULL; const char *runas_group = NULL; - const char *debug_flags; + const char *debug_flags, *errstr; int nenv = 0; int env_size = 32; debug_decl(parse_args, SUDO_DEBUG_ARGS) @@ -242,8 +242,9 @@ parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp, SET(flags, MODE_BACKGROUND); break; case 'C': - if (atoi(optarg) < 3) { - warningx(U_("the argument to -C must be a number greater than or equal to 3")); + strtonum(optarg, 4, INT_MAX, &errstr); + if (errstr != NULL) { + warningx(U_("the argument to -C was %s"), U_(errstr)); usage(1); } sudo_settings[ARG_CLOSEFROM].value = optarg; diff --git a/src/sudo.c b/src/sudo.c index 075b414a3..f8c3fab53 100644 --- a/src/sudo.c +++ b/src/sudo.c @@ -530,8 +530,7 @@ command_info_to_details(char * const info[], struct command_details *details) { int i; id_t id; - long lval; - char *cp, *ep; + char *cp; const char *errstr; debug_decl(command_info_to_details, SUDO_DEBUG_PCOMM) @@ -553,16 +552,10 @@ command_info_to_details(char * const info[], struct command_details *details) SET_STRING("command=", command) SET_STRING("cwd=", cwd) if (strncmp("closefrom=", info[i], sizeof("closefrom=") - 1) == 0) { - errno = 0; cp = info[i] + sizeof("closefrom=") - 1; - lval = strtol(cp, &ep, 10); - if (*cp == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), info[i], U_("invalid value")); - if ((errno == ERANGE && - (lval == LONG_MAX || lval == LONG_MIN)) || - (lval > INT_MAX || lval < 0)) - fatalx(U_("%s: %s"), info[i], U_("value out of range")); - details->closefrom = (int)lval; + details->closefrom = strtonum(cp, 0, INT_MAX, &errstr); + if (errstr != NULL) + fatalx(U_("%s: %s"), cp, U_(errstr)); break; } break; @@ -578,16 +571,10 @@ command_info_to_details(char * const info[], struct command_details *details) break; case 'n': if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) { - errno = 0; cp = info[i] + sizeof("nice=") - 1; - lval = strtol(cp, &ep, 10); - if (*cp == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), info[i], U_("invalid value")); - if ((errno == ERANGE && - (lval == LONG_MAX || lval == LONG_MIN)) || - (lval > INT_MAX || lval < INT_MIN)) - fatalx(U_("%s: %s"), info[i], U_("value out of range")); - details->priority = (int)lval; + details->priority = strtonum(cp, INT_MIN, INT_MAX, &errstr); + if (errstr != NULL) + fatalx(U_("%s: %s"), cp, U_(errstr)); SET(details->flags, CD_SET_PRIORITY); break; } @@ -686,31 +673,27 @@ command_info_to_details(char * const info[], struct command_details *details) break; case 't': if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) { - errno = 0; cp = info[i] + sizeof("timeout=") - 1; - lval = strtol(cp, &ep, 10); - if (*cp == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), info[i], U_("invalid value")); - if ((errno == ERANGE && - (lval == LONG_MAX || lval == LONG_MIN)) || - (lval > INT_MAX || lval < 0)) - fatalx(U_("%s: %s"), info[i], U_("value out of range")); - details->timeout = (int)lval; + details->timeout = strtonum(cp, 0, INT_MAX, &errstr); + if (errstr != NULL) + fatalx(U_("%s: %s"), cp, U_(errstr)); SET(details->flags, CD_SET_TIMEOUT); break; } break; case 'u': if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) { + long lval; + char *ep; errno = 0; cp = info[i] + sizeof("umask=") - 1; lval = strtol(cp, &ep, 8); - if (*cp == '\0' || *ep != '\0') - fatalx(U_("%s: %s"), info[i], U_("invalid value")); - if ((errno == ERANGE && - (lval == LONG_MAX || lval == LONG_MIN)) || - (lval > 0777 || lval < 0)) - fatalx(U_("%s: %s"), info[i], U_("value out of range")); + if (ep == cp || *ep != '\0') + fatalx(U_("%s: %s"), info[i], U_("invalid")); + if (lval < 0) + fatalx(U_("%s: %s"), info[i], U_("too small")); + if (lval > 0777) + fatalx(U_("%s: %s"), info[i], U_("too large")); details->umask = (mode_t)lval; SET(details->flags, CD_SET_UMASK); break; diff --git a/src/ttyname.c b/src/ttyname.c index 6551b7cc5..b9532e15d 100644 --- a/src/ttyname.c +++ b/src/ttyname.c @@ -441,11 +441,16 @@ get_process_ttyname(void) if (len != -1) { /* Field 7 is the tty dev (0 if no tty) */ char *cp = line; + const char *errstr; int field = 1; while (*cp != '\0') { if (*cp++ == ' ') { if (++field == 7) { - dev_t tdev = (dev_t)atoi(cp); + dev_t tdev = strtonum(cp, INT_MIN, INT_MAX, &errstr); + if (errstr) { + sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, + "%s: tty device number %s", path, errstr); + } if (tdev > 0) tty = sudo_ttyname_dev(tdev); break;