[apache2] 03/03: mod_session_crypto: prevent padding oracle attack
Stefan Fritsch
sf at moszumanska.debian.org
Sat Jan 21 22:05:25 UTC 2017
This is an automated email from the git hooks/post-receive script.
sf pushed a commit to branch jessie
in repository apache2.
commit 5c80b90b75cd7970bf74cc09a66e000fd02e10cf
Author: Stefan Fritsch <sf at sfritsch.de>
Date: Sat Jan 21 22:19:47 2017 +0100
mod_session_crypto: prevent padding oracle attack
---
debian/changelog | 1 +
...016-0736-mod_session_crypto-padding-oracle.diff | 323 +++++++++++++++++++++
debian/patches/series | 1 +
3 files changed, 325 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index 5de448e..2e8cc3d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,6 @@
apache2 (2.4.10-10+deb8u8) UNRELEASED; urgency=medium
+ * CVE-2016-0736: mod_session_crypto: Prevent padding oracle attack.
* CVE-2016-2161: mod_auth_digest: Prevent segfaults when the shared memory
space is exhausted.
* Activate mod_reqtimeout in new installs and during updates from
diff --git a/debian/patches/CVE-2016-0736-mod_session_crypto-padding-oracle.diff b/debian/patches/CVE-2016-0736-mod_session_crypto-padding-oracle.diff
new file mode 100644
index 0000000..71d6b77
--- /dev/null
+++ b/debian/patches/CVE-2016-0736-mod_session_crypto-padding-oracle.diff
@@ -0,0 +1,323 @@
+# https://svn.apache.org/r1772925
+--- apache2.orig/modules/session/mod_session_crypto.c
++++ apache2/modules/session/mod_session_crypto.c
+@@ -18,6 +18,7 @@
+ #include "apu_version.h"
+ #include "apr_base64.h" /* for apr_base64_decode et al */
+ #include "apr_lib.h"
++#include "apr_md5.h"
+ #include "apr_strings.h"
+ #include "http_log.h"
+ #include "http_core.h"
+@@ -57,6 +58,146 @@ typedef struct {
+ int library_set;
+ } session_crypto_conf;
+
++/* Wrappers around apr_siphash24() and apr_crypto_equals(),
++ * available in APU-1.6/APR-2.0 only.
++ */
++#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 6)
++
++#include "apr_siphash.h"
++
++#define AP_SIPHASH_DSIZE APR_SIPHASH_DSIZE
++#define AP_SIPHASH_KSIZE APR_SIPHASH_KSIZE
++#define ap_siphash24_auth apr_siphash24_auth
++
++#define ap_crypto_equals apr_crypto_equals
++
++#else
++
++#define AP_SIPHASH_DSIZE 8
++#define AP_SIPHASH_KSIZE 16
++
++#define ROTL64(x, n) (((x) << (n)) | ((x) >> (64 - (n))))
++
++#define U8TO64_LE(p) \
++ (((apr_uint64_t)((p)[0]) ) | \
++ ((apr_uint64_t)((p)[1]) << 8) | \
++ ((apr_uint64_t)((p)[2]) << 16) | \
++ ((apr_uint64_t)((p)[3]) << 24) | \
++ ((apr_uint64_t)((p)[4]) << 32) | \
++ ((apr_uint64_t)((p)[5]) << 40) | \
++ ((apr_uint64_t)((p)[6]) << 48) | \
++ ((apr_uint64_t)((p)[7]) << 56))
++
++#define U64TO8_LE(p, v) \
++do { \
++ (p)[0] = (unsigned char)((v) ); \
++ (p)[1] = (unsigned char)((v) >> 8); \
++ (p)[2] = (unsigned char)((v) >> 16); \
++ (p)[3] = (unsigned char)((v) >> 24); \
++ (p)[4] = (unsigned char)((v) >> 32); \
++ (p)[5] = (unsigned char)((v) >> 40); \
++ (p)[6] = (unsigned char)((v) >> 48); \
++ (p)[7] = (unsigned char)((v) >> 56); \
++} while (0)
++
++#define SIPROUND() \
++do { \
++ v0 += v1; v1=ROTL64(v1,13); v1 ^= v0; v0=ROTL64(v0,32); \
++ v2 += v3; v3=ROTL64(v3,16); v3 ^= v2; \
++ v0 += v3; v3=ROTL64(v3,21); v3 ^= v0; \
++ v2 += v1; v1=ROTL64(v1,17); v1 ^= v2; v2=ROTL64(v2,32); \
++} while(0)
++
++static apr_uint64_t ap_siphash24(const void *src, apr_size_t len,
++ const unsigned char key[AP_SIPHASH_KSIZE])
++{
++ const unsigned char *ptr, *end;
++ apr_uint64_t v0, v1, v2, v3, m;
++ apr_uint64_t k0, k1;
++ unsigned int rem;
++
++ k0 = U8TO64_LE(key + 0);
++ k1 = U8TO64_LE(key + 8);
++ v3 = k1 ^ (apr_uint64_t)0x7465646279746573ULL;
++ v2 = k0 ^ (apr_uint64_t)0x6c7967656e657261ULL;
++ v1 = k1 ^ (apr_uint64_t)0x646f72616e646f6dULL;
++ v0 = k0 ^ (apr_uint64_t)0x736f6d6570736575ULL;
++
++ rem = (unsigned int)(len & 0x7);
++ for (ptr = src, end = ptr + len - rem; ptr < end; ptr += 8) {
++ m = U8TO64_LE(ptr);
++ v3 ^= m;
++ SIPROUND();
++ SIPROUND();
++ v0 ^= m;
++ }
++ m = (apr_uint64_t)(len & 0xff) << 56;
++ switch (rem) {
++ case 7: m |= (apr_uint64_t)ptr[6] << 48;
++ case 6: m |= (apr_uint64_t)ptr[5] << 40;
++ case 5: m |= (apr_uint64_t)ptr[4] << 32;
++ case 4: m |= (apr_uint64_t)ptr[3] << 24;
++ case 3: m |= (apr_uint64_t)ptr[2] << 16;
++ case 2: m |= (apr_uint64_t)ptr[1] << 8;
++ case 1: m |= (apr_uint64_t)ptr[0];
++ case 0: break;
++ }
++ v3 ^= m;
++ SIPROUND();
++ SIPROUND();
++ v0 ^= m;
++
++ v2 ^= 0xff;
++ SIPROUND();
++ SIPROUND();
++ SIPROUND();
++ SIPROUND();
++
++ return v0 ^ v1 ^ v2 ^ v3;
++}
++
++static void ap_siphash24_auth(unsigned char out[AP_SIPHASH_DSIZE],
++ const void *src, apr_size_t len,
++ const unsigned char key[AP_SIPHASH_KSIZE])
++{
++ apr_uint64_t h;
++ h = ap_siphash24(src, len, key);
++ U64TO8_LE(out, h);
++}
++
++static int ap_crypto_equals(const void *buf1, const void *buf2,
++ apr_size_t size)
++{
++ const unsigned char *p1 = buf1;
++ const unsigned char *p2 = buf2;
++ unsigned char diff = 0;
++ apr_size_t i;
++
++ for (i = 0; i < size; ++i) {
++ diff |= p1[i] ^ p2[i];
++ }
++
++ return 1 & ((diff - 1) >> 8);
++}
++
++#endif
++
++static void compute_auth(const void *src, apr_size_t len,
++ const char *passphrase, apr_size_t passlen,
++ unsigned char auth[AP_SIPHASH_DSIZE])
++{
++ unsigned char key[APR_MD5_DIGESTSIZE];
++
++ /* XXX: if we had a way to get the raw bytes from an apr_crypto_key_t
++ * we could use them directly (not available in APR-1.5.x).
++ * MD5 is 128bit too, so use it to get a suitable siphash key
++ * from the passphrase.
++ */
++ apr_md5(key, passphrase, passlen);
++
++ ap_siphash24_auth(auth, src, len, key);
++}
++
+ /**
+ * Initialise the encryption as per the current config.
+ *
+@@ -128,21 +269,14 @@ static apr_status_t encrypt_string(reque
+ apr_crypto_block_t *block = NULL;
+ unsigned char *encrypt = NULL;
+ unsigned char *combined = NULL;
+- apr_size_t encryptlen, tlen;
++ apr_size_t encryptlen, tlen, combinedlen;
+ char *base64;
+ apr_size_t blockSize = 0;
+ const unsigned char *iv = NULL;
+ apr_uuid_t salt;
+ apr_crypto_block_key_type_e *cipher;
+ const char *passphrase;
+-
+- /* by default, return an empty string */
+- *out = "";
+-
+- /* don't attempt to encrypt an empty string, trying to do so causes a segfault */
+- if (!in || !*in) {
+- return APR_SUCCESS;
+- }
++ apr_size_t passlen;
+
+ /* use a uuid as a salt value, and prepend it to our result */
+ apr_uuid_get(&salt);
+@@ -152,9 +286,9 @@ static apr_status_t encrypt_string(reque
+ }
+
+ /* encrypt using the first passphrase in the list */
+- passphrase = APR_ARRAY_IDX(dconf->passphrases, 0, char *);
+- res = apr_crypto_passphrase(&key, &ivSize, passphrase,
+- strlen(passphrase),
++ passphrase = APR_ARRAY_IDX(dconf->passphrases, 0, const char *);
++ passlen = strlen(passphrase);
++ res = apr_crypto_passphrase(&key, &ivSize, passphrase, passlen,
+ (unsigned char *) (&salt), sizeof(apr_uuid_t),
+ *cipher, APR_MODE_CBC, 1, 4096, f, r->pool);
+ if (APR_STATUS_IS_ENOKEY(res)) {
+@@ -183,8 +317,9 @@ static apr_status_t encrypt_string(reque
+ }
+
+ /* encrypt the given string */
+- res = apr_crypto_block_encrypt(&encrypt, &encryptlen, (unsigned char *)in,
+- strlen(in), block);
++ res = apr_crypto_block_encrypt(&encrypt, &encryptlen,
++ (const unsigned char *)in, strlen(in),
++ block);
+ if (APR_SUCCESS != res) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, res, r, APLOGNO(01830)
+ "apr_crypto_block_encrypt failed");
+@@ -198,18 +333,20 @@ static apr_status_t encrypt_string(reque
+ }
+ encryptlen += tlen;
+
+- /* prepend the salt and the iv to the result */
+- combined = apr_palloc(r->pool, ivSize + encryptlen + sizeof(apr_uuid_t));
+- memcpy(combined, &salt, sizeof(apr_uuid_t));
+- memcpy(combined + sizeof(apr_uuid_t), iv, ivSize);
+- memcpy(combined + sizeof(apr_uuid_t) + ivSize, encrypt, encryptlen);
+-
+- /* base64 encode the result */
+- base64 = apr_palloc(r->pool, apr_base64_encode_len(ivSize + encryptlen +
+- sizeof(apr_uuid_t) + 1)
+- * sizeof(char));
+- apr_base64_encode(base64, (const char *) combined,
+- ivSize + encryptlen + sizeof(apr_uuid_t));
++ /* prepend the salt and the iv to the result (keep room for the MAC) */
++ combinedlen = AP_SIPHASH_DSIZE + sizeof(apr_uuid_t) + ivSize + encryptlen;
++ combined = apr_palloc(r->pool, combinedlen);
++ memcpy(combined + AP_SIPHASH_DSIZE, &salt, sizeof(apr_uuid_t));
++ memcpy(combined + AP_SIPHASH_DSIZE + sizeof(apr_uuid_t), iv, ivSize);
++ memcpy(combined + AP_SIPHASH_DSIZE + sizeof(apr_uuid_t) + ivSize,
++ encrypt, encryptlen);
++ /* authenticate the whole salt+IV+ciphertext with a leading MAC */
++ compute_auth(combined + AP_SIPHASH_DSIZE, combinedlen - AP_SIPHASH_DSIZE,
++ passphrase, passlen, combined);
++
++ /* base64 encode the result (APR handles the trailing '\0') */
++ base64 = apr_palloc(r->pool, apr_base64_encode_len(combinedlen));
++ apr_base64_encode(base64, (const char *) combined, combinedlen);
+ *out = base64;
+
+ return res;
+@@ -234,6 +371,7 @@ static apr_status_t decrypt_string(reque
+ char *decoded;
+ apr_size_t blockSize = 0;
+ apr_crypto_block_key_type_e *cipher;
++ unsigned char auth[AP_SIPHASH_DSIZE];
+ int i = 0;
+
+ /* strip base64 from the string */
+@@ -241,6 +379,13 @@ static apr_status_t decrypt_string(reque
+ decodedlen = apr_base64_decode(decoded, in);
+ decoded[decodedlen] = '\0';
+
++ /* sanity check - decoded too short? */
++ if (decodedlen < (AP_SIPHASH_DSIZE + sizeof(apr_uuid_t))) {
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO()
++ "too short to decrypt, aborting");
++ return APR_ECRYPT;
++ }
++
+ res = crypt_init(r, f, &cipher, dconf);
+ if (res != APR_SUCCESS) {
+ return res;
+@@ -249,14 +394,25 @@ static apr_status_t decrypt_string(reque
+ /* try each passphrase in turn */
+ for (; i < dconf->passphrases->nelts; i++) {
+ const char *passphrase = APR_ARRAY_IDX(dconf->passphrases, i, char *);
+- apr_size_t len = decodedlen;
+- char *slider = decoded;
++ apr_size_t passlen = strlen(passphrase);
++ apr_size_t len = decodedlen - AP_SIPHASH_DSIZE;
++ unsigned char *slider = (unsigned char *)decoded + AP_SIPHASH_DSIZE;
++
++ /* Verify authentication of the whole salt+IV+ciphertext by computing
++ * the MAC and comparing it (timing safe) with the one in the payload.
++ */
++ compute_auth(slider, len, passphrase, passlen, auth);
++ if (!ap_crypto_equals(auth, decoded, AP_SIPHASH_DSIZE)) {
++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO()
++ "auth does not match, skipping");
++ continue;
++ }
+
+ /* encrypt using the first passphrase in the list */
+- res = apr_crypto_passphrase(&key, &ivSize, passphrase,
+- strlen(passphrase),
+- (unsigned char *)decoded, sizeof(apr_uuid_t),
+- *cipher, APR_MODE_CBC, 1, 4096, f, r->pool);
++ res = apr_crypto_passphrase(&key, &ivSize, passphrase, passlen,
++ slider, sizeof(apr_uuid_t),
++ *cipher, APR_MODE_CBC, 1, 4096,
++ f, r->pool);
+ if (APR_STATUS_IS_ENOKEY(res)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01832)
+ "the passphrase '%s' was empty", passphrase);
+@@ -279,7 +435,7 @@ static apr_status_t decrypt_string(reque
+ }
+
+ /* sanity check - decoded too short? */
+- if (decodedlen < (sizeof(apr_uuid_t) + ivSize)) {
++ if (len < (sizeof(apr_uuid_t) + ivSize)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(01836)
+ "too short to decrypt, skipping");
+ res = APR_ECRYPT;
+@@ -290,8 +446,8 @@ static apr_status_t decrypt_string(reque
+ slider += sizeof(apr_uuid_t);
+ len -= sizeof(apr_uuid_t);
+
+- res = apr_crypto_block_decrypt_init(&block, &blockSize, (unsigned char *)slider, key,
+- r->pool);
++ res = apr_crypto_block_decrypt_init(&block, &blockSize, slider, key,
++ r->pool);
+ if (APR_SUCCESS != res) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01837)
+ "apr_crypto_block_decrypt_init failed");
+@@ -304,7 +460,7 @@ static apr_status_t decrypt_string(reque
+
+ /* decrypt the given string */
+ res = apr_crypto_block_decrypt(&decrypted, &decryptedlen,
+- (unsigned char *)slider, len, block);
++ slider, len, block);
+ if (res) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(01838)
+ "apr_crypto_block_decrypt failed");
diff --git a/debian/patches/series b/debian/patches/series
index 592e402..6079a04 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -22,3 +22,4 @@ CVE-2016-5387.patch
mod_socache_memcache_idle_timeout.patch
mod_proxy_fcgi_304_body.patch
CVE-2016-2161-mod_auth_digest_segfault.diff
+CVE-2016-0736-mod_session_crypto-padding-oracle.diff
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-apache/apache2.git
More information about the Pkg-apache-commits
mailing list