diff --git a/src/src/auths/cram_md5.c b/src/src/auths/cram_md5.c index 44b05465cf..6274780dd5 100644 --- a/src/src/auths/cram_md5.c +++ b/src/src/auths/cram_md5.c @@ -238,15 +238,21 @@ HDEBUG(D_auth) /* We now have to compare the digest, which is 16 bytes in binary, with the data received, which is expressed in lower case hex. We checked above that -there were 32 characters of data left. */ +there were 32 characters of data left. Use constant-time comparison to +prevent timing side-channel. */ -for (i = 0; i < 16; i++) - { - int a = *clear++; - int b = *clear++; - if (((((a >= 'a')? a - 'a' + 10 : a - '0') << 4) + - ((b >= 'a')? b - 'a' + 10 : b - '0')) != digest[i]) return FAIL; - } +{ + volatile unsigned char result = 0; + for (i = 0; i < 16; i++) + { + int a = *clear++; + int b = *clear++; + unsigned char hex_val = (((a >= 'a')? a - 'a' + 10 : a - '0') << 4) + + ((b >= 'a')? b - 'a' + 10 : b - '0'); + result |= hex_val ^ digest[i]; + } + if (result != 0) return FAIL; +} /* Expand server_condition as an authorization check */ return auth_check_serv_cond(ablock); diff --git a/src/src/auths/spa.c b/src/src/auths/spa.c index 3e2cccde9d..12e63d8af2 100644 --- a/src/src/auths/spa.c +++ b/src/src/auths/spa.c @@ -260,8 +260,14 @@ if (off >= sizeof(SPAAuthResponse) - 24) } s = (US responseptr) + off; -if (memcmp(ntRespData, s, 24) == 0) - return auth_check_serv_cond(ablock); /* success. we have a winner. */ +/* Use constant-time comparison to prevent timing side-channel */ +{ + volatile unsigned char result = 0; + for (int i = 0; i < 24; i++) + result |= ntRespData[i] ^ s[i]; + if (result == 0) + return auth_check_serv_cond(ablock); /* success. we have a winner. */ +} /* Expand server_condition as an authorization check (PH) */ diff --git a/src/src/mime.c b/src/src/mime.c index 0197894dab..da48325f33 100644 --- a/src/src/mime.c +++ b/src/src/mime.c @@ -496,6 +496,21 @@ int rc = OK; uschar * header = NULL; struct mime_boundary_context nested_context; +/* Limit MIME nesting depth to prevent stack overflow from crafted messages. +RFC 2046 does not specify a maximum, but 100 levels is far beyond any +legitimate use. */ + + { + int depth = 0; + for (struct mime_boundary_context *p = context; p; p = p->parent) + if (++depth > 100) + { + log_write(0, LOG_MAIN, + "MIME: nesting depth limit (100) exceeded, skipping"); + return OK; + } + } + /* reserve a line buffer to work in. Assume tainted data. */ header = store_get(MIME_MAX_HEADER_SIZE+1, GET_TAINTED); diff --git a/src/src/receive.c b/src/src/receive.c index f1833c5a09..dc518e0f7b 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1095,7 +1095,7 @@ for (;;) { if (chunking_data_left > 0) { - unsigned len = MAX(chunking_data_left, thismessage_size_limit - message_size + 1); + unsigned len = MIN(chunking_data_left, thismessage_size_limit - message_size + 1); const uschar * buf = bdat_getbuf(&len); if (!buf) return END_EOF;