From 0e749c1c84326a51f0f8a80c6db49c31c8e920ab Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Sun, 8 Sep 2019 18:26:58 -0400 Subject: [PATCH] [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 --- src/http_auth.c | 23 +++++++++++++++++++++++ src/http_auth.h | 3 +++ src/mod_auth.c | 2 +- src/mod_authn_file.c | 2 +- src/mod_authn_mysql.c | 2 +- 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/http_auth.c b/src/http_auth.c index 24c2319a..bedb5fe0 100644 --- a/src/http_auth.c +++ b/src/http_auth.c @@ -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 diff --git a/src/http_auth.h b/src/http_auth.h index 64a32da7..5f6e00d4 100644 --- a/src/http_auth.h +++ b/src/http_auth.h @@ -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); diff --git a/src/mod_auth.c b/src/mod_auth.c index 34e5e91a..2a7bcea8 100644 --- a/src/mod_auth.c +++ b/src/mod_auth.c @@ -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); diff --git a/src/mod_authn_file.c b/src/mod_authn_file.c index 6f76794a..fa21892b 100644 --- a/src/mod_authn_file.c +++ b/src/mod_authn_file.c @@ -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; diff --git a/src/mod_authn_mysql.c b/src/mod_authn_mysql.c index c1f881f5..d8842bfa 100644 --- a/src/mod_authn_mysql.c +++ b/src/mod_authn_mysql.c @@ -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; }