Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CVE-2016-6225 in 2.4 #267

Merged
merged 2 commits into from
Nov 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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