diff --git a/crypto/ctype.c b/crypto/ctype.c index 83c24a546f53b..b8419795f4797 100644 --- a/crypto/ctype.c +++ b/crypto/ctype.c @@ -12,6 +12,18 @@ #include "crypto/ctype.h" #include +#include "internal/e_os.h" +#include "internal/core.h" + +#ifndef OPENSSL_SYS_WINDOWS +#include +#endif +#include + +#ifdef OPENSSL_SYS_MACOSX +#include +#endif + /* * Define the character classes for each character in the seven bit ASCII * character set. This is independent of the host's character set, characters @@ -278,3 +290,69 @@ int ossl_ascii_isdigit(const char inchar) { return 1; return 0; } + +/* str[n]casecmp_l is defined in POSIX 2008-01. Value is taken accordingly + * https://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html */ + +#if (defined OPENSSL_SYS_WINDOWS) || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) + +# if defined OPENSSL_SYS_WINDOWS +# define locale_t _locale_t +# define freelocale _free_locale +# define strcasecmp_l _stricmp_l +# define strncasecmp_l _strnicmp_l +# endif +static locale_t loc; + +# ifndef FIPS_MODULE +void *ossl_c_locale() { + return (void *)loc; +} +# endif + +int ossl_init_casecmp_int() { +# ifdef OPENSSL_SYS_WINDOWS + loc = _create_locale(LC_COLLATE, "C"); +# else + loc = newlocale(LC_COLLATE_MASK, "C", (locale_t) 0); +# endif + return (loc == (locale_t) 0) ? 0 : 1; +} + +void ossl_deinit_casecmp() { + freelocale(loc); +} + +int OPENSSL_strcasecmp(const char *s1, const char *s2) +{ + return strcasecmp_l(s1, s2, (locale_t)ossl_c_locale()); +} + +int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t n) +{ + return strncasecmp_l(s1, s2, n, (locale_t)ossl_c_locale()); +} +#else +# ifndef FIPS_MODULE +void *ossl_c_locale() { + return NULL; +} +# endif + +int ossl_init_casecmp_int() { + return 1; +} + +void ossl_deinit_casecmp() { +} + +int OPENSSL_strcasecmp(const char *s1, const char *s2) +{ + return strcasecmp(s1, s2); +} + +int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t n) +{ + return strncasecmp(s1, s2, n); +} +#endif diff --git a/crypto/init.c b/crypto/init.c index b7d7ad0ea35d7..d859bd42c62d7 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -32,6 +32,7 @@ #include "crypto/store.h" #include /* for OSSL_CMP_log_close() */ #include +#include "crypto/ctype.h" static int stopped = 0; static uint64_t optsdone = 0; @@ -270,6 +271,15 @@ DEFINE_RUN_ONCE_STATIC(ossl_init_async) return 1; } +static CRYPTO_ONCE casecmp = CRYPTO_ONCE_STATIC_INIT; +static int casecmp_inited = 0; +DEFINE_RUN_ONCE_STATIC(ossl_init_casecmp) +{ + int ret = ossl_init_casecmp_int(); + + casecmp_inited = 1; + return ret; +} #ifndef OPENSSL_NO_ENGINE static CRYPTO_ONCE engine_openssl = CRYPTO_ONCE_STATIC_INIT; DEFINE_RUN_ONCE_STATIC(ossl_init_engine_openssl) @@ -387,6 +397,11 @@ void OPENSSL_cleanup(void) async_deinit(); } + if (casecmp_inited) { + OSSL_TRACE(INIT, "OPENSSL_cleanup: ossl_deinit_casecmp()\n"); + ossl_deinit_casecmp(); + } + if (load_crypto_strings_inited) { OSSL_TRACE(INIT, "OPENSSL_cleanup: err_free_strings_int()\n"); err_free_strings_int(); @@ -459,6 +474,9 @@ int OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) { uint64_t tmp; int aloaddone = 0; + if (!RUN_ONCE(&casecmp, ossl_init_casecmp)) + return 0; + /* Applications depend on 0 being returned when cleanup was already done */ if (stopped) { diff --git a/doc/build.info b/doc/build.info index 46dabeb9a63ce..ea159c6d43072 100644 --- a/doc/build.info +++ b/doc/build.info @@ -1531,6 +1531,10 @@ DEPEND[html/man3/OPENSSL_secure_malloc.html]=man3/OPENSSL_secure_malloc.pod GENERATE[html/man3/OPENSSL_secure_malloc.html]=man3/OPENSSL_secure_malloc.pod DEPEND[man/man3/OPENSSL_secure_malloc.3]=man3/OPENSSL_secure_malloc.pod GENERATE[man/man3/OPENSSL_secure_malloc.3]=man3/OPENSSL_secure_malloc.pod +DEPEND[html/man3/OPENSSL_strcasecmp.html]=man3/OPENSSL_strcasecmp.pod +GENERATE[html/man3/OPENSSL_strcasecmp.html]=man3/OPENSSL_strcasecmp.pod +DEPEND[man/man3/OPENSSL_strcasecmp.3]=man3/OPENSSL_strcasecmp.pod +GENERATE[man/man3/OPENSSL_strcasecmp.3]=man3/OPENSSL_strcasecmp.pod DEPEND[html/man3/OSSL_CMP_CTX_new.html]=man3/OSSL_CMP_CTX_new.pod GENERATE[html/man3/OSSL_CMP_CTX_new.html]=man3/OSSL_CMP_CTX_new.pod DEPEND[man/man3/OSSL_CMP_CTX_new.3]=man3/OSSL_CMP_CTX_new.pod @@ -3110,6 +3114,7 @@ html/man3/OPENSSL_load_builtin_modules.html \ html/man3/OPENSSL_malloc.html \ html/man3/OPENSSL_s390xcap.html \ html/man3/OPENSSL_secure_malloc.html \ +html/man3/OPENSSL_strcasecmp.html \ html/man3/OSSL_CMP_CTX_new.html \ html/man3/OSSL_CMP_HDR_get0_transactionID.html \ html/man3/OSSL_CMP_ITAV_set0.html \ @@ -3704,6 +3709,7 @@ man/man3/OPENSSL_load_builtin_modules.3 \ man/man3/OPENSSL_malloc.3 \ man/man3/OPENSSL_s390xcap.3 \ man/man3/OPENSSL_secure_malloc.3 \ +man/man3/OPENSSL_strcasecmp.3 \ man/man3/OSSL_CMP_CTX_new.3 \ man/man3/OSSL_CMP_HDR_get0_transactionID.3 \ man/man3/OSSL_CMP_ITAV_set0.3 \ diff --git a/doc/man3/OPENSSL_strcasecmp.pod b/doc/man3/OPENSSL_strcasecmp.pod new file mode 100644 index 0000000000000..1bb8b18c50136 --- /dev/null +++ b/doc/man3/OPENSSL_strcasecmp.pod @@ -0,0 +1,47 @@ +=pod + +=head1 NAME + +OPENSSL_strcasecmp, OPENSSL_strncasecmp - compare two strings ignoring case + +=head1 SYNOPSIS + + #include + + int OPENSSL_strcasecmp(const char *s1, const char *s2); + int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t n); + +=head1 DESCRIPTION + +The OPENSSL_strcasecmp function performs a byte-by-byte comparison of the strings +B and B, ignoring the case of the characters. + +The OPENSSL_strncasecmp function is similar, except that it compares no more than +B bytes of B and B. + +In POSIX-compatible system and on Windows these functions use "C" locale for +case insensitive. Otherwise the comparison is done in current locale. + +=head1 RETURN VALUES + +Both functions return an integer less than, equal to, or greater than zero if +s1 is found, respectively, to be less than, to match, or be greater than s2. + +=head1 NOTES + +OpenSSL extensively uses case insensitive comparison of ASCII strings. Though +OpenSSL itself is locale-agnostic, the applications using OpenSSL libraries may +unpredictably suffer when they use localization (e.g. Turkish locale is +well-known with a specific I/i cases). These functions use C locale for string +comparison. + +=head1 COPYRIGHT + +Copyright 2022 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/include/crypto/ctype.h b/include/crypto/ctype.h index a35c137e84315..831e53882df21 100644 --- a/include/crypto/ctype.h +++ b/include/crypto/ctype.h @@ -80,4 +80,6 @@ int ossl_ascii_isdigit(const char inchar); # define ossl_isbase64(c) (ossl_ctype_check((c), CTYPE_MASK_base64)) # define ossl_isasn1print(c) (ossl_ctype_check((c), CTYPE_MASK_asn1print)) +int ossl_init_casecmp_int(void); +void ossl_deinit_casecmp(void); #endif diff --git a/include/internal/core.h b/include/internal/core.h index d9dc424164c93..b63af84787af6 100644 --- a/include/internal/core.h +++ b/include/internal/core.h @@ -63,4 +63,6 @@ __owur int ossl_lib_ctx_read_lock(OSSL_LIB_CTX *ctx); int ossl_lib_ctx_unlock(OSSL_LIB_CTX *ctx); int ossl_lib_ctx_is_child(OSSL_LIB_CTX *ctx); +void *ossl_c_locale(void); + #endif diff --git a/include/internal/e_os.h b/include/internal/e_os.h index e1608ae55d7de..5490a48fcd489 100644 --- a/include/internal/e_os.h +++ b/include/internal/e_os.h @@ -249,8 +249,6 @@ FILE *__iob_func(); /***********************************************/ # if defined(OPENSSL_SYS_WINDOWS) -# define strcasecmp _stricmp -# define strncasecmp _strnicmp # if (_MSC_VER >= 1310) && !defined(_WIN32_WCE) # define open _open # define fdopen _fdopen diff --git a/include/openssl/crypto.h.in b/include/openssl/crypto.h.in index df7abb2a29a98..78e47b4ac192c 100644 --- a/include/openssl/crypto.h.in +++ b/include/openssl/crypto.h.in @@ -133,6 +133,8 @@ int OPENSSL_hexstr2buf_ex(unsigned char *buf, size_t buf_n, size_t *buflen, const char *str, const char sep); unsigned char *OPENSSL_hexstr2buf(const char *str, long *buflen); int OPENSSL_hexchar2int(unsigned char c); +int OPENSSL_strcasecmp(const char *s1, const char *s2); +int OPENSSL_strncasecmp(const char *s1, const char *s2, size_t n); # define OPENSSL_MALLOC_MAX_NELEMS(type) (((1U<<(sizeof(int)*8-1))-1)/sizeof(type)) diff --git a/util/libcrypto.num b/util/libcrypto.num index 98cc061f30ba1..f2426bc8cd1d8 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5438,3 +5438,5 @@ ASYNC_set_mem_functions ? 3_1_0 EXIST::FUNCTION: ASYNC_get_mem_functions ? 3_1_0 EXIST::FUNCTION: BIO_ADDR_dup ? 3_1_0 EXIST::FUNCTION:SOCK CMS_final_digest ? 3_1_0 EXIST::FUNCTION:CMS +OPENSSL_strcasecmp ? 3_0_3 EXIST::FUNCTION: +OPENSSL_strncasecmp ? 3_0_3 EXIST::FUNCTION: