[mod_auth] http_auth_const_time_memeq() (#2975, #2976)

use constant time comparison when comparing digests

(mitigation for brute-force timing attacks against digests
 generated using the same nonce)

x-ref:
  "Digest auth nonces are not validated"
  https://redmine.lighttpd.net/issues/2976
  "safe_memcmp new function proposal"
  https://redmine.lighttpd.net/issues/2975
This commit is contained in:
Glenn Strauss 2019-09-08 18:26:58 -04:00
parent 89dfbf14a5
commit 0e749c1c84
5 changed files with 29 additions and 3 deletions

View File

@ -51,6 +51,29 @@ 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

View File

@ -70,6 +70,9 @@ void http_auth_scheme_set (const http_auth_scheme_t *scheme);
const http_auth_backend_t * http_auth_backend_get (const buffer *name);
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);

View File

@ -1128,7 +1128,7 @@ static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d,
mod_auth_digest_mutate(&ai,m,uri,nonce,cnonce,nc,qop);
if (0 != memcmp(rdigest, ai.digest, ai.dlen)) {
if (!http_auth_const_time_memeq(rdigest, ai.digest, ai.dlen)) {
/* digest not ok */
log_error_write(srv, __FILE__, __LINE__, "sssB",
"digest: auth failed for ", username, ": wrong password, IP:", con->dst_addr_buf);

View File

@ -356,7 +356,7 @@ static handler_t mod_authn_file_htdigest_basic(server *srv, connection *con, voi
mod_authn_file_digest(&ai, pw, strlen(pw));
return (0 == memcmp(htdigest, ai.digest, ai.dlen)
return (http_auth_const_time_memeq(htdigest, ai.digest, ai.dlen)
&& http_auth_match_rules(require, username->ptr, NULL, NULL))
? HANDLER_GO_ON
: HANDLER_ERROR;

View File

@ -380,7 +380,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)))
? memcmp(HA1, md5pw, sizeof(md5pw))
? http_auth_const_time_memeq(HA1, md5pw, sizeof(md5pw)) ? 0 : 1
: -1;
}