diff options
Diffstat (limited to 'xlators/encryption/crypt/src/metadata.c')
-rw-r--r-- | xlators/encryption/crypt/src/metadata.c | 605 |
1 files changed, 605 insertions, 0 deletions
diff --git a/xlators/encryption/crypt/src/metadata.c b/xlators/encryption/crypt/src/metadata.c new file mode 100644 index 00000000000..36b14c0558e --- /dev/null +++ b/xlators/encryption/crypt/src/metadata.c @@ -0,0 +1,605 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "defaults.h" +#include "crypt-common.h" +#include "crypt.h" +#include "metadata.h" + +int32_t alloc_format(crypt_local_t *local, size_t size) +{ + if (size > 0) { + local->format = GF_CALLOC(1, size, gf_crypt_mt_mtd); + if (!local->format) + return ENOMEM; + } + local->format_size = size; + return 0; +} + +int32_t alloc_format_create(crypt_local_t *local) +{ + return alloc_format(local, new_format_size()); +} + +void free_format(crypt_local_t *local) +{ + GF_FREE(local->format); +} + +/* + * Check compatibility with extracted metadata + */ +static int32_t check_file_metadata(struct crypt_inode_info *info) +{ + struct object_cipher_info *object = &info->cinfo; + + if (info->nr_minor != CRYPT_XLATOR_ID) { + gf_log("crypt", GF_LOG_WARNING, + "unsupported minor subversion %d", info->nr_minor); + return EINVAL; + } + if (object->o_alg > LAST_CIPHER_ALG) { + gf_log("crypt", GF_LOG_WARNING, + "unsupported cipher algorithm %d", + object->o_alg); + return EINVAL; + } + if (object->o_mode > LAST_CIPHER_MODE) { + gf_log("crypt", GF_LOG_WARNING, + "unsupported cipher mode %d", + object->o_mode); + return EINVAL; + } + if (object->o_block_bits < CRYPT_MIN_BLOCK_BITS || + object->o_block_bits > CRYPT_MAX_BLOCK_BITS) { + gf_log("crypt", GF_LOG_WARNING, "unsupported block bits %d", + object->o_block_bits); + return EINVAL; + } + /* TBD: check data key size */ + return 0; +} + +static size_t format_size_v1(mtd_op_t op, size_t old_size) +{ + + switch (op) { + case MTD_CREATE: + return sizeof(struct mtd_format_v1); + case MTD_OVERWRITE: + return old_size; + case MTD_APPEND: + return old_size + NMTD_8_MAC_SIZE; + case MTD_CUT: + if (old_size > sizeof(struct mtd_format_v1)) + return old_size - NMTD_8_MAC_SIZE; + else + return 0; + default: + gf_log("crypt", GF_LOG_WARNING, "Bad mtd operation"); + return 0; + } +} + +/* + * Calculate size of the updated format string. + * Returned zero means that we don't need to update the format string. + */ +size_t format_size(mtd_op_t op, size_t old_size) +{ + size_t versioned; + + versioned = mtd_loaders[current_mtd_loader()].format_size(op, + old_size - sizeof(struct crypt_format)); + if (versioned != 0) + return versioned + sizeof(struct crypt_format); + return 0; +} + +/* + * size of the format string of newly created file (nr_links = 1) + */ +size_t new_format_size(void) +{ + return format_size(MTD_CREATE, 0); +} + +/* + * Calculate per-link MAC by pathname + */ +static int32_t calc_link_mac_v1(struct mtd_format_v1 *fmt, + loc_t *loc, + unsigned char *result, + struct crypt_inode_info *info, + struct master_cipher_info *master) +{ + int32_t ret; + unsigned char nmtd_link_key[16]; + CMAC_CTX *cctx; + size_t len; + + ret = get_nmtd_link_key(loc, master, nmtd_link_key); + if (ret) { + gf_log("crypt", GF_LOG_ERROR, "Can not get nmtd link key"); + return -1; + } + cctx = CMAC_CTX_new(); + if (!cctx) { + gf_log("crypt", GF_LOG_ERROR, "CMAC_CTX_new failed"); + return -1; + } + ret = CMAC_Init(cctx, nmtd_link_key, sizeof(nmtd_link_key), + EVP_aes_128_cbc(), 0); + if (!ret) { + gf_log("crypt", GF_LOG_ERROR, "CMAC_Init failed"); + CMAC_CTX_free(cctx); + return -1; + } + ret = CMAC_Update(cctx, get_NMTD_V1(info), SIZE_OF_NMTD_V1); + if (!ret) { + gf_log("crypt", GF_LOG_ERROR, "CMAC_Update failed"); + CMAC_CTX_free(cctx); + return -1; + } + ret = CMAC_Final(cctx, result, &len); + CMAC_CTX_free(cctx); + if (!ret) { + gf_log("crypt", GF_LOG_ERROR, "CMAC_Final failed"); + return -1; + } + return 0; +} + +/* + * Create per-link MAC of index @idx by pathname + */ +static int32_t create_link_mac_v1(struct mtd_format_v1 *fmt, + uint32_t idx, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master) +{ + int32_t ret; + unsigned char *mac; + unsigned char cmac[16]; + + mac = get_NMTD_V1_MAC(fmt) + idx * SIZE_OF_NMTD_V1_MAC; + + ret = calc_link_mac_v1(fmt, loc, cmac, info, master); + if (ret) + return -1; + memcpy(mac, cmac, SIZE_OF_NMTD_V1_MAC); + return 0; +} + +static int32_t create_format_v1(unsigned char *wire, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master) +{ + int32_t ret; + struct mtd_format_v1 *fmt; + unsigned char mtd_key[16]; + AES_KEY EMTD_KEY; + unsigned char nmtd_link_key[16]; + uint32_t ad; + GCM128_CONTEXT *gctx; + + fmt = (struct mtd_format_v1 *)wire; + + fmt->minor_id = info->nr_minor; + fmt->alg_id = AES_CIPHER_ALG; + fmt->dkey_factor = master->m_dkey_size >> KEY_FACTOR_BITS; + fmt->block_bits = master->m_block_bits; + fmt->mode_id = master->m_mode; + /* + * retrieve keys for the parts of metadata + */ + ret = get_emtd_file_key(info, master, mtd_key); + if (ret) + return ret; + ret = get_nmtd_link_key(loc, master, nmtd_link_key); + if (ret) + return ret; + + AES_set_encrypt_key(mtd_key, sizeof(mtd_key)*8, &EMTD_KEY); + + gctx = CRYPTO_gcm128_new(&EMTD_KEY, (block128_f)AES_encrypt); + + /* TBD: Check return values */ + + CRYPTO_gcm128_setiv(gctx, info->oid, sizeof(uuid_t)); + + ad = htole32(MTD_LOADER_V1); + ret = CRYPTO_gcm128_aad(gctx, (const unsigned char *)&ad, sizeof(ad)); + if (ret) { + gf_log("crypt", GF_LOG_ERROR, " CRYPTO_gcm128_aad failed"); + CRYPTO_gcm128_release(gctx); + return ret; + } + ret = CRYPTO_gcm128_encrypt(gctx, + get_EMTD_V1(fmt), + get_EMTD_V1(fmt), + SIZE_OF_EMTD_V1); + if (ret) { + gf_log("crypt", GF_LOG_ERROR, " CRYPTO_gcm128_encrypt failed"); + CRYPTO_gcm128_release(gctx); + return ret; + } + /* + * set MAC of encrypted part of metadata + */ + CRYPTO_gcm128_tag(gctx, get_EMTD_V1_MAC(fmt), SIZE_OF_EMTD_V1_MAC); + CRYPTO_gcm128_release(gctx); + /* + * set the first MAC of non-encrypted part of metadata + */ + return create_link_mac_v1(fmt, 0, loc, info, master); +} + +/* + * Called by fops: + * ->create(); + * ->link(); + * + * Pack common and version-specific parts of file's metadata + * Pre-conditions: @info contains valid object-id. + */ +int32_t create_format(unsigned char *wire, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master) +{ + struct crypt_format *fmt = (struct crypt_format *)wire; + + fmt->loader_id = current_mtd_loader(); + + wire += sizeof(struct crypt_format); + return mtd_loaders[current_mtd_loader()].create_format(wire, loc, + info, master); +} + +/* + * Append or overwrite per-link mac of @mac_idx index + * in accordance with the new pathname + */ +int32_t appov_link_mac_v1(unsigned char *new, + unsigned char *old, + uint32_t old_size, + int32_t mac_idx, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master, + crypt_local_t *local) +{ + memcpy(new, old, old_size); + return create_link_mac_v1((struct mtd_format_v1 *)new, mac_idx, + loc, info, master); +} + +/* + * Cut per-link mac of @mac_idx index + */ +static int32_t cut_link_mac_v1(unsigned char *new, + unsigned char *old, + uint32_t old_size, + int32_t mac_idx, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master, + crypt_local_t *local) +{ + memcpy(new, + old, + sizeof(struct mtd_format_v1) + NMTD_8_MAC_SIZE * (mac_idx - 1)); + + memcpy(new + sizeof(struct mtd_format_v1) + NMTD_8_MAC_SIZE * (mac_idx - 1), + old + sizeof(struct mtd_format_v1) + NMTD_8_MAC_SIZE * mac_idx, + old_size - (sizeof(struct mtd_format_v1) + NMTD_8_MAC_SIZE * mac_idx)); + return 0; +} + +int32_t update_format_v1(unsigned char *new, + unsigned char *old, + size_t old_len, + int32_t mac_idx, /* of old name */ + mtd_op_t op, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master, + crypt_local_t *local) +{ + switch (op) { + case MTD_APPEND: + mac_idx = 1 + (old_len - sizeof(struct mtd_format_v1))/8; + case MTD_OVERWRITE: + return appov_link_mac_v1(new, old, old_len, mac_idx, + loc, info, master, local); + case MTD_CUT: + return cut_link_mac_v1(new, old, old_len, mac_idx, + loc, info, master, local); + default: + gf_log("crypt", GF_LOG_ERROR, "Bad mtd operation %d", op); + return -1; + } +} + +/* + * Called by fops: + * + * ->link() + * ->unlink() + * ->rename() + * + */ +int32_t update_format(unsigned char *new, + unsigned char *old, + size_t old_len, + int32_t mac_idx, + mtd_op_t op, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master, + crypt_local_t *local) +{ + if (!new) + return 0; + memcpy(new, old, sizeof(struct crypt_format)); + + old += sizeof(struct crypt_format); + new += sizeof(struct crypt_format); + old_len -= sizeof(struct crypt_format); + + return mtd_loaders[current_mtd_loader()].update_format(new, old, + old_len, + mac_idx, op, + loc, info, + master, local); +} + +/* + * Perform preliminary checks of found metadata + * Return < 0 on errors; + * Return number of object-id MACs (>= 1) on success + */ +int32_t check_format_v1(uint32_t len, unsigned char *wire) +{ + uint32_t nr_links; + + if (len < sizeof(struct mtd_format_v1)) { + gf_log("crypt", GF_LOG_ERROR, + "v1-loader: bad metadata size %d", len); + goto error; + } + len -= sizeof(struct mtd_format_v1); + if (len % sizeof(nmtd_8_mac_t)) { + gf_log("crypt", GF_LOG_ERROR, + "v1-loader: bad metadata format"); + goto error; + } + nr_links = 1 + len / sizeof(nmtd_8_mac_t); + if (nr_links > _POSIX_LINK_MAX) + goto error; + return nr_links; + error: + return EIO; +} + +/* + * Verify per-link MAC specified by index @idx + * + * return: + * -1 on errors; + * 0 on failed verification; + * 1 on sucessful verification + */ +static int32_t verify_link_mac_v1(struct mtd_format_v1 *fmt, + uint32_t idx /* index of the mac to verify */, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master) +{ + int32_t ret; + unsigned char *mac; + unsigned char cmac[16]; + + mac = get_NMTD_V1_MAC(fmt) + idx * SIZE_OF_NMTD_V1_MAC; + + ret = calc_link_mac_v1(fmt, loc, cmac, info, master); + if (ret) + return -1; + if (memcmp(cmac, mac, SIZE_OF_NMTD_V1_MAC)) + return 0; + return 1; +} + +/* + * Lookup per-link MAC by pathname. + * + * return index of the MAC, if it was found; + * return < 0 on errors, or if the MAC wasn't found + */ +static int32_t lookup_link_mac_v1(struct mtd_format_v1 *fmt, + uint32_t nr_macs, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master) +{ + int32_t ret; + uint32_t idx; + + for (idx = 0; idx < nr_macs; idx++) { + ret = verify_link_mac_v1(fmt, idx, loc, info, master); + if (ret < 0) + return ret; + if (ret > 0) + return idx; + } + return -ENOENT; +} + +/* + * Extract version-specific part of metadata + */ +static int32_t open_format_v1(unsigned char *wire, + int32_t len, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master, + crypt_local_t *local, + gf_boolean_t load_info) +{ + int32_t ret; + int32_t num_nmtd_macs; + struct mtd_format_v1 *fmt; + unsigned char mtd_key[16]; + AES_KEY EMTD_KEY; + GCM128_CONTEXT *gctx; + uint32_t ad; + emtd_8_mac_t gmac; + struct object_cipher_info *object; + + num_nmtd_macs = check_format_v1(len, wire); + if (num_nmtd_macs <= 0) + return EIO; + fmt = (struct mtd_format_v1 *)wire; + + ret = lookup_link_mac_v1(fmt, num_nmtd_macs, loc, info, master); + if (ret < 0) { + gf_log("crypt", GF_LOG_ERROR, "NMTD verification failed"); + return EINVAL; + } + local->mac_idx = ret; + if (load_info == _gf_false) + /* the case of partial open */ + return 0; + + object = &info->cinfo; + + ret = get_emtd_file_key(info, master, mtd_key); + if (ret) { + gf_log("crypt", GF_LOG_ERROR, "Can not retrieve metadata key"); + return ret; + } + /* + * decrypt encrypted meta-data + */ + ret = AES_set_encrypt_key(mtd_key, sizeof(mtd_key)*8, &EMTD_KEY); + if (ret < 0) { + gf_log("crypt", GF_LOG_ERROR, "Can not set encrypt key"); + return ret; + } + gctx = CRYPTO_gcm128_new(&EMTD_KEY, (block128_f)AES_encrypt); + if (!gctx) { + gf_log("crypt", GF_LOG_ERROR, "Can not alloc gcm context"); + return ENOMEM; + } + CRYPTO_gcm128_setiv(gctx, info->oid, sizeof(uuid_t)); + + ad = htole32(MTD_LOADER_V1); + ret = CRYPTO_gcm128_aad(gctx, (const unsigned char *)&ad, sizeof(ad)); + if (ret) { + gf_log("crypt", GF_LOG_ERROR, " CRYPTO_gcm128_aad failed"); + CRYPTO_gcm128_release(gctx); + return ret; + } + ret = CRYPTO_gcm128_decrypt(gctx, + get_EMTD_V1(fmt), + get_EMTD_V1(fmt), + SIZE_OF_EMTD_V1); + if (ret) { + gf_log("crypt", GF_LOG_ERROR, " CRYPTO_gcm128_decrypt failed"); + CRYPTO_gcm128_release(gctx); + return ret; + } + /* + * verify metadata + */ + CRYPTO_gcm128_tag(gctx, gmac, sizeof(gmac)); + CRYPTO_gcm128_release(gctx); + if (memcmp(gmac, get_EMTD_V1_MAC(fmt), SIZE_OF_EMTD_V1_MAC)) { + gf_log("crypt", GF_LOG_ERROR, "EMTD verification failed"); + return EINVAL; + } + /* + * load verified metadata to the private part of inode + */ + info->nr_minor = fmt->minor_id; + + object->o_alg = fmt->alg_id; + object->o_dkey_size = fmt->dkey_factor << KEY_FACTOR_BITS; + object->o_block_bits = fmt->block_bits; + object->o_mode = fmt->mode_id; + + return check_file_metadata(info); +} + +/* + * perform metadata authentication against @loc->path; + * extract crypt-specific attribtes and populate @info + * with them (optional) + */ +int32_t open_format(unsigned char *str, + int32_t len, + loc_t *loc, + struct crypt_inode_info *info, + struct master_cipher_info *master, + crypt_local_t *local, + gf_boolean_t load_info) +{ + struct crypt_format *fmt; + if (len < sizeof(*fmt)) { + gf_log("crypt", GF_LOG_ERROR, "Bad core format"); + return EIO; + } + fmt = (struct crypt_format *)str; + + if (fmt->loader_id >= LAST_MTD_LOADER) { + gf_log("crypt", GF_LOG_ERROR, + "Unsupported loader id %d", fmt->loader_id); + return EINVAL; + } + str += sizeof(*fmt); + len -= sizeof(*fmt); + + return mtd_loaders[fmt->loader_id].open_format(str, + len, + loc, + info, + master, + local, + load_info); +} + +struct crypt_mtd_loader mtd_loaders [LAST_MTD_LOADER] = { + [MTD_LOADER_V1] = + {.format_size = format_size_v1, + .create_format = create_format_v1, + .open_format = open_format_v1, + .update_format = update_format_v1 + } +}; + +/* + Local variables: + c-indentation-style: "K&R" + mode-name: "LC" + c-basic-offset: 8 + tab-width: 8 + fill-column: 80 + scroll-step: 1 + End: +*/ |