Skip to content

Commit

Permalink
Merge pull request #267 from gl-sergei/2.4-xb-pxb-186
Browse files Browse the repository at this point in the history
Fix CVE-2016-6225 in 2.4
  • Loading branch information
gl-sergei committed Nov 22, 2016
2 parents 83b3366 + c043893 commit 83efde4
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 105 deletions.
35 changes: 25 additions & 10 deletions storage/innobase/xtrabackup/src/ds_encrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA

#include "xbcrypt.h"

#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif

#define XB_CRYPT_CHUNK_SIZE ((size_t) (xtrabackup_encrypt_chunk_size))

Expand Down Expand Up @@ -101,7 +103,6 @@ static uint encrypt_algos[] = { GCRY_CIPHER_NONE, GCRY_CIPHER_AES128,
static uint encrypt_algo;
static const uint encrypt_mode = GCRY_CIPHER_MODE_CTR;
static uint encrypt_key_len = 0;
static const unsigned char encrypt_iv[] = "Percona Xtrabackup is Awesome!!!";
static size_t encrypt_iv_len = 0;

static
Expand Down Expand Up @@ -132,6 +133,7 @@ encrypt_init(const char *root)

/* Acording to gcrypt docs (and my testing), setting up the threading
callbacks must be done first, so, lets give it a shot */
#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
gcry_error = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
if (gcry_error) {
msg("encrypt: unable to set libgcrypt thread cbs - "
Expand All @@ -140,6 +142,7 @@ encrypt_init(const char *root)
gcry_strerror(gcry_error));
return NULL;
}
#endif

/* Version check should be the very next call because it
makes sure that important subsystems are intialized. */
Expand Down Expand Up @@ -181,7 +184,6 @@ encrypt_init(const char *root)
/* Set up the iv length */
encrypt_iv_len = gcry_cipher_get_algo_blklen(encrypt_algo);
xb_a(encrypt_iv_len > 0);
xb_a(encrypt_iv_len <= sizeof(encrypt_iv));

/* Now set up the key */
if (xtrabackup_encrypt_key == NULL &&
Expand Down Expand Up @@ -347,7 +349,8 @@ encrypt_write(ds_file_t *file, const void *buf, size_t len)

if (xb_crypt_write_chunk(crypt_file->xbcrypt_file,
threads[i].to,
threads[i].from_len,
threads[i].from_len +
XB_CRYPT_HASH_LEN,
threads[i].to_len,
threads[i].iv,
encrypt_iv_len)) {
Expand Down Expand Up @@ -428,8 +431,8 @@ create_worker_threads(uint n)
thd->data_avail = FALSE;

thd->to = (char *) my_malloc(PSI_NOT_INSTRUMENTED,
XB_CRYPT_CHUNK_SIZE,
MYF(MY_FAE));
XB_CRYPT_CHUNK_SIZE +
XB_CRYPT_HASH_LEN, MYF(MY_FAE));

thd->iv = (char *) my_malloc(PSI_NOT_INSTRUMENTED,
encrypt_iv_len,
Expand Down Expand Up @@ -557,6 +560,14 @@ encrypt_worker_thread_func(void *arg)
if (thd->cancelled)
break;

/* ensure that XB_CRYPT_HASH_LEN is the correct length
of XB_CRYPT_HASH hashing algorithm output */
assert(gcry_md_get_algo_dlen(XB_CRYPT_HASH) ==
XB_CRYPT_HASH_LEN);

memcpy(thd->to, thd->from, thd->from_len);
gcry_md_hash_buffer(XB_CRYPT_HASH, thd->to + thd->from_len,
thd->from, thd->from_len);
thd->to_len = thd->from_len;

if (encrypt_algo != GCRY_CIPHER_NONE) {
Expand All @@ -573,7 +584,7 @@ encrypt_worker_thread_func(void *arg)
}

xb_crypt_create_iv(thd->iv, encrypt_iv_len);
gcry_error = gcry_cipher_setiv(thd->cipher_handle,
gcry_error = gcry_cipher_setctr(thd->cipher_handle,
thd->iv,
encrypt_iv_len);
if (gcry_error) {
Expand All @@ -587,18 +598,22 @@ encrypt_worker_thread_func(void *arg)

gcry_error = gcry_cipher_encrypt(thd->cipher_handle,
thd->to,
thd->to_len,
thd->from,
thd->from_len);
thd->to_len +
XB_CRYPT_HASH_LEN,
thd->to,
thd->from_len +
XB_CRYPT_HASH_LEN);
if (gcry_error) {
msg("encrypt: unable to encrypt buffer - "
"%s : %s\n", gcry_strsource(gcry_error),
gcry_strerror(gcry_error));
thd->to_len = 0;
}
} else {
memcpy(thd->to, thd->from, thd->from_len);
memcpy(thd->to, thd->from,
thd->from_len + XB_CRYPT_HASH_LEN);
}
thd->to_len += XB_CRYPT_HASH_LEN;
}

pthread_mutex_unlock(&thd->data_mutex);
Expand Down
111 changes: 65 additions & 46 deletions storage/innobase/xtrabackup/src/xbcrypt.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include <gcrypt.h>
#include <string.h>

#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif

#define XBCRYPT_VERSION "1.0"
#define XBCRYPT_VERSION "1.1"

typedef enum {
RUN_MODE_NONE,
Expand Down Expand Up @@ -58,8 +60,6 @@ static uint encrypt_algos[] = { GCRY_CIPHER_NONE,
static int encrypt_algo = 0;
static int encrypt_mode = GCRY_CIPHER_MODE_CTR;
static uint encrypt_key_len = 0;
static const unsigned char v1_encrypt_iv[] =
"Percona Xtrabackup is Awesome!!!";
static size_t encrypt_iv_len = 0;

static struct my_option my_long_options[] =
Expand Down Expand Up @@ -132,7 +132,9 @@ mode_encrypt(File filein, File fileout);
int
main(int argc, char **argv)
{
#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
gcry_error_t gcry_error;
#endif
File filein = 0;
File fileout = 0;

Expand All @@ -144,6 +146,7 @@ main(int argc, char **argv)

/* Acording to gcrypt docs (and my testing), setting up the threading
callbacks must be done first, so, lets give it a shot */
#if !defined(GCRYPT_VERSION_NUMBER) || (GCRYPT_VERSION_NUMBER < 0x010600)
gcry_error = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
if (gcry_error) {
msg("%s: unable to set libgcrypt thread cbs - "
Expand All @@ -152,6 +155,7 @@ main(int argc, char **argv)
gcry_strerror(gcry_error));
return 1;
}
#endif

/* Version check should be the very first call because it
makes sure that important subsystems are intialized. */
Expand Down Expand Up @@ -305,6 +309,7 @@ mode_decrypt(File filein, File fileout)
xb_rcrypt_result_t result;
gcry_cipher_hd_t cipher_handle;
gcry_error_t gcry_error;
my_bool hash_appended;

if (encrypt_algo != GCRY_CIPHER_NONE) {
gcry_error = gcry_cipher_open(&cipher_handle,
Expand Down Expand Up @@ -340,7 +345,7 @@ mode_decrypt(File filein, File fileout)
/* Walk the encrypted chunks, decrypting them and writing out */
while ((result = xb_crypt_read_chunk(xbcrypt_file, &chunkbuf,
&originalsize, &chunksize,
&ivbuf, &ivsize))
&ivbuf, &ivsize, &hash_appended))
== XB_CRYPT_READ_CHUNK) {

if (encrypt_algo != GCRY_CIPHER_NONE) {
Expand All @@ -354,13 +359,9 @@ mode_decrypt(File filein, File fileout)
}

if (ivsize) {
gcry_error = gcry_cipher_setiv(cipher_handle,
ivbuf,
ivsize);
} else {
gcry_error = gcry_cipher_setiv(cipher_handle,
v1_encrypt_iv,
encrypt_iv_len);
gcry_error = gcry_cipher_setctr(cipher_handle,
ivbuf,
ivsize);
}
if (gcry_error) {
msg("%s:decrypt: unable to set cipher iv - "
Expand All @@ -371,18 +372,12 @@ mode_decrypt(File filein, File fileout)
}

if (decryptbufsize < originalsize) {
if (decryptbufsize) {
decryptbuf = my_realloc(PSI_NOT_INSTRUMENTED,
decryptbuf,
originalsize,
MYF(MY_WME));
decryptbufsize = originalsize;
} else {
decryptbuf = my_malloc(PSI_NOT_INSTRUMENTED,
originalsize,
MYF(MY_WME));
decryptbufsize = originalsize;
}
decryptbuf = my_realloc(
PSI_NOT_INSTRUMENTED,
decryptbuf,
originalsize,
MYF(MY_WME | MY_ALLOW_ZERO_PTR));
decryptbufsize = originalsize;
}

/* Try to decrypt it */
Expand All @@ -404,8 +399,29 @@ mode_decrypt(File filein, File fileout)
decryptbuf = chunkbuf;
}

if (hash_appended) {
uchar hash[XB_CRYPT_HASH_LEN];

originalsize -= XB_CRYPT_HASH_LEN;

/* ensure that XB_CRYPT_HASH_LEN is the correct length
of XB_CRYPT_HASH hashing algorithm output */
assert(gcry_md_get_algo_dlen(XB_CRYPT_HASH) ==
XB_CRYPT_HASH_LEN);
gcry_md_hash_buffer(XB_CRYPT_HASH, hash, decryptbuf,
originalsize);
if (memcmp(hash, (char *) decryptbuf + originalsize,
XB_CRYPT_HASH_LEN) != 0) {
msg("%s:%s invalid plaintext hash. "
"Wrong encrytion key specified?\n",
my_progname, __FUNCTION__);
result = XB_CRYPT_READ_ERROR;
goto err;
}
}

/* Write it out */
if (my_write(fileout, decryptbuf, originalsize,
if (my_write(fileout, (const uchar *) decryptbuf, originalsize,
MYF(MY_WME | MY_NABP))) {
msg("%s:decrypt: unable to write output chunk.\n",
my_progname);
Expand Down Expand Up @@ -460,7 +476,7 @@ mode_encrypt(File filein, File fileout)
{
size_t bytesread;
size_t chunkbuflen;
void *chunkbuf = NULL;
uchar *chunkbuf = NULL;
void *ivbuf = NULL;
size_t encryptbuflen = 0;
size_t encryptedlen = 0;
Expand Down Expand Up @@ -508,11 +524,21 @@ mode_encrypt(File filein, File fileout)
ivbuf = my_malloc(PSI_NOT_INSTRUMENTED, encrypt_iv_len, MYF(MY_FAE));

/* now read in data in chunk size, encrypt and write out */
chunkbuflen = opt_encrypt_chunk_size;
chunkbuf = my_malloc(PSI_NOT_INSTRUMENTED, chunkbuflen, MYF(MY_FAE));
while ((bytesread = my_read(filein, chunkbuf, chunkbuflen,
chunkbuflen = opt_encrypt_chunk_size + XB_CRYPT_HASH_LEN;
chunkbuf = (uchar *) my_malloc(PSI_NOT_INSTRUMENTED,
chunkbuflen, MYF(MY_FAE));
while ((bytesread = my_read(filein, chunkbuf, opt_encrypt_chunk_size,
MYF(MY_WME))) > 0) {

size_t origbuflen = bytesread + XB_CRYPT_HASH_LEN;

/* ensure that XB_CRYPT_HASH_LEN is the correct length
of XB_CRYPT_HASH hashing algorithm output */
assert(XB_CRYPT_HASH_LEN ==
gcry_md_get_algo_dlen(XB_CRYPT_HASH));
gcry_md_hash_buffer(XB_CRYPT_HASH, chunkbuf + bytesread,
chunkbuf, bytesread);

if (encrypt_algo != GCRY_CIPHER_NONE) {
gcry_error = gcry_cipher_reset(cipher_handle);

Expand All @@ -525,7 +551,7 @@ mode_encrypt(File filein, File fileout)
}

xb_crypt_create_iv(ivbuf, encrypt_iv_len);
gcry_error = gcry_cipher_setiv(cipher_handle,
gcry_error = gcry_cipher_setctr(cipher_handle,
ivbuf,
encrypt_iv_len);

Expand All @@ -537,28 +563,20 @@ mode_encrypt(File filein, File fileout)
continue;
}

if (encryptbuflen < bytesread) {
if (encryptbuflen) {
encryptbuf = my_realloc(PSI_NOT_INSTRUMENTED,
encryptbuf,
bytesread,
MYF(MY_WME));
encryptbuflen = bytesread;
} else {
encryptbuf = my_malloc(PSI_NOT_INSTRUMENTED,
bytesread,
MYF(MY_WME));
encryptbuflen = bytesread;
}
if (encryptbuflen < origbuflen) {
encryptbuf = my_realloc(PSI_NOT_INSTRUMENTED,
encryptbuf, origbuflen,
MYF(MY_WME | MY_ALLOW_ZERO_PTR));
encryptbuflen = origbuflen;
}

gcry_error = gcry_cipher_encrypt(cipher_handle,
encryptbuf,
encryptbuflen,
chunkbuf,
bytesread);
origbuflen);

encryptedlen = bytesread;
encryptedlen = origbuflen;

if (gcry_error) {
msg("%s:encrypt: unable to encrypt chunk - "
Expand All @@ -569,11 +587,12 @@ mode_encrypt(File filein, File fileout)
goto err;
}
} else {
encryptedlen = bytesread;
encryptedlen = origbuflen;
encryptbuf = chunkbuf;
}

if (xb_crypt_write_chunk(xbcrypt_file, encryptbuf, bytesread,
if (xb_crypt_write_chunk(xbcrypt_file, encryptbuf,
bytesread + XB_CRYPT_HASH_LEN,
encryptedlen, ivbuf, encrypt_iv_len)) {
msg("%s:encrypt: abcrypt_write_chunk() failed.\n",
my_progname);
Expand Down
9 changes: 7 additions & 2 deletions storage/innobase/xtrabackup/src/xbcrypt.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "common.h"

#define XB_CRYPT_CHUNK_MAGIC1 "XBCRYP01"
#define XB_CRYPT_CHUNK_MAGIC2 "XBCRYP02" /* must be same size as ^^ */
#define XB_CRYPT_CHUNK_MAGIC2 "XBCRYP02"
#define XB_CRYPT_CHUNK_MAGIC3 "XBCRYP03" /* must be same size as ^^ */
#define XB_CRYPT_CHUNK_MAGIC_CURRENT XB_CRYPT_CHUNK_MAGIC3
#define XB_CRYPT_CHUNK_MAGIC_SIZE (sizeof(XB_CRYPT_CHUNK_MAGIC1)-1)

#define XB_CRYPT_HASH GCRY_MD_SHA256
#define XB_CRYPT_HASH_LEN 32

/******************************************************************************
Write interface */
typedef struct xb_wcrypt_struct xb_wcrypt_t;
Expand Down Expand Up @@ -66,7 +71,7 @@ typedef enum {

xb_rcrypt_result_t xb_crypt_read_chunk(xb_rcrypt_t *crypt, void **buf,
size_t *olen, size_t *elen, void **iv,
size_t *ivlen);
size_t *ivlen, my_bool *hash_appended);

int xb_crypt_read_close(xb_rcrypt_t *crypt);

Expand Down
Loading

0 comments on commit 83efde4

Please sign in to comment.