From: Neil Horman Date: Fri, 12 Jul 2024 14:46:23 +0000 (-0400) Subject: Add an OPENSSL_strtoul wrapper X-Git-Tag: openssl-3.4.0-alpha1~315 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=04f7729c409afad235737ee6b4edcb78efdc1bfd;p=thirdparty%2Fopenssl.git Add an OPENSSL_strtoul wrapper utility function to give us sane checking on strtoul conversions Reviewed-by: Tom Cosgrove Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/24861) --- diff --git a/crypto/o_str.c b/crypto/o_str.c index dfac215ac35..ba41d76fc17 100644 --- a/crypto/o_str.c +++ b/crypto/o_str.c @@ -90,6 +90,74 @@ size_t OPENSSL_strlcat(char *dst, const char *src, size_t size) return l + OPENSSL_strlcpy(dst, src, size); } +/** + * @brief Converts a string to an unsigned long integer. + * + * This function attempts to convert a string representation of a number + * to an unsigned long integer, given a specified base. It also provides + * error checking and reports whether the conversion was successful. + * This function is just a wrapper around the POSIX strtoul function with + * additional error checking. This implies that errno for the caller is set + * on calls to this function. + * + * @param str The string containing the representation of the number. + * @param endptr A pointer to a pointer to character. If not NULL, it is set + * to the character immediately following the number in the + * string. + * @param base The base to use for the conversion, which must be between 2, + * and 36 inclusive, or be the special value 0. If the base is 0, + * the actual base is determined by the format of the initial + * characters of the string. + * @param num A pointer to an unsigned long where the result of the + * conversion is stored. + * + * @return 1 if the conversion was successful, 0 otherwise. Conversion is + * considered unsuccessful if no digits were consumed or if an error + * occurred during conversion. + * + * @note It is the caller's responsibility to check if the conversion is + * correct based on the expected consumption of the string as reported + * by endptr. + */ +int OPENSSL_strtoul(const char *str, char **endptr, int base, + unsigned long *num) +{ + char *tmp_endptr; + char **internal_endptr = endptr == NULL ? &tmp_endptr : endptr; + + errno = 0; + + *internal_endptr = (char *)str; + + if (num == NULL) + return 0; + + if (str == NULL) + return 0; + + /* Fail on negative input */ + if (*str == '-') + return 0; + + *num = strtoul(str, internal_endptr, base); + /* + * We return error from this function under the following conditions + * 1) If strtoul itself returned an error in translation + * 2) If the caller didn't pass in an endptr value, and **internal_endptr + * doesn't point to '\0'. The implication here is that if the caller + * doesn't care how much of a string is consumed, they expect the entire + * string to be consumed. As such, no pointing to the NULL terminator + * means there was some part of the string left over after translation + * 3) If no bytes of the string were consumed + */ + if (errno != 0 || + (endptr == NULL && **internal_endptr != '\0') || + (str == *internal_endptr)) + return 0; + + return 1; +} + int OPENSSL_hexchar2int(unsigned char c) { #ifdef CHARSET_EBCDIC diff --git a/doc/man3/OPENSSL_malloc.pod b/doc/man3/OPENSSL_malloc.pod index 3c4099bc859..6d9251ec3b2 100644 --- a/doc/man3/OPENSSL_malloc.pod +++ b/doc/man3/OPENSSL_malloc.pod @@ -7,7 +7,7 @@ OPENSSL_malloc, OPENSSL_aligned_alloc, OPENSSL_zalloc, OPENSSL_realloc, OPENSSL_free, OPENSSL_clear_realloc, OPENSSL_clear_free, OPENSSL_cleanse, CRYPTO_malloc, CRYPTO_aligned_alloc, CRYPTO_zalloc, CRYPTO_realloc, CRYPTO_free, OPENSSL_strdup, OPENSSL_strndup, -OPENSSL_memdup, OPENSSL_strlcpy, OPENSSL_strlcat, +OPENSSL_memdup, OPENSSL_strlcpy, OPENSSL_strlcat, OPENSSL_strtoul, CRYPTO_strdup, CRYPTO_strndup, OPENSSL_mem_debug_push, OPENSSL_mem_debug_pop, CRYPTO_mem_debug_push, CRYPTO_mem_debug_pop, @@ -36,6 +36,7 @@ OPENSSL_MALLOC_FD char *OPENSSL_strndup(const char *str, size_t s); size_t OPENSSL_strlcat(char *dst, const char *src, size_t size); size_t OPENSSL_strlcpy(char *dst, const char *src, size_t size); + int OPENSSL_strtoul(char *src, char **endptr, int base, unsigned long *num); void *OPENSSL_memdup(void *data, size_t s); void *OPENSSL_clear_realloc(void *p, size_t old_len, size_t num); void OPENSSL_clear_free(void *str, size_t num); @@ -135,6 +136,12 @@ OPENSSL_strlcpy(), OPENSSL_strlcat() and OPENSSL_strnlen() are equivalents of the common C library functions and are provided for portability. +OPENSSL_strtoul() is a wrapper around the POSIX function strtoul, with the same +behaviors listed in the POSIX documentation, with the additional behavior that +it validates the input I and I parameters for not being NULL, and confirms +that at least a single byte of input has been consumed in the translation, +returning an error in the event that no bytes were consumed. + If no allocations have been done, it is possible to "swap out" the default implementations for OPENSSL_malloc(), OPENSSL_realloc() and OPENSSL_free() and replace them with alternate versions. @@ -203,6 +210,35 @@ OPENSSL_mem_debug_push(), OPENSSL_mem_debug_pop(), CRYPTO_mem_debug_push(), and CRYPTO_mem_debug_pop() are deprecated and are no-ops that always return 0. +OPENSSL_strtoul() returns 1 on success and 0 in the event that an error has +occured. Specifically, 0 is returned in the following events: + +=over 4 + +=item * + +If the underlying call to strtoul returned a non zero errno value + +=item * + +If the translation did not consume the entire input string, and the passed +endptr value was NULL + +=item * + +If no characters were consumed in the translation + +=back + +Note that a success condition does not imply that the expected +translation has been preformed. For instance calling + + OPENSSL_strtoul("0x12345", &endptr, 10, &num); + +will result in a successful translation with num having the value 0, and +*endptr = 'x'. Be sure to validate how much data was consumed when calling this +function. + =head1 HISTORY OPENSSL_mem_debug_push(), OPENSSL_mem_debug_pop(), diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in index 99041cce065..11a9ec9a24f 100644 --- a/include/openssl/crypto.h.in +++ b/include/openssl/crypto.h.in @@ -134,6 +134,7 @@ int CRYPTO_atomic_store(uint64_t *dst, uint64_t val, CRYPTO_RWLOCK *lock); size_t OPENSSL_strlcpy(char *dst, const char *src, size_t siz); size_t OPENSSL_strlcat(char *dst, const char *src, size_t siz); size_t OPENSSL_strnlen(const char *str, size_t maxlen); +int OPENSSL_strtoul(const char *str, char **endptr, int base, unsigned long *num); int OPENSSL_buf2hexstr_ex(char *str, size_t str_n, size_t *strlength, const unsigned char *buf, size_t buflen, const char sep); diff --git a/util/libcrypto.num b/util/libcrypto.num index e434b95d460..295f96bded4 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5702,3 +5702,4 @@ OSSL_USER_NOTICE_SYNTAX_new ? 3_4_0 EXIST::FUNCTION: OSSL_USER_NOTICE_SYNTAX_it ? 3_4_0 EXIST::FUNCTION: OSSL_INDICATOR_set_callback ? 3_4_0 EXIST::FUNCTION: OSSL_INDICATOR_get_callback ? 3_4_0 EXIST::FUNCTION: +OPENSSL_strtoul ? 3_4_0 EXIST::FUNCTION: