diff --git a/src/ck.c b/src/ck.c index 5bdc4b01..3dc87d0c 100644 --- a/src/ck.c +++ b/src/ck.c @@ -254,3 +254,46 @@ ck_strerror_s (char * const s, const rsize_t maxsize, const errno_t errnum) #endif } + + +int +ck_memeq_const_time (const void *a, const size_t alen, const void *b, const size_t blen) +{ + /* constant time memory compare for equality */ + /* rounds to next multiple of 64 to avoid potentially leaking exact + * string lengths when subject to high precision timing attacks + */ + /* Note: some libs provide similar funcs but might not obscure length, e.g. + * OpenSSL: + * int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len) + * Note: some OS provide similar funcs but might not obscure length, e.g. + * OpenBSD: int timingsafe_bcmp(const void *b1, const void *b2, size_t len) + * NetBSD: int consttime_memequal(void *b1, void *b2, size_t len) + */ + const volatile unsigned char * const av = (const unsigned char *)a; + const volatile unsigned char * const bv = (const unsigned char *)b; + size_t lim = ((alen >= blen ? alen : blen) + 0x3F) & ~0x3F; + int diff = (alen != blen); /*(never match if string length mismatch)*/ + for (size_t i = 0, j = 0; lim; --lim) { + diff |= (av[i] ^ bv[j]); + i += (i < alen); + j += (j < blen); + } + return (0 == diff); +} + + +int +ck_memeq_const_time_fixed_len (const void *a, const void *b, const size_t len) +{ + /* constant time memory compare for equality for fixed len (e.g. digests) + * (padding not necessary for digests, which have fixed, defined lengths) */ + /* caller should prefer ck_memeq_const_time() if not operating on digests */ + const volatile unsigned char * const av = (const unsigned char *)a; + const volatile unsigned char * const bv = (const unsigned char *)b; + int diff = 0; + for (size_t i = 0; i < len; ++i) { + diff |= (av[i] ^ bv[i]); + } + return (0 == diff); +} diff --git a/src/ck.h b/src/ck.h index 7f41db8a..e28e3c5e 100644 --- a/src/ck.h +++ b/src/ck.h @@ -42,6 +42,16 @@ static inline errno_t ck_memzero(void *s, rsize_t n) { errno_t ck_strerror_s (char *s, rsize_t maxsize, errno_t errnum); +/*(ck_memeq_const_time() is not from C11 Annex K) + * constant time memory compare for equality + * rounds to next multiple of 64 to avoid potentially leaking exact + * string lengths when subject to high precision timing attacks */ +int ck_memeq_const_time (const void *a, size_t alen, const void *b, size_t blen); + +/*(ck_memeq_const_time_fixed_len() is not from C11 Annex K) + * constant time memory compare for equality for fixed len (e.g. digests) + * (padding not necessary for digests, which have fixed, defined lengths) */ +int ck_memeq_const_time_fixed_len (const void *a, const void *b, size_t len); __END_DECLS diff --git a/src/http_auth.c b/src/http_auth.c index c0ccc9ce..c16e45f5 100644 --- a/src/http_auth.c +++ b/src/http_auth.c @@ -58,57 +58,6 @@ void http_auth_backend_set (const http_auth_backend_t *backend) } -int http_auth_const_time_memeq (const void *a, const void *b, const size_t len) -{ - /* constant time memory compare, unless compiler figures it out - * (similar to mod_secdownload.c:const_time_memeq()) */ - /* caller should prefer http_auth_const_time_memeq_pad() - * if not operating on digests, which have defined lengths */ - /* Note: some libs provide similar funcs, e.g. - * OpenSSL: - * int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len) - * Note: some OS provide similar funcs, e.g. - * OpenBSD: int timingsafe_bcmp(const void *b1, const void *b2, size_t len) - * NetBSD: int consttime_memequal(void *b1, void *b2, size_t len) - */ - const volatile unsigned char * const av = (const unsigned char *)a; - const volatile unsigned char * const bv = (const unsigned char *)b; - int diff = 0; - for (size_t i = 0; i < len; ++i) { - diff |= (av[i] ^ bv[i]); - } - return (0 == diff); -} - - -int http_auth_const_time_memeq_pad (const void *a, const size_t alen, const void *b, const size_t blen) -{ - /* constant time memory compare, unless compiler figures it out - * (similar to mod_secdownload.c:const_time_memeq()) */ - /* round to next multiple of 64 to avoid potentially leaking exact - * password length when subject to high precision timing attacks) - * (not necessary when comparing digests, which have defined lengths) - */ - /* Note: some libs provide similar funcs but might not obscure length, e.g. - * OpenSSL: - * int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len) - * Note: some OS provide similar funcs but might not obscure length, e.g. - * OpenBSD: int timingsafe_bcmp(const void *b1, const void *b2, size_t len) - * NetBSD: int consttime_memequal(void *b1, void *b2, size_t len) - */ - const volatile unsigned char * const av = (const unsigned char *)a; - const volatile unsigned char * const bv = (const unsigned char *)b; - size_t lim = ((alen >= blen ? alen : blen) + 0x3F) & ~0x3F; - int diff = (alen != blen); /*(never match if string length mismatch)*/ - for (size_t i = 0, j = 0; lim; --lim) { - diff |= (av[i] ^ bv[j]); - i += (i < alen); - j += (j < blen); - } - return (0 == diff); -} - - void http_auth_dumbdata_reset (void) { memset(http_auth_schemes, 0, sizeof(http_auth_schemes)); diff --git a/src/http_auth.h b/src/http_auth.h index 13b82a18..4add202f 100644 --- a/src/http_auth.h +++ b/src/http_auth.h @@ -88,12 +88,6 @@ const http_auth_backend_t * http_auth_backend_get (const buffer *name); __attribute_cold__ void http_auth_backend_set (const http_auth_backend_t *backend); -__attribute_pure__ -int http_auth_const_time_memeq (const void *a, const void *b, size_t len); - -__attribute_pure__ -int http_auth_const_time_memeq_pad (const void *a, size_t alen, const void *b, size_t blen); - void http_auth_setenv(request_st *r, const char *username, size_t ulen, const char *auth_type, size_t alen); int http_auth_digest_hex2bin (const char *hexstr, size_t len, unsigned char *bin, size_t binlen); diff --git a/src/mod_auth.c b/src/mod_auth.c index dfadf481..bd11dad1 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -6,6 +6,7 @@ #include "sys-crypto-md.h" /* USE_LIB_CRYPTO */ #include "base.h" +#include "ck.h" #include "plugin.h" #include "plugin_config.h" #include "http_auth.h" @@ -795,8 +796,7 @@ static handler_t mod_auth_check_basic(request_st * const r, void *p_d, const str ae = http_auth_cache_query(sptree, ndx); if (ae && ae->require == require && buffer_is_equal_string(username, ae->username, ae->ulen)) - rc = http_auth_const_time_memeq_pad(ae->pwdigest, ae->dlen, - pw, pwlen) + rc = ck_memeq_const_time(ae->pwdigest, ae->dlen, pw, pwlen) ? HANDLER_GO_ON : HANDLER_ERROR; else /*(not found or hash collision)*/ @@ -1473,7 +1473,7 @@ static handler_t mod_auth_check_digest(request_st * const r, void *p_d, const st mod_auth_digest_mutate(&ai,m,uri,nonce,cnonce,nc,qop); - if (!http_auth_const_time_memeq(rdigest, ai.digest, ai.dlen)) { + if (!ck_memeq_const_time_fixed_len(rdigest, ai.digest, ai.dlen)) { /*safe_memclear(ai.digest, ai.dlen);*//* skip clear since mutated */ /* digest not ok */ log_error(r->conf.errh, __FILE__, __LINE__, diff --git a/src/mod_authn_dbi.c b/src/mod_authn_dbi.c index ccd6c4ee..3081d789 100644 --- a/src/mod_authn_dbi.c +++ b/src/mod_authn_dbi.c @@ -41,6 +41,7 @@ #include "sys-crypto-md.h" #include "safe_memclear.h" #include "base.h" +#include "ck.h" #include "http_auth.h" #include "fdevent.h" #include "log.h" @@ -403,7 +404,7 @@ mod_authn_dbi_password_cmp (const char *userpw, unsigned long userpwlen, http_au /*(compare 16-byte MD5 binary instead of converting to hex strings * in order to then have to do case-insensitive hex str comparison)*/ return (0 == http_auth_digest_hex2bin(userpw, 32, md5pw, sizeof(md5pw))) - ? http_auth_const_time_memeq(HA1, md5pw, sizeof(md5pw)) ? 0 : 1 + ? ck_memeq_const_time_fixed_len(HA1, md5pw, sizeof(md5pw)) ? 0 : 1 : -1; } #ifdef USE_LIB_CRYPTO @@ -423,7 +424,7 @@ mod_authn_dbi_password_cmp (const char *userpw, unsigned long userpwlen, http_au /*(compare 32-byte binary digest instead of converting to hex strings * in order to then have to do case-insensitive hex str comparison)*/ return (0 == http_auth_digest_hex2bin(userpw, 64, shapw, sizeof(shapw))) - ? http_auth_const_time_memeq(HA1, shapw, sizeof(shapw)) ? 0 : 1 + ? ck_memeq_const_time_fixed_len(HA1, shapw, sizeof(shapw)) ? 0 : 1 : -1; } #endif diff --git a/src/mod_authn_file.c b/src/mod_authn_file.c index 8d17cf75..9b9182b8 100644 --- a/src/mod_authn_file.c +++ b/src/mod_authn_file.c @@ -21,6 +21,7 @@ #include "safe_memclear.h" #include "base.h" +#include "ck.h" #include "plugin.h" #include "fdevent.h" #include "http_auth.h" @@ -302,7 +303,7 @@ static handler_t mod_authn_file_htdigest_basic(request_st * const r, void *p_d, mod_authn_file_digest(&ai, pw, strlen(pw)); - int rc = (http_auth_const_time_memeq(htdigest, ai.digest, ai.dlen) + int rc = (ck_memeq_const_time_fixed_len(htdigest, ai.digest, ai.dlen) && http_auth_match_rules(require, username->ptr, NULL, NULL)); safe_memclear(htdigest, ai.dlen); @@ -394,7 +395,9 @@ static handler_t mod_authn_file_plain_basic(request_st * const r, void *p_d, con mod_authn_file_patch_config(r, p); rc = mod_authn_file_htpasswd_get(p->conf.auth_plain_userfile, CONST_BUF_LEN(username), password_buf, r->conf.errh); if (0 == rc) { - rc = http_auth_const_time_memeq_pad(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1; + rc = ck_memeq_const_time(CONST_BUF_LEN(password_buf), pw, strlen(pw)) + ? 0 + : -1; } safe_memclear(password_buf->ptr, password_buf->size); buffer_free(password_buf); diff --git a/src/mod_authn_mysql.c b/src/mod_authn_mysql.c index b99a53c4..99476aec 100644 --- a/src/mod_authn_mysql.c +++ b/src/mod_authn_mysql.c @@ -30,6 +30,7 @@ #include #include "base.h" +#include "ck.h" #include "http_auth.h" #include "log.h" #include "plugin.h" @@ -317,7 +318,7 @@ static int mod_authn_mysql_password_cmp(const char *userpw, unsigned long userpw /*(compare 16-byte MD5 binary instead of converting to hex strings * in order to then have to do case-insensitive hex str comparison)*/ return (0 == http_auth_digest_hex2bin(userpw, 32, md5pw, sizeof(md5pw))) - ? http_auth_const_time_memeq(HA1, md5pw, sizeof(md5pw)) ? 0 : 1 + ? ck_memeq_const_time_fixed_len(HA1, md5pw, sizeof(md5pw)) ? 0 : 1 : -1; } diff --git a/src/mod_secdownload.c b/src/mod_secdownload.c index 885cc32b..4e930449 100644 --- a/src/mod_secdownload.c +++ b/src/mod_secdownload.c @@ -4,6 +4,7 @@ #include "log.h" #include "buffer.h" #include "base64.h" +#include "ck.h" #include "http_auth.h" #include "plugin.h" @@ -159,8 +160,8 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c li_MD5_Update(&Md5Ctx, ts_str, 8); li_MD5_Final(HA1, &Md5Ctx); - return http_auth_const_time_memeq((char *)HA1, - (char *)md5bin, sizeof(md5bin)); + return ck_memeq_const_time_fixed_len((char *)HA1, + (char *)md5bin,sizeof(md5bin)); } #ifdef USE_LIB_CRYPTO case SECDL_HMAC_SHA1: @@ -179,7 +180,7 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c li_to_base64_no_padding(base64_digest, 28, digest, 20, BASE64_URL); return (27 == maclen) - && http_auth_const_time_memeq(mac, base64_digest, 27); + && ck_memeq_const_time_fixed_len(mac, base64_digest, 27); } break; case SECDL_HMAC_SHA256: @@ -198,7 +199,7 @@ static int secdl_verify_mac(plugin_config *config, const char* protected_path, c li_to_base64_no_padding(base64_digest, 44, digest, 32, BASE64_URL); return (43 == maclen) - && http_auth_const_time_memeq(mac, base64_digest, 43); + && ck_memeq_const_time_fixed_len(mac, base64_digest, 43); } break; #endif