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:
parent
89dfbf14a5
commit
0e749c1c84
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue