diff options
Diffstat (limited to 'core/tee')
-rw-r--r-- | core/tee/fs_htree.c | 937 | ||||
-rw-r--r-- | core/tee/sub.mk | 2 | ||||
-rw-r--r-- | core/tee/tee_cryp_utl.c | 7 | ||||
-rw-r--r-- | core/tee/tee_fs_key_manager.c | 232 | ||||
-rw-r--r-- | core/tee/tee_fs_rpc.c | 95 | ||||
-rw-r--r-- | core/tee/tee_obj.c | 17 | ||||
-rw-r--r-- | core/tee/tee_pobj.c | 14 | ||||
-rw-r--r-- | core/tee/tee_ree_fs.c | 706 | ||||
-rw-r--r-- | core/tee/tee_rpmb_fs.c | 205 | ||||
-rw-r--r-- | core/tee/tee_sql_fs.c | 419 | ||||
-rw-r--r-- | core/tee/tee_svc_storage.c | 346 |
11 files changed, 1568 insertions, 1412 deletions
diff --git a/core/tee/fs_htree.c b/core/tee/fs_htree.c new file mode 100644 index 0000000..53cb81e --- /dev/null +++ b/core/tee/fs_htree.c @@ -0,0 +1,937 @@ +/* + * Copyright (c) 2017, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <assert.h> +#include <initcall.h> +#include <kernel/tee_common_otp.h> +#include <optee_msg_supplicant.h> +#include <stdlib.h> +#include <string_ext.h> +#include <string.h> +#include <tee/fs_htree.h> +#include <tee/tee_cryp_provider.h> +#include <tee/tee_fs_key_manager.h> +#include <tee/tee_fs_rpc.h> +#include <utee_defines.h> +#include <util.h> + +#define TEE_FS_HTREE_CHIP_ID_SIZE 32 +#define TEE_FS_HTREE_HASH_ALG TEE_ALG_SHA256 +#define TEE_FS_HTREE_TSK_SIZE TEE_FS_HTREE_HASH_SIZE +#define TEE_FS_HTREE_ENC_ALG TEE_ALG_AES_ECB_NOPAD +#define TEE_FS_HTREE_ENC_SIZE TEE_AES_BLOCK_SIZE +#define TEE_FS_HTREE_SSK_SIZE TEE_FS_HTREE_HASH_SIZE + +#define TEE_FS_HTREE_AUTH_ENC_ALG TEE_ALG_AES_GCM +#define TEE_FS_HTREE_HMAC_ALG TEE_ALG_HMAC_SHA256 + +#define BLOCK_NUM_TO_NODE_ID(num) ((num) + 1) + +#define NODE_ID_TO_BLOCK_NUM(id) ((id) - 1) + +/* + * The hash tree is implemented as a binary tree with the purpose to ensure + * integrity of the data in the nodes. The data in the nodes their turn + * provides both integrity and confidentiality of the data blocks. + * + * The hash tree is saved in a file as: + * +----------------------------+ + * | htree_image.0 | + * | htree_image.1 | + * +----------------------------+ + * | htree_node_image.1.0 | + * | htree_node_image.1.1 | + * +----------------------------+ + * | htree_node_image.2.0 | + * | htree_node_image.2.1 | + * +----------------------------+ + * | htree_node_image.3.0 | + * | htree_node_image.3.1 | + * +----------------------------+ + * | htree_node_image.4.0 | + * | htree_node_image.4.1 | + * +----------------------------+ + * ... + * + * htree_image is the header of the file, there's two instances of it. One + * which is committed and the other is used when updating the file. Which + * is committed is indicated by the "counter" field, the one with the + * largest value is selected. + * + * htree_node_image is a node in the hash tree, each node has two instances + * which is committed is decided by the parent node .flag bit + * HTREE_NODE_COMMITTED_CHILD. Which version is the committed version of + * node 1 is determined by the by the lowest bit of the counter field in + * the header. + * + * Note that nodes start counting at 1 while blocks at 0, this means that + * block 0 is represented by node 1. + * + * Where different elements are stored in the file is managed by the file + * system. In the case of SQL FS the version of the node/block is ignored + * as the atomic update is finalized with a call to + * tee_fs_rpc_end_transaction(). + */ + +#define HTREE_NODE_COMMITTED_BLOCK BIT32(0) +/* n is 0 or 1 */ +#define HTREE_NODE_COMMITTED_CHILD(n) BIT32(1 + (n)) + +struct htree_node { + size_t id; + bool dirty; + bool block_updated; + struct tee_fs_htree_node_image node; + struct htree_node *parent; + struct htree_node *child[2]; +}; + +struct tee_fs_htree { + struct htree_node root; + struct tee_fs_htree_image head; + uint8_t fek[TEE_FS_HTREE_FEK_SIZE]; + struct tee_fs_htree_imeta imeta; + bool dirty; + const struct tee_fs_htree_storage *stor; + void *stor_aux; +}; + +struct traverse_arg; +typedef TEE_Result (*traverse_cb_t)(struct traverse_arg *targ, + struct htree_node *node); +struct traverse_arg { + struct tee_fs_htree *ht; + traverse_cb_t cb; + void *arg; +}; + +static TEE_Result rpc_read(struct tee_fs_htree *ht, enum tee_fs_htree_type type, + size_t idx, size_t vers, void *data, size_t dlen) +{ + TEE_Result res; + struct tee_fs_rpc_operation op; + size_t bytes; + void *p; + + res = ht->stor->rpc_read_init(ht->stor_aux, &op, type, idx, vers, &p); + if (res != TEE_SUCCESS) + return res; + + res = ht->stor->rpc_read_final(&op, &bytes); + if (res != TEE_SUCCESS) + return res; + + if (bytes != dlen) + return TEE_ERROR_CORRUPT_OBJECT; + + memcpy(data, p, dlen); + return TEE_SUCCESS; +} + +static TEE_Result rpc_read_head(struct tee_fs_htree *ht, size_t vers, + struct tee_fs_htree_image *head) +{ + return rpc_read(ht, TEE_FS_HTREE_TYPE_HEAD, 0, vers, + head, sizeof(*head)); +} + +static TEE_Result rpc_read_node(struct tee_fs_htree *ht, size_t node_id, + size_t vers, + struct tee_fs_htree_node_image *node) +{ + return rpc_read(ht, TEE_FS_HTREE_TYPE_NODE, node_id - 1, vers, + node, sizeof(*node)); +} + +static TEE_Result rpc_write(struct tee_fs_htree *ht, + enum tee_fs_htree_type type, size_t idx, + size_t vers, const void *data, size_t dlen) +{ + TEE_Result res; + struct tee_fs_rpc_operation op; + void *p; + + res = ht->stor->rpc_write_init(ht->stor_aux, &op, type, idx, vers, &p); + if (res != TEE_SUCCESS) + return res; + + memcpy(p, data, dlen); + return ht->stor->rpc_write_final(&op); +} + +static TEE_Result rpc_write_head(struct tee_fs_htree *ht, size_t vers, + const struct tee_fs_htree_image *head) +{ + return rpc_write(ht, TEE_FS_HTREE_TYPE_HEAD, 0, vers, + head, sizeof(*head)); +} + +static TEE_Result rpc_write_node(struct tee_fs_htree *ht, size_t node_id, + size_t vers, + const struct tee_fs_htree_node_image *node) +{ + return rpc_write(ht, TEE_FS_HTREE_TYPE_NODE, node_id - 1, vers, + node, sizeof(*node)); +} + +static TEE_Result traverse_post_order(struct traverse_arg *targ, + struct htree_node *node) +{ + TEE_Result res; + + /* + * This function is recursing but not very deep, only with Log(N) + * maximum depth. + */ + + if (!node) + return TEE_SUCCESS; + + res = traverse_post_order(targ, node->child[0]); + if (res != TEE_SUCCESS) + return res; + + res = traverse_post_order(targ, node->child[1]); + if (res != TEE_SUCCESS) + return res; + + return targ->cb(targ, node); +} + +static TEE_Result htree_traverse_post_order(struct tee_fs_htree *ht, + traverse_cb_t cb, void *arg) +{ + struct traverse_arg targ = { ht, cb, arg }; + + return traverse_post_order(&targ, &ht->root); +} + +static size_t node_id_to_level(size_t node_id) +{ + assert(node_id && node_id < UINT_MAX); + /* Calculate level of the node, root node (1) has level 1 */ + return sizeof(unsigned int) * 8 - __builtin_clz(node_id); +} + +static struct htree_node *find_closest_node(struct tee_fs_htree *ht, + size_t node_id) +{ + struct htree_node *node = &ht->root; + size_t level = node_id_to_level(node_id); + size_t n; + + /* n = 1 because root node is level 1 */ + for (n = 1; n < level; n++) { + struct htree_node *child; + size_t bit_idx; + + /* + * The difference between levels of the current node and + * the node we're looking for tells which bit decides + * direction in the tree. + * + * As the first bit has index 0 we'll subtract 1 + */ + bit_idx = level - n - 1; + child = node->child[((node_id >> bit_idx) & 1)]; + if (!child) + return node; + node = child; + } + + return node; +} + +static struct htree_node *find_node(struct tee_fs_htree *ht, size_t node_id) +{ + struct htree_node *node = find_closest_node(ht, node_id); + + if (node && node->id == node_id) + return node; + return NULL; +} + +static TEE_Result get_node(struct tee_fs_htree *ht, bool create, + size_t node_id, struct htree_node **node_ret) +{ + struct htree_node *node; + struct htree_node *nc; + size_t n; + + node = find_closest_node(ht, node_id); + if (!node) + return TEE_ERROR_GENERIC; + if (node->id == node_id) + goto ret_node; + + /* + * Trying to read beyond end of file should be caught earlier than + * here. + */ + if (!create) + return TEE_ERROR_GENERIC; + + /* + * Add missing nodes, some nodes may already be there. When we've + * processed the range all nodes up to node_id will be in the tree. + */ + for (n = node->id + 1; n <= node_id; n++) { + node = find_closest_node(ht, n); + if (node->id == n) + continue; + /* Node id n should be a child of node */ + assert((n >> 1) == node->id); + assert(!node->child[n & 1]); + + nc = calloc(1, sizeof(*nc)); + if (!nc) + return TEE_ERROR_OUT_OF_MEMORY; + nc->id = n; + nc->parent = node; + node->child[n & 1] = nc; + node = nc; + } + + if (node->id > ht->imeta.max_node_id) + ht->imeta.max_node_id = node->id; + +ret_node: + *node_ret = node; + return TEE_SUCCESS; +} + +static int get_idx_from_counter(uint32_t counter0, uint32_t counter1) +{ + if (!(counter0 & 1)) { + if (!(counter1 & 1)) + return 0; + if (counter0 > counter1) + return 0; + else + return 1; + } + + if (counter1 & 1) + return 1; + else + return -1; +} + +static TEE_Result init_head_from_data(struct tee_fs_htree *ht) +{ + TEE_Result res; + struct tee_fs_htree_image head[2]; + int idx; + + for (idx = 0; idx < 2; idx++) { + res = rpc_read_head(ht, idx, head + idx); + if (res != TEE_SUCCESS) + return res; + } + + idx = get_idx_from_counter(head[0].counter, head[1].counter); + if (idx < 0) + return TEE_ERROR_SECURITY; + + res = rpc_read_node(ht, 1, idx, &ht->root.node); + if (res != TEE_SUCCESS) + return res; + + ht->head = head[idx]; + ht->root.id = 1; + + return TEE_SUCCESS; +} + +static TEE_Result init_tree_from_data(struct tee_fs_htree *ht) +{ + TEE_Result res; + struct tee_fs_htree_node_image node_image; + struct htree_node *node; + struct htree_node *nc; + size_t committed_version; + size_t node_id = 2; + + while (node_id <= ht->imeta.max_node_id) { + node = find_node(ht, node_id >> 1); + if (!node) + return TEE_ERROR_GENERIC; + committed_version = !!(node->node.flags & + HTREE_NODE_COMMITTED_CHILD(node_id & 1)); + + res = rpc_read_node(ht, node_id, committed_version, + &node_image); + if (res != TEE_SUCCESS) + return res; + + res = get_node(ht, true, node_id, &nc); + if (res != TEE_SUCCESS) + return res; + nc->node = node_image; + node_id++; + } + + return TEE_SUCCESS; +} + +static TEE_Result calc_node_hash(struct htree_node *node, void *ctx, + uint8_t *digest) +{ + TEE_Result res; + uint32_t alg = TEE_FS_HTREE_HASH_ALG; + uint8_t *ndata = (uint8_t *)&node->node + sizeof(node->node.hash); + size_t nsize = sizeof(node->node) - sizeof(node->node.hash); + + res = crypto_ops.hash.init(ctx, alg); + if (res != TEE_SUCCESS) + return res; + + res = crypto_ops.hash.update(ctx, alg, ndata, nsize); + if (res != TEE_SUCCESS) + return res; + + if (node->child[0]) { + res = crypto_ops.hash.update(ctx, alg, + node->child[0]->node.hash, + sizeof(node->child[0]->node.hash)); + if (res != TEE_SUCCESS) + return res; + } + + if (node->child[1]) { + res = crypto_ops.hash.update(ctx, alg, + node->child[1]->node.hash, + sizeof(node->child[1]->node.hash)); + if (res != TEE_SUCCESS) + return res; + } + + return crypto_ops.hash.final(ctx, alg, digest, TEE_FS_HTREE_HASH_SIZE); +} + +static TEE_Result authenc_init(void **ctx_ret, TEE_OperationMode mode, + struct tee_fs_htree *ht, + struct tee_fs_htree_node_image *ni, + size_t payload_len) +{ + TEE_Result res = TEE_SUCCESS; + const uint32_t alg = TEE_FS_HTREE_AUTH_ENC_ALG; + uint8_t *ctx; + size_t ctx_size; + size_t aad_len = TEE_FS_HTREE_FEK_SIZE + TEE_FS_HTREE_IV_SIZE; + uint8_t *iv; + + if (ni) { + iv = ni->iv; + } else { + iv = ht->head.iv; + aad_len += TEE_FS_HTREE_HASH_SIZE + sizeof(ht->head.counter); + } + + if (mode == TEE_MODE_ENCRYPT) { + res = crypto_ops.prng.read(iv, TEE_FS_HTREE_IV_SIZE); + if (res != TEE_SUCCESS) + return res; + } + + res = crypto_ops.authenc.get_ctx_size(alg, &ctx_size); + if (res != TEE_SUCCESS) + return res; + + ctx = malloc(ctx_size); + if (!ctx) { + EMSG("request memory size %zu failed", ctx_size); + return TEE_ERROR_OUT_OF_MEMORY; + } + + res = crypto_ops.authenc.init(ctx, alg, mode, + ht->fek, TEE_FS_HTREE_FEK_SIZE, + iv, TEE_FS_HTREE_IV_SIZE, + TEE_FS_HTREE_TAG_SIZE, aad_len, + payload_len); + if (res != TEE_SUCCESS) + goto exit; + + if (!ni) { + res = crypto_ops.authenc.update_aad(ctx, alg, mode, + ht->root.node.hash, + TEE_FS_HTREE_FEK_SIZE); + if (res != TEE_SUCCESS) + goto exit; + + res = crypto_ops.authenc.update_aad(ctx, alg, mode, + (void *)&ht->head.counter, + sizeof(ht->head.counter)); + if (res != TEE_SUCCESS) + goto exit; + } + + res = crypto_ops.authenc.update_aad(ctx, alg, mode, ht->head.enc_fek, + TEE_FS_HTREE_FEK_SIZE); + if (res != TEE_SUCCESS) + goto exit; + + res = crypto_ops.authenc.update_aad(ctx, alg, mode, iv, + TEE_FS_HTREE_IV_SIZE); + +exit: + if (res == TEE_SUCCESS) + *ctx_ret = ctx; + else + free(ctx); + + return res; +} + +static TEE_Result authenc_decrypt_final(void *ctx, const uint8_t *tag, + const void *crypt, size_t len, + void *plain) +{ + TEE_Result res; + size_t out_size = len; + + res = crypto_ops.authenc.dec_final(ctx, TEE_FS_HTREE_AUTH_ENC_ALG, + crypt, len, plain, &out_size, + tag, TEE_FS_HTREE_TAG_SIZE); + crypto_ops.authenc.final(ctx, TEE_FS_HTREE_AUTH_ENC_ALG); + free(ctx); + + if (res == TEE_SUCCESS && out_size != len) + return TEE_ERROR_GENERIC; + if (res == TEE_ERROR_MAC_INVALID) + return TEE_ERROR_CORRUPT_OBJECT; + + return res; +} + +static TEE_Result authenc_encrypt_final(void *ctx, uint8_t *tag, + const void *plain, size_t len, + void *crypt) +{ + TEE_Result res; + size_t out_size = len; + size_t out_tag_size = TEE_FS_HTREE_TAG_SIZE; + + res = crypto_ops.authenc.enc_final(ctx, TEE_FS_HTREE_AUTH_ENC_ALG, + plain, len, crypt, &out_size, + tag, &out_tag_size); + crypto_ops.authenc.final(ctx, TEE_FS_HTREE_AUTH_ENC_ALG); + free(ctx); + + if (res == TEE_SUCCESS && + (out_size != len || out_tag_size != TEE_FS_HTREE_TAG_SIZE)) + return TEE_ERROR_GENERIC; + + return res; +} + +static TEE_Result verify_root(struct tee_fs_htree *ht) +{ + TEE_Result res; + void *ctx; + + res = tee_fs_fek_crypt(TEE_MODE_DECRYPT, ht->head.enc_fek, + sizeof(ht->fek), ht->fek); + if (res != TEE_SUCCESS) + return res; + + res = authenc_init(&ctx, TEE_MODE_DECRYPT, ht, NULL, sizeof(ht->imeta)); + if (res != TEE_SUCCESS) + return res; + + return authenc_decrypt_final(ctx, ht->head.tag, ht->head.imeta, + sizeof(ht->imeta), &ht->imeta); +} + +static TEE_Result verify_node(struct traverse_arg *targ, + struct htree_node *node) +{ + void *ctx = targ->arg; + TEE_Result res; + uint8_t digest[TEE_FS_HTREE_HASH_SIZE]; + + res = calc_node_hash(node, ctx, digest); + if (res == TEE_SUCCESS && + buf_compare_ct(digest, node->node.hash, sizeof(digest))) + return TEE_ERROR_CORRUPT_OBJECT; + + return res; +} + +static TEE_Result verify_tree(struct tee_fs_htree *ht) +{ + TEE_Result res; + size_t size; + void *ctx; + + if (!crypto_ops.hash.get_ctx_size || !crypto_ops.hash.init || + !crypto_ops.hash.update || !crypto_ops.hash.final) + return TEE_ERROR_NOT_SUPPORTED; + + res = crypto_ops.hash.get_ctx_size(TEE_FS_HTREE_HASH_ALG, &size); + if (res != TEE_SUCCESS) + return res; + + ctx = malloc(size); + if (!ctx) + return TEE_ERROR_OUT_OF_MEMORY; + + res = htree_traverse_post_order(ht, verify_node, ctx); + free(ctx); + + return res; +} + +static TEE_Result init_root_node(struct tee_fs_htree *ht) +{ + TEE_Result res; + size_t size; + void *ctx; + + res = crypto_ops.hash.get_ctx_size(TEE_FS_HTREE_HASH_ALG, &size); + if (res != TEE_SUCCESS) + return res; + ctx = malloc(size); + if (!ctx) + return TEE_ERROR_OUT_OF_MEMORY; + + ht->root.id = 1; + + res = calc_node_hash(&ht->root, ctx, ht->root.node.hash); + free(ctx); + + return res; +} + +TEE_Result tee_fs_htree_open(bool create, + const struct tee_fs_htree_storage *stor, + void *stor_aux, struct tee_fs_htree **ht_ret) +{ + TEE_Result res; + struct tee_fs_htree *ht = calloc(1, sizeof(*ht)); + + if (!ht) + return TEE_ERROR_OUT_OF_MEMORY; + + ht->stor = stor; + ht->stor_aux = stor_aux; + + if (create) { + const struct tee_fs_htree_image dummy_head = { .counter = 0 }; + + res = crypto_ops.prng.read(ht->fek, sizeof(ht->fek)); + if (res != TEE_SUCCESS) + goto out; + + res = tee_fs_fek_crypt(TEE_MODE_ENCRYPT, ht->fek, + sizeof(ht->fek), ht->head.enc_fek); + if (res != TEE_SUCCESS) + goto out; + + res = init_root_node(ht); + if (res != TEE_SUCCESS) + goto out; + + ht->dirty = true; + res = tee_fs_htree_sync_to_storage(&ht); + if (res != TEE_SUCCESS) + goto out; + res = rpc_write_head(ht, 0, &dummy_head); + } else { + res = init_head_from_data(ht); + if (res != TEE_SUCCESS) + goto out; + + res = verify_root(ht); + if (res != TEE_SUCCESS) + goto out; + + res = init_tree_from_data(ht); + if (res != TEE_SUCCESS) + goto out; + + res = verify_tree(ht); + } +out: + if (res == TEE_SUCCESS) + *ht_ret = ht; + else + tee_fs_htree_close(&ht); + return res; +} + +struct tee_fs_htree_meta *tee_fs_htree_get_meta(struct tee_fs_htree *ht) +{ + return &ht->imeta.meta; +} + +static TEE_Result free_node(struct traverse_arg *targ __unused, + struct htree_node *node) +{ + if (node->parent) + free(node); + return TEE_SUCCESS; +} + +void tee_fs_htree_close(struct tee_fs_htree **ht) +{ + if (!*ht) + return; + htree_traverse_post_order(*ht, free_node, NULL); + free(*ht); + *ht = NULL; +} + +static TEE_Result htree_sync_node_to_storage(struct traverse_arg *targ, + struct htree_node *node) +{ + TEE_Result res; + uint8_t vers; + + /* + * The node can be dirty while the block isn't updated due to + * updated children, but if block is updated the node has to be + * dirty. + */ + assert(node->dirty >= node->block_updated); + + if (!node->dirty) + return TEE_SUCCESS; + + if (node->parent) { + uint32_t f = HTREE_NODE_COMMITTED_CHILD(node->id & 1); + + node->parent->dirty = true; + node->parent->node.flags ^= f; + vers = !!(node->parent->node.flags & f); + } else { + /* + * Counter isn't updated yet, it's increased just before + * writing the header. + */ + vers = !(targ->ht->head.counter & 1); + } + + res = calc_node_hash(node, targ->arg, node->node.hash); + if (res != TEE_SUCCESS) + return res; + + node->dirty = false; + node->block_updated = false; + + return rpc_write_node(targ->ht, node->id, vers, &node->node); +} + +static TEE_Result update_root(struct tee_fs_htree *ht) +{ + TEE_Result res; + void *ctx; + + ht->head.counter++; + + res = authenc_init(&ctx, TEE_MODE_ENCRYPT, ht, NULL, sizeof(ht->imeta)); + if (res != TEE_SUCCESS) + return res; + + return authenc_encrypt_final(ctx, ht->head.tag, &ht->imeta, + sizeof(ht->imeta), &ht->head.imeta); +} + +TEE_Result tee_fs_htree_sync_to_storage(struct tee_fs_htree **ht_arg) +{ + TEE_Result res; + struct tee_fs_htree *ht = *ht_arg; + size_t size; + void *ctx; + + if (!ht) + return TEE_ERROR_CORRUPT_OBJECT; + + if (!ht->dirty) + return TEE_SUCCESS; + + res = crypto_ops.hash.get_ctx_size(TEE_FS_HTREE_HASH_ALG, &size); + if (res != TEE_SUCCESS) + return res; + ctx = malloc(size); + if (!ctx) + return TEE_ERROR_OUT_OF_MEMORY; + + res = htree_traverse_post_order(ht, htree_sync_node_to_storage, ctx); + if (res != TEE_SUCCESS) + goto out; + + /* All the nodes are written to storage now. Time to update root. */ + res = update_root(ht); + if (res != TEE_SUCCESS) + goto out; + + res = rpc_write_head(ht, ht->head.counter & 1, &ht->head); + if (res != TEE_SUCCESS) + goto out; + + ht->dirty = false; +out: + free(ctx); + if (res != TEE_SUCCESS) + tee_fs_htree_close(ht_arg); + return res; +} + +static TEE_Result get_block_node(struct tee_fs_htree *ht, bool create, + size_t block_num, struct htree_node **node) +{ + TEE_Result res; + struct htree_node *nd; + + res = get_node(ht, create, BLOCK_NUM_TO_NODE_ID(block_num), &nd); + if (res == TEE_SUCCESS) + *node = nd; + + return res; +} + +TEE_Result tee_fs_htree_write_block(struct tee_fs_htree **ht_arg, + size_t block_num, const void *block) +{ + struct tee_fs_htree *ht = *ht_arg; + TEE_Result res; + struct tee_fs_rpc_operation op; + struct htree_node *node = NULL; + uint8_t block_vers; + void *ctx; + void *enc_block; + + if (!ht) + return TEE_ERROR_CORRUPT_OBJECT; + + res = get_block_node(ht, true, block_num, &node); + if (res != TEE_SUCCESS) + goto out; + + if (!node->block_updated) + node->node.flags ^= HTREE_NODE_COMMITTED_BLOCK; + + block_vers = !!(node->node.flags & HTREE_NODE_COMMITTED_BLOCK); + res = ht->stor->rpc_write_init(ht->stor_aux, &op, + TEE_FS_HTREE_TYPE_BLOCK, block_num, + block_vers, &enc_block); + if (res != TEE_SUCCESS) + goto out; + + res = authenc_init(&ctx, TEE_MODE_ENCRYPT, ht, &node->node, + ht->stor->block_size); + if (res != TEE_SUCCESS) + goto out; + res = authenc_encrypt_final(ctx, node->node.tag, block, + ht->stor->block_size, enc_block); + if (res != TEE_SUCCESS) + goto out; + + res = ht->stor->rpc_write_final(&op); + if (res != TEE_SUCCESS) + goto out; + + node->block_updated = true; + node->dirty = true; + ht->dirty = true; +out: + if (res != TEE_SUCCESS) + tee_fs_htree_close(ht_arg); + return res; +} + +TEE_Result tee_fs_htree_read_block(struct tee_fs_htree **ht_arg, + size_t block_num, void *block) +{ + struct tee_fs_htree *ht = *ht_arg; + TEE_Result res; + struct tee_fs_rpc_operation op; + struct htree_node *node; + uint8_t block_vers; + size_t len; + void *ctx; + void *enc_block; + + if (!ht) + return TEE_ERROR_CORRUPT_OBJECT; + + res = get_block_node(ht, false, block_num, &node); + if (res != TEE_SUCCESS) + goto out; + + block_vers = !!(node->node.flags & HTREE_NODE_COMMITTED_BLOCK); + res = ht->stor->rpc_read_init(ht->stor_aux, &op, + TEE_FS_HTREE_TYPE_BLOCK, block_num, + block_vers, &enc_block); + if (res != TEE_SUCCESS) + goto out; + + res = ht->stor->rpc_read_final(&op, &len); + if (res != TEE_SUCCESS) + goto out; + if (len != ht->stor->block_size) { + res = TEE_ERROR_CORRUPT_OBJECT; + goto out; + } + + res = authenc_init(&ctx, TEE_MODE_DECRYPT, ht, &node->node, + ht->stor->block_size); + if (res != TEE_SUCCESS) + goto out; + + res = authenc_decrypt_final(ctx, node->node.tag, enc_block, + ht->stor->block_size, block); +out: + if (res != TEE_SUCCESS) + tee_fs_htree_close(ht_arg); + return res; +} + +TEE_Result tee_fs_htree_truncate(struct tee_fs_htree **ht_arg, size_t block_num) +{ + struct tee_fs_htree *ht = *ht_arg; + size_t node_id = BLOCK_NUM_TO_NODE_ID(block_num); + struct htree_node *node; + + if (!ht) + return TEE_ERROR_CORRUPT_OBJECT; + + while (node_id < ht->imeta.max_node_id) { + node = find_closest_node(ht, ht->imeta.max_node_id); + assert(node && node->id == ht->imeta.max_node_id); + assert(!node->child[0] && !node->child[1]); + assert(node->parent); + assert(node->parent->child[node->id & 1] == node); + node->parent->child[node->id & 1] = NULL; + free(node); + ht->imeta.max_node_id--; + ht->dirty = true; + } + + return TEE_SUCCESS; +} diff --git a/core/tee/sub.mk b/core/tee/sub.mk index 32f0f98..97c4b7a 100644 --- a/core/tee/sub.mk +++ b/core/tee/sub.mk @@ -31,6 +31,8 @@ srcs-y += tee_svc_storage.c srcs-$(CFG_RPMB_FS) += tee_rpmb_fs.c srcs-$(CFG_REE_FS) += tee_ree_fs.c srcs-$(CFG_SQL_FS) += tee_sql_fs.c +srcs-$(call cfg-one-enabled,CFG_REE_FS CFG_SQL_FS \ + CFG_TEE_CORE_EMBED_INTERNAL_TESTS) += fs_htree.c srcs-$(call cfg-one-enabled,CFG_REE_FS CFG_SQL_FS) += tee_fs_rpc.c srcs-$(call cfg-one-enabled,CFG_REE_FS CFG_SQL_FS CFG_RPMB_FS) += \ tee_fs_rpc_cache.c diff --git a/core/tee/tee_cryp_utl.c b/core/tee/tee_cryp_utl.c index fa01161..b63e790 100644 --- a/core/tee/tee_cryp_utl.c +++ b/core/tee/tee_cryp_utl.c @@ -392,6 +392,13 @@ __weak void plat_prng_add_jitter_entropy(void) tee_prng_add_entropy((uint8_t *)¤t, sizeof(current)); } +__weak void plat_prng_add_jitter_entropy_norpc(void) +{ +#ifndef CFG_SECURE_TIME_SOURCE_REE + plat_prng_add_jitter_entropy(); +#endif +} + static TEE_Result tee_cryp_init(void) { if (crypto_ops.init) diff --git a/core/tee/tee_fs_key_manager.c b/core/tee/tee_fs_key_manager.c index c827cef..fa579c6 100644 --- a/core/tee/tee_fs_key_manager.c +++ b/core/tee/tee_fs_key_manager.c @@ -55,16 +55,6 @@ struct tee_fs_ssk { uint8_t key[TEE_FS_KM_SSK_SIZE]; }; -struct aad { - const uint8_t *encrypted_key; - const uint8_t *iv; -}; - -struct km_header { - struct aad aad; - uint8_t *tag; -}; - static struct tee_fs_ssk tee_fs_ssk; static uint8_t string_for_ssk_gen[] = "ONLY_FOR_tee_fs_ssk"; @@ -109,17 +99,17 @@ exit: return res; } -static TEE_Result fek_crypt(TEE_OperationMode mode, - uint8_t *key, int size) +TEE_Result tee_fs_fek_crypt(TEE_OperationMode mode, const uint8_t *in_key, + size_t size, uint8_t *out_key) { TEE_Result res; uint8_t *ctx = NULL; size_t ctx_size; uint8_t tsk[TEE_FS_KM_TSK_SIZE]; - uint8_t dst_key[TEE_FS_KM_FEK_SIZE]; + uint8_t dst_key[size]; struct tee_ta_session *sess; - if (!key) + if (!in_key || !out_key) return TEE_ERROR_BAD_PARAMETERS; if (size != TEE_FS_KM_FEK_SIZE) @@ -151,13 +141,13 @@ static TEE_Result fek_crypt(TEE_OperationMode mode, goto exit; res = crypto_ops.cipher.update(ctx, TEE_FS_KM_ENC_FEK_ALG, - mode, true, key, size, dst_key); + mode, true, in_key, size, dst_key); if (res != TEE_SUCCESS) goto exit; crypto_ops.cipher.final(ctx, TEE_FS_KM_ENC_FEK_ALG); - memcpy(key, dst_key, sizeof(dst_key)); + memcpy(out_key, dst_key, sizeof(dst_key)); exit: free(ctx); @@ -170,11 +160,6 @@ static TEE_Result generate_fek(uint8_t *key, uint8_t len) return crypto_ops.prng.read(key, len); } -static TEE_Result generate_iv(uint8_t *iv, uint8_t len) -{ - return crypto_ops.prng.read(iv, len); -} - static TEE_Result tee_fs_init_key_manager(void) { int res = TEE_SUCCESS; @@ -204,94 +189,6 @@ static TEE_Result tee_fs_init_key_manager(void) return res; } -static TEE_Result do_auth_enc(TEE_OperationMode mode, - struct km_header *hdr, - uint8_t *fek, int fek_len, - const uint8_t *data_in, size_t in_size, - uint8_t *data_out, size_t *out_size) -{ - TEE_Result res = TEE_SUCCESS; - uint8_t *ctx = NULL; - size_t ctx_size; - size_t tag_len = TEE_FS_KM_MAX_TAG_LEN; - - if ((mode != TEE_MODE_ENCRYPT) && (mode != TEE_MODE_DECRYPT)) - return TEE_ERROR_BAD_PARAMETERS; - - if (*out_size < in_size) { - EMSG("output buffer(%zd) < input buffer(%zd)", - *out_size, in_size); - return TEE_ERROR_SHORT_BUFFER; - } - - res = crypto_ops.authenc.get_ctx_size(TEE_FS_KM_AUTH_ENC_ALG, - &ctx_size); - if (res != TEE_SUCCESS) - return res; - - ctx = malloc(ctx_size); - if (!ctx) { - EMSG("request memory size %zu failed", ctx_size); - return TEE_ERROR_OUT_OF_MEMORY; - } - - res = crypto_ops.authenc.init(ctx, TEE_FS_KM_AUTH_ENC_ALG, - mode, fek, fek_len, hdr->aad.iv, - TEE_FS_KM_IV_LEN, TEE_FS_KM_MAX_TAG_LEN, - sizeof(struct aad), in_size); - if (res != TEE_SUCCESS) - goto exit; - - res = crypto_ops.authenc.update_aad(ctx, TEE_FS_KM_AUTH_ENC_ALG, - mode, (uint8_t *)hdr->aad.encrypted_key, - TEE_FS_KM_FEK_SIZE); - if (res != TEE_SUCCESS) - goto exit; - - res = crypto_ops.authenc.update_aad(ctx, TEE_FS_KM_AUTH_ENC_ALG, - mode, (uint8_t *)hdr->aad.iv, - TEE_FS_KM_IV_LEN); - if (res != TEE_SUCCESS) - goto exit; - - if (mode == TEE_MODE_ENCRYPT) { - res = crypto_ops.authenc.enc_final(ctx, TEE_FS_KM_AUTH_ENC_ALG, - data_in, in_size, data_out, out_size, - hdr->tag, &tag_len); - } else { - res = crypto_ops.authenc.dec_final(ctx, TEE_FS_KM_AUTH_ENC_ALG, - data_in, in_size, data_out, out_size, - hdr->tag, tag_len); - } - - if (res != TEE_SUCCESS) - goto exit; - - crypto_ops.authenc.final(ctx, TEE_FS_KM_AUTH_ENC_ALG); - -exit: - free(ctx); - return res; -} - -size_t tee_fs_get_header_size(enum tee_fs_file_type type) -{ - size_t header_size = 0; - - switch (type) { - case META_FILE: - header_size = sizeof(struct meta_header); - break; - case BLOCK_FILE: - header_size = sizeof(struct block_header); - break; - default: - panic("Unknown file type"); - } - - return header_size; -} - TEE_Result tee_fs_generate_fek(uint8_t *buf, int buf_size) { TEE_Result res; @@ -303,117 +200,7 @@ TEE_Result tee_fs_generate_fek(uint8_t *buf, int buf_size) if (res != TEE_SUCCESS) return res; - return fek_crypt(TEE_MODE_ENCRYPT, buf, - TEE_FS_KM_FEK_SIZE); -} - -TEE_Result tee_fs_encrypt_file(enum tee_fs_file_type file_type, - const uint8_t *data_in, size_t data_in_size, - uint8_t *data_out, size_t *data_out_size, - const uint8_t *encrypted_fek) -{ - TEE_Result res = TEE_SUCCESS; - struct km_header hdr; - uint8_t iv[TEE_FS_KM_IV_LEN]; - uint8_t tag[TEE_FS_KM_MAX_TAG_LEN]; - uint8_t fek[TEE_FS_KM_FEK_SIZE]; - uint8_t *ciphertext; - size_t cipher_size; - size_t header_size = tee_fs_get_header_size(file_type); - - /* - * Meta File Format: |Header|Chipertext| - * Header Format: |AAD|Tag| - * AAD Format: |Encrypted_FEK|IV| - * - * Block File Format: |Header|Ciphertext| - * Header Format: |IV|Tag| - * - * TSK = HMAC(SSK, TA_UUID) - * FEK = AES_DECRYPT(TSK, Encrypted_FEK) - * Chipertext = AES_GCM_ENCRYPT(FEK, IV, Meta_Info, AAD) - */ - - if (*data_out_size != (header_size + data_in_size)) - return TEE_ERROR_SHORT_BUFFER; - - res = generate_iv(iv, TEE_FS_KM_IV_LEN); - if (res != TEE_SUCCESS) - goto fail; - - memcpy(fek, encrypted_fek, TEE_FS_KM_FEK_SIZE); - res = fek_crypt(TEE_MODE_DECRYPT, fek, TEE_FS_KM_FEK_SIZE); - if (res != TEE_SUCCESS) - goto fail; - - ciphertext = data_out + header_size; - cipher_size = data_in_size; - - hdr.aad.iv = iv; - hdr.aad.encrypted_key = encrypted_fek; - hdr.tag = tag; - - res = do_auth_enc(TEE_MODE_ENCRYPT, &hdr, - fek, TEE_FS_KM_FEK_SIZE, - data_in, data_in_size, - ciphertext, &cipher_size); - - if (res == TEE_SUCCESS) { - if (file_type == META_FILE) { - memcpy(data_out, encrypted_fek, TEE_FS_KM_FEK_SIZE); - data_out += TEE_FS_KM_FEK_SIZE; - } - - memcpy(data_out, iv, TEE_FS_KM_IV_LEN); - data_out += TEE_FS_KM_IV_LEN; - memcpy(data_out, tag, TEE_FS_KM_MAX_TAG_LEN); - - *data_out_size = header_size + cipher_size; - } - -fail: - return res; -} - -TEE_Result tee_fs_decrypt_file(enum tee_fs_file_type file_type, - const uint8_t *data_in, size_t data_in_size, - uint8_t *plaintext, size_t *plaintext_size, - uint8_t *encrypted_fek) -{ - TEE_Result res = TEE_SUCCESS; - struct km_header km_hdr; - size_t file_hdr_size = tee_fs_get_header_size(file_type); - const uint8_t *cipher = data_in + file_hdr_size; - int cipher_size = data_in_size - file_hdr_size; - uint8_t fek[TEE_FS_KM_FEK_SIZE]; - - if (file_type == META_FILE) { - struct meta_header *hdr = (struct meta_header *)data_in; - - km_hdr.aad.encrypted_key = hdr->encrypted_key; - km_hdr.aad.iv = hdr->common.iv; - km_hdr.tag = hdr->common.tag; - - /* return encrypted FEK to tee_fs which is used for block - * encryption/decryption */ - memcpy(encrypted_fek, hdr->encrypted_key, TEE_FS_KM_FEK_SIZE); - } else { - struct block_header *hdr = (struct block_header *)data_in; - - km_hdr.aad.encrypted_key = encrypted_fek; - km_hdr.aad.iv = hdr->common.iv; - km_hdr.tag = hdr->common.tag; - } - - memcpy(fek, km_hdr.aad.encrypted_key, TEE_FS_KM_FEK_SIZE); - res = fek_crypt(TEE_MODE_DECRYPT, fek, TEE_FS_KM_FEK_SIZE); - if (res != TEE_SUCCESS) { - EMSG("Failed to decrypt FEK, res=0x%x", res); - return res; - } - - return do_auth_enc(TEE_MODE_DECRYPT, &km_hdr, fek, TEE_FS_KM_FEK_SIZE, - cipher, cipher_size, plaintext, plaintext_size); + return tee_fs_fek_crypt(TEE_MODE_ENCRYPT, buf, TEE_FS_KM_FEK_SIZE, buf); } static TEE_Result sha256(uint8_t *out, size_t out_size, const uint8_t *in, @@ -518,8 +305,8 @@ TEE_Result tee_fs_crypt_block(uint8_t *out, const uint8_t *in, size_t size, blk_idx); /* Decrypt FEK */ - memcpy(fek, encrypted_fek, TEE_FS_KM_FEK_SIZE); - res = fek_crypt(TEE_MODE_DECRYPT, fek, TEE_FS_KM_FEK_SIZE); + res = tee_fs_fek_crypt(TEE_MODE_DECRYPT, encrypted_fek, + TEE_FS_KM_FEK_SIZE, fek); if (res != TEE_SUCCESS) return res; @@ -550,4 +337,3 @@ exit: } service_init_late(tee_fs_init_key_manager); - diff --git a/core/tee/tee_fs_rpc.c b/core/tee/tee_fs_rpc.c index 5e1078a..3ce1ba8 100644 --- a/core/tee/tee_fs_rpc.c +++ b/core/tee/tee_fs_rpc.c @@ -26,14 +26,17 @@ */ #include <assert.h> +#include <kernel/tee_misc.h> #include <kernel/thread.h> #include <mm/core_memprot.h> #include <optee_msg_supplicant.h> #include <stdlib.h> -#include <string.h> #include <string_ext.h> +#include <string.h> #include <tee/tee_fs.h> #include <tee/tee_fs_rpc.h> +#include <tee/tee_pobj.h> +#include <tee/tee_svc_storage.h> #include <trace.h> #include <util.h> @@ -48,16 +51,15 @@ static TEE_Result operation_commit(struct tee_fs_rpc_operation *op) } static TEE_Result operation_open(uint32_t id, unsigned int cmd, - const char *fname, int *fd) + struct tee_pobj *po, int *fd) { struct tee_fs_rpc_operation op = { .id = id, .num_params = 3 }; TEE_Result res; void *va; paddr_t pa; uint64_t cookie; - size_t fname_size = strlen(fname) + 1; - va = tee_fs_rpc_cache_alloc(fname_size, &pa, &cookie); + va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &pa, &cookie); if (!va) return TEE_ERROR_OUT_OF_MEMORY; @@ -66,9 +68,12 @@ static TEE_Result operation_open(uint32_t id, unsigned int cmd, op.params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; op.params[1].u.tmem.buf_ptr = pa; - op.params[1].u.tmem.size = fname_size; + op.params[1].u.tmem.size = TEE_FS_NAME_MAX; op.params[1].u.tmem.shm_ref = cookie; - strlcpy(va, fname, fname_size); + res = tee_svc_storage_create_filename(va, TEE_FS_NAME_MAX, + po, po->temporary); + if (res != TEE_SUCCESS) + return res; op.params[2].attr = OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT; @@ -79,14 +84,14 @@ static TEE_Result operation_open(uint32_t id, unsigned int cmd, return res; } -TEE_Result tee_fs_rpc_open(uint32_t id, const char *fname, int *fd) +TEE_Result tee_fs_rpc_open(uint32_t id, struct tee_pobj *po, int *fd) { - return operation_open(id, OPTEE_MRF_OPEN, fname, fd); + return operation_open(id, OPTEE_MRF_OPEN, po, fd); } -TEE_Result tee_fs_rpc_create(uint32_t id, const char *fname, int *fd) +TEE_Result tee_fs_rpc_create(uint32_t id, struct tee_pobj *po, int *fd) { - return operation_open(id, OPTEE_MRF_CREATE, fname, fd); + return operation_open(id, OPTEE_MRF_CREATE, po, fd); } TEE_Result tee_fs_rpc_close(uint32_t id, int fd) @@ -196,15 +201,15 @@ TEE_Result tee_fs_rpc_truncate(uint32_t id, int fd, size_t len) return operation_commit(&op); } -TEE_Result tee_fs_rpc_remove(uint32_t id, const char *fname) +TEE_Result tee_fs_rpc_remove(uint32_t id, struct tee_pobj *po) { + TEE_Result res; struct tee_fs_rpc_operation op = { .id = id, .num_params = 2 }; void *va; paddr_t pa; uint64_t cookie; - size_t name_len = strlen(fname) + 1; - va = tee_fs_rpc_cache_alloc(name_len, &pa, &cookie); + va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &pa, &cookie); if (!va) return TEE_ERROR_OUT_OF_MEMORY; @@ -213,25 +218,27 @@ TEE_Result tee_fs_rpc_remove(uint32_t id, const char *fname) op.params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; op.params[1].u.tmem.buf_ptr = pa; - op.params[1].u.tmem.size = name_len; + op.params[1].u.tmem.size = TEE_FS_NAME_MAX; op.params[1].u.tmem.shm_ref = cookie; - strlcpy(va, fname, name_len); + res = tee_svc_storage_create_filename(va, TEE_FS_NAME_MAX, + po, po->temporary); + if (res != TEE_SUCCESS) + return res; return operation_commit(&op); } -TEE_Result tee_fs_rpc_rename(uint32_t id, const char *old_fname, - const char *new_fname, bool overwrite) +TEE_Result tee_fs_rpc_rename(uint32_t id, struct tee_pobj *old, + struct tee_pobj *new, bool overwrite) { + TEE_Result res; struct tee_fs_rpc_operation op = { .id = id, .num_params = 3 }; char *va; paddr_t pa; uint64_t cookie; - size_t old_fname_size = strlen(old_fname) + 1; - size_t new_fname_size = strlen(new_fname) + 1; + bool temp; - va = tee_fs_rpc_cache_alloc(old_fname_size + new_fname_size, - &pa, &cookie); + va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX * 2, &pa, &cookie); if (!va) return TEE_ERROR_OUT_OF_MEMORY; @@ -241,20 +248,37 @@ TEE_Result tee_fs_rpc_rename(uint32_t id, const char *old_fname, op.params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; op.params[1].u.tmem.buf_ptr = pa; - op.params[1].u.tmem.size = old_fname_size; + op.params[1].u.tmem.size = TEE_FS_NAME_MAX; op.params[1].u.tmem.shm_ref = cookie; - strlcpy(va, old_fname, old_fname_size); + if (new) + temp = old->temporary; + else + temp = true; + res = tee_svc_storage_create_filename(va, TEE_FS_NAME_MAX, + old, temp); + if (res != TEE_SUCCESS) + return res; op.params[2].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; - op.params[2].u.tmem.buf_ptr = pa + old_fname_size; - op.params[2].u.tmem.size = new_fname_size; + op.params[2].u.tmem.buf_ptr = pa + TEE_FS_NAME_MAX; + op.params[2].u.tmem.size = TEE_FS_NAME_MAX; op.params[2].u.tmem.shm_ref = cookie; - strlcpy(va + old_fname_size, new_fname, new_fname_size); + if (new) { + res = tee_svc_storage_create_filename(va + TEE_FS_NAME_MAX, + TEE_FS_NAME_MAX, + new, new->temporary); + } else { + res = tee_svc_storage_create_filename(va + TEE_FS_NAME_MAX, + TEE_FS_NAME_MAX, + old, false); + } + if (res != TEE_SUCCESS) + return res; return operation_commit(&op); } -TEE_Result tee_fs_rpc_opendir(uint32_t id, const char *name, +TEE_Result tee_fs_rpc_opendir(uint32_t id, const TEE_UUID *uuid, struct tee_fs_dir **d) { TEE_Result res; @@ -262,13 +286,12 @@ TEE_Result tee_fs_rpc_opendir(uint32_t id, const char *name, void *va; paddr_t pa; uint64_t cookie; - size_t name_len = strlen(name) + 1; struct tee_fs_dir *dir = calloc(1, sizeof(*dir)); if (!dir) return TEE_ERROR_OUT_OF_MEMORY; - va = tee_fs_rpc_cache_alloc(name_len, &pa, &cookie); + va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &pa, &cookie); if (!va) { res = TEE_ERROR_OUT_OF_MEMORY; goto err_exit; @@ -279,9 +302,11 @@ TEE_Result tee_fs_rpc_opendir(uint32_t id, const char *name, op.params[1].attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT; op.params[1].u.tmem.buf_ptr = pa; - op.params[1].u.tmem.size = name_len; + op.params[1].u.tmem.size = TEE_FS_NAME_MAX; op.params[1].u.tmem.shm_ref = cookie; - strlcpy(va, name, name_len); + res = tee_svc_storage_create_dirname(va, TEE_FS_NAME_MAX, uuid); + if (res != TEE_SUCCESS) + return res; op.params[2].attr = OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT; @@ -308,8 +333,6 @@ TEE_Result tee_fs_rpc_closedir(uint32_t id, struct tee_fs_dir *d) op.params[0].u.value.a = OPTEE_MRF_CLOSEDIR; op.params[0].u.value.b = d->nw_dir; - if (d) - free(d->d.d_name); free(d); return operation_commit(&op); } @@ -344,9 +367,9 @@ TEE_Result tee_fs_rpc_readdir(uint32_t id, struct tee_fs_dir *d, if (res != TEE_SUCCESS) return res; - free(d->d.d_name); - d->d.d_name = strndup(va, max_name_len); - if (!d->d.d_name) + d->d.oidlen = tee_hs2b(va, d->d.oid, strnlen(va, max_name_len), + sizeof(d->d.oid)); + if (!d->d.oidlen) return TEE_ERROR_OUT_OF_MEMORY; *ent = &d->d; diff --git a/core/tee/tee_obj.c b/core/tee/tee_obj.c index 78cf937..6dcfbf3 100644 --- a/core/tee/tee_obj.c +++ b/core/tee/tee_obj.c @@ -31,7 +31,6 @@ #include <tee_api_defines.h> #include <mm/tee_mmu.h> #include <tee/tee_fs.h> -#include <tee/tee_fs_defs.h> #include <tee/tee_pobj.h> #include <trace.h> #include <tee/tee_svc_storage.h> @@ -79,32 +78,20 @@ void tee_obj_close_all(struct user_ta_ctx *utc) TEE_Result tee_obj_verify(struct tee_ta_session *sess, struct tee_obj *o) { TEE_Result res; - char *file = NULL; const struct tee_file_operations *fops = o->pobj->fops; struct tee_file_handle *fh = NULL; if (!fops) return TEE_ERROR_STORAGE_NOT_AVAILABLE; - file = tee_svc_storage_create_filename(sess, - o->pobj->obj_id, - o->pobj->obj_id_len, - false); - if (file == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - - res = fops->open(file, &fh); + res = fops->open(o->pobj, &fh); if (res == TEE_ERROR_CORRUPT_OBJECT) { EMSG("Object corrupt\n"); + fops->remove(o->pobj); tee_obj_close(to_user_ta_ctx(sess->ctx), o); - fops->remove(file); } - free(file); fops->close(&fh); -exit: return res; } diff --git a/core/tee/tee_pobj.c b/core/tee/tee_pobj.c index a7aee31..0f9fb46 100644 --- a/core/tee/tee_pobj.c +++ b/core/tee/tee_pobj.c @@ -80,7 +80,8 @@ static TEE_Result tee_pobj_check_access(uint32_t oflags, uint32_t nflags) } TEE_Result tee_pobj_get(TEE_UUID *uuid, void *obj_id, uint32_t obj_id_len, - uint32_t flags, const struct tee_file_operations *fops, + uint32_t flags, bool temporary, + const struct tee_file_operations *fops, struct tee_pobj **obj) { struct tee_pobj *o; @@ -100,10 +101,12 @@ TEE_Result tee_pobj_get(TEE_UUID *uuid, void *obj_id, uint32_t obj_id_len, } if (*obj) { + if (temporary != (*obj)->temporary) { + res = TEE_ERROR_ACCESS_CONFLICT; + goto out; + } res = tee_pobj_check_access((*obj)->flags, flags); - if (res != TEE_SUCCESS) - *obj = NULL; - else + if (res == TEE_SUCCESS) (*obj)->refcnt++; goto out; } @@ -119,6 +122,7 @@ TEE_Result tee_pobj_get(TEE_UUID *uuid, void *obj_id, uint32_t obj_id_len, memcpy(&o->uuid, uuid, sizeof(TEE_UUID)); o->flags = flags; o->fops = fops; + o->temporary = temporary; o->obj_id = malloc(obj_id_len); if (o->obj_id == NULL) { @@ -134,6 +138,8 @@ TEE_Result tee_pobj_get(TEE_UUID *uuid, void *obj_id, uint32_t obj_id_len, res = TEE_SUCCESS; out: + if (res != TEE_SUCCESS) + *obj = NULL; mutex_unlock(&pobjs_mutex); return res; } diff --git a/core/tee/tee_ree_fs.c b/core/tee/tee_ree_fs.c index 7a82acb..544ed3e 100644 --- a/core/tee/tee_ree_fs.c +++ b/core/tee/tee_ree_fs.c @@ -26,125 +26,45 @@ */ #include <assert.h> -#include <kernel/thread.h> #include <kernel/mutex.h> #include <kernel/panic.h> +#include <kernel/thread.h> #include <mm/core_memprot.h> #include <optee_msg_supplicant.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <string_ext.h> +#include <string.h> #include <sys/queue.h> +#include <tee/fs_htree.h> #include <tee/tee_cryp_provider.h> #include <tee/tee_fs.h> -#include <tee/tee_fs_defs.h> #include <tee/tee_fs_rpc.h> -#include <tee/tee_fs_key_manager.h> #include <trace.h> #include <utee_defines.h> #include <util.h> -/* - * This file implements the tee_file_operations structure for a secure - * filesystem based on single file in normal world. - * - * All fields in the REE file are duplicated with two versions 0 and 1. The - * active meta-data block is selected by the lowest bit in the - * meta-counter. The active file block is selected by corresponding bit - * number in struct tee_fs_file_info.backup_version_table. - * - * The atomicity of each operation is ensured by updating meta-counter when - * everything in the secondary blocks (both meta-data and file-data blocks) - * are successfully written. The main purpose of the code below is to - * perform block encryption and authentication of the file data, and - * properly handle seeking through the file. One file (in the sense of - * struct tee_file_operations) maps to one file in the REE filesystem, and - * has the following structure: - * - * [ 4 bytes meta-counter] - * [ meta-data version 0][ meta-data version 1 ] - * [ Block 0 version 0 ][ Block 0 version 1 ] - * [ Block 1 version 0 ][ Block 1 version 1 ] - * ... - * [ Block n version 0 ][ Block n version 1 ] - * - * One meta-data block is built up as: - * [ struct meta_header | struct tee_fs_get_header_size ] - * - * One data block is built up as: - * [ struct block_header | BLOCK_FILE_SIZE bytes ] - * - * struct meta_header and struct block_header are defined in - * tee_fs_key_manager.h. - * - */ - #define BLOCK_SHIFT 12 #define BLOCK_SIZE (1 << BLOCK_SHIFT) -#define MAX_FILE_SIZE (BLOCK_SIZE * NUM_BLOCKS_PER_FILE) - struct tee_fs_fd { - uint32_t meta_counter; - struct tee_fs_file_meta meta; - tee_fs_off_t pos; - uint32_t flags; - bool is_new_file; + struct tee_fs_htree *ht; int fd; }; -static inline int pos_to_block_num(int position) +static int pos_to_block_num(int position) { return position >> BLOCK_SHIFT; } -static inline int get_last_block_num(size_t size) -{ - return pos_to_block_num(size - 1); -} - -static bool get_backup_version_of_block(struct tee_fs_file_meta *meta, - size_t block_num) -{ - uint32_t index = (block_num / 32); - uint32_t block_mask = 1 << (block_num % 32); - - return !!(meta->info.backup_version_table[index] & block_mask); -} - -static inline void toggle_backup_version_of_block( - struct tee_fs_file_meta *meta, - size_t block_num) -{ - uint32_t index = (block_num / 32); - uint32_t block_mask = 1 << (block_num % 32); - - meta->info.backup_version_table[index] ^= block_mask; -} - -struct block_operations { - - /* - * Read a block from REE File System which is corresponding - * to the given block_num. - */ - struct block *(*read)(struct tee_fs_fd *fdp, int block_num); - - /* - * Write the given block to REE File System - */ - int (*write)(struct tee_fs_fd *fdp, struct block *b, - struct tee_fs_file_meta *new_meta); -}; - static struct mutex ree_fs_mutex = MUTEX_INITIALIZER; -static TEE_Result ree_fs_opendir_rpc(const char *name, struct tee_fs_dir **d) +static TEE_Result ree_fs_opendir_rpc(const TEE_UUID *uuid, + struct tee_fs_dir **d) { - return tee_fs_rpc_opendir(OPTEE_MSG_RPC_CMD_FS, name, d); + return tee_fs_rpc_opendir(OPTEE_MSG_RPC_CMD_FS, uuid, d); } static void ree_fs_closedir_rpc(struct tee_fs_dir *d) @@ -159,330 +79,187 @@ static TEE_Result ree_fs_readdir_rpc(struct tee_fs_dir *d, return tee_fs_rpc_readdir(OPTEE_MSG_RPC_CMD_FS, d, ent); } -static size_t meta_size(void) -{ - return tee_fs_get_header_size(META_FILE) + - sizeof(struct tee_fs_file_meta); -} - -static size_t meta_pos_raw(struct tee_fs_fd *fdp, bool active) -{ - size_t offs = sizeof(uint32_t); - - if ((fdp->meta_counter & 1) == active) - offs += meta_size(); - return offs; -} - -static size_t block_size_raw(void) -{ - return tee_fs_get_header_size(BLOCK_FILE) + BLOCK_SIZE; -} - -static size_t block_pos_raw(struct tee_fs_file_meta *meta, size_t block_num, - bool active) -{ - size_t n = block_num * 2; - - if (active == get_backup_version_of_block(meta, block_num)) - n++; - - return sizeof(uint32_t) + meta_size() * 2 + n * block_size_raw(); -} - -/* - * encrypted_fek: as input for META_FILE and BLOCK_FILE - */ -static TEE_Result encrypt_and_write_file(struct tee_fs_fd *fdp, - enum tee_fs_file_type file_type, size_t offs, - void *data_in, size_t data_in_size, - uint8_t *encrypted_fek) +static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, size_t pos, + const void *buf, size_t len) { TEE_Result res; - struct tee_fs_rpc_operation op; - void *ciphertext; - size_t header_size = tee_fs_get_header_size(file_type); - size_t ciphertext_size = header_size + data_in_size; - + size_t start_block_num = pos_to_block_num(pos); + size_t end_block_num = pos_to_block_num(pos + len - 1); + size_t remain_bytes = len; + uint8_t *data_ptr = (uint8_t *)buf; + uint8_t *block; + struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); - res = tee_fs_rpc_write_init(&op, OPTEE_MSG_RPC_CMD_FS, fdp->fd, - offs, ciphertext_size, &ciphertext); - if (res != TEE_SUCCESS) - return res; + block = malloc(BLOCK_SIZE); + if (!block) + return TEE_ERROR_OUT_OF_MEMORY; - res = tee_fs_encrypt_file(file_type, data_in, data_in_size, - ciphertext, &ciphertext_size, encrypted_fek); - if (res != TEE_SUCCESS) - return res; + while (start_block_num <= end_block_num) { + size_t offset = pos % BLOCK_SIZE; + size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE); - return tee_fs_rpc_write_final(&op); -} + if (size_to_write + offset > BLOCK_SIZE) + size_to_write = BLOCK_SIZE - offset; -/* - * encrypted_fek: as output for META_FILE - * as input for BLOCK_FILE - */ -static TEE_Result read_and_decrypt_file(struct tee_fs_fd *fdp, - enum tee_fs_file_type file_type, size_t offs, - void *data_out, size_t *data_out_size, - uint8_t *encrypted_fek) -{ - TEE_Result res; - struct tee_fs_rpc_operation op; - size_t bytes; - void *ciphertext; + if (start_block_num * BLOCK_SIZE < + ROUNDUP(meta->length, BLOCK_SIZE)) { + res = tee_fs_htree_read_block(&fdp->ht, + start_block_num, block); + if (res != TEE_SUCCESS) + goto exit; + } else { + memset(block, 0, BLOCK_SIZE); + } - bytes = *data_out_size + tee_fs_get_header_size(file_type); - res = tee_fs_rpc_read_init(&op, OPTEE_MSG_RPC_CMD_FS, fdp->fd, offs, - bytes, &ciphertext); - if (res != TEE_SUCCESS) - return res; + if (data_ptr) + memcpy(block + offset, data_ptr, size_to_write); + else + memset(block + offset, 0, size_to_write); - res = tee_fs_rpc_read_final(&op, &bytes); - if (res != TEE_SUCCESS) - return res; + res = tee_fs_htree_write_block(&fdp->ht, start_block_num, + block); + if (res != TEE_SUCCESS) + goto exit; - if (!bytes) { - *data_out_size = 0; - return TEE_SUCCESS; + if (data_ptr) + data_ptr += size_to_write; + remain_bytes -= size_to_write; + start_block_num++; + pos += size_to_write; } - res = tee_fs_decrypt_file(file_type, ciphertext, bytes, data_out, - data_out_size, encrypted_fek); - if (res != TEE_SUCCESS) - return TEE_ERROR_CORRUPT_OBJECT; - return TEE_SUCCESS; -} - -static TEE_Result write_meta_file(struct tee_fs_fd *fdp, - struct tee_fs_file_meta *meta) -{ - size_t offs = meta_pos_raw(fdp, false); - - return encrypt_and_write_file(fdp, META_FILE, offs, - (void *)&meta->info, sizeof(meta->info), - meta->encrypted_fek); -} + if (pos > meta->length) + meta->length = pos; -static TEE_Result write_meta_counter(struct tee_fs_fd *fdp) -{ - TEE_Result res; - struct tee_fs_rpc_operation op; - size_t bytes = sizeof(uint32_t); - void *data; - - res = tee_fs_rpc_write_init(&op, OPTEE_MSG_RPC_CMD_FS, fdp->fd, 0, - bytes, &data); - if (res != TEE_SUCCESS) - return res; - - memcpy(data, &fdp->meta_counter, bytes); - - return tee_fs_rpc_write_final(&op); -} - -static TEE_Result create_meta(struct tee_fs_fd *fdp, const char *fname) -{ - TEE_Result res; - - memset(fdp->meta.info.backup_version_table, 0xff, - sizeof(fdp->meta.info.backup_version_table)); - fdp->meta.info.length = 0; - - res = tee_fs_generate_fek(fdp->meta.encrypted_fek, TEE_FS_KM_FEK_SIZE); - if (res != TEE_SUCCESS) - return res; - - res = tee_fs_rpc_create(OPTEE_MSG_RPC_CMD_FS, fname, &fdp->fd); - if (res != TEE_SUCCESS) - return res; - - fdp->meta.counter = fdp->meta_counter; - - res = write_meta_file(fdp, &fdp->meta); - if (res != TEE_SUCCESS) - return res; - return write_meta_counter(fdp); +exit: + free(block); + return res; } -static TEE_Result commit_meta_file(struct tee_fs_fd *fdp, - struct tee_fs_file_meta *new_meta) +static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx, + uint8_t vers, size_t *offs, size_t *size) { - TEE_Result res; - - new_meta->counter = fdp->meta_counter + 1; + const size_t node_size = sizeof(struct tee_fs_htree_node_image); + const size_t block_nodes = BLOCK_SIZE / (node_size * 2); + size_t pbn; + size_t bidx; - res = write_meta_file(fdp, new_meta); - if (res != TEE_SUCCESS) - return res; + assert(vers == 0 || vers == 1); /* - * From now on the new meta is successfully committed, - * change tee_fs_fd accordingly + * File layout + * + * phys block 0: + * tee_fs_htree_image vers 0 @ offs = 0 + * tee_fs_htree_image vers 1 @ offs = sizeof(tee_fs_htree_image) + * + * phys block 1: + * tee_fs_htree_node_image 0 vers 0 @ offs = 0 + * tee_fs_htree_node_image 0 vers 1 @ offs = node_size + * tee_fs_htree_node_image 1 vers 0 @ offs = node_size * 2 + * tee_fs_htree_node_image 1 vers 1 @ offs = node_size * 3 + * ... + * tee_fs_htree_node_image 61 vers 0 @ offs = node_size * 122 + * tee_fs_htree_node_image 61 vers 1 @ offs = node_size * 123 + * + * phys block 2: + * data block 0 vers 0 + * + * phys block 3: + * data block 0 vers 1 + * + * ... + * phys block 63: + * data block 61 vers 0 + * + * phys block 64: + * data block 61 vers 1 + * + * phys block 65: + * tee_fs_htree_node_image 62 vers 0 @ offs = 0 + * tee_fs_htree_node_image 62 vers 1 @ offs = node_size + * tee_fs_htree_node_image 63 vers 0 @ offs = node_size * 2 + * tee_fs_htree_node_image 63 vers 1 @ offs = node_size * 3 + * ... + * tee_fs_htree_node_image 121 vers 0 @ offs = node_size * 122 + * tee_fs_htree_node_image 121 vers 1 @ offs = node_size * 123 + * + * ... */ - fdp->meta = *new_meta; - fdp->meta_counter = fdp->meta.counter; - return write_meta_counter(fdp); -} - -static TEE_Result read_meta_file(struct tee_fs_fd *fdp, - struct tee_fs_file_meta *meta) -{ - size_t meta_info_size = sizeof(struct tee_fs_file_info); - size_t offs = meta_pos_raw(fdp, true); - - return read_and_decrypt_file(fdp, META_FILE, offs, - &meta->info, &meta_info_size, - meta->encrypted_fek); + switch (type) { + case TEE_FS_HTREE_TYPE_HEAD: + *offs = sizeof(struct tee_fs_htree_image) * vers; + *size = sizeof(struct tee_fs_htree_image); + return TEE_SUCCESS; + case TEE_FS_HTREE_TYPE_NODE: + pbn = 1 + ((idx / block_nodes) * block_nodes * 2); + *offs = pbn * BLOCK_SIZE + + 2 * node_size * (idx % block_nodes) + + node_size * vers; + *size = node_size; + return TEE_SUCCESS; + case TEE_FS_HTREE_TYPE_BLOCK: + bidx = 2 * idx + vers; + pbn = 2 + bidx + bidx / (block_nodes * 2 - 1); + *offs = pbn * BLOCK_SIZE; + *size = BLOCK_SIZE; + return TEE_SUCCESS; + default: + return TEE_ERROR_GENERIC; + } } -static TEE_Result read_meta_counter(struct tee_fs_fd *fdp) +static TEE_Result ree_fs_rpc_read_init(void *aux, + struct tee_fs_rpc_operation *op, + enum tee_fs_htree_type type, size_t idx, + uint8_t vers, void **data) { + struct tee_fs_fd *fdp = aux; TEE_Result res; - struct tee_fs_rpc_operation op; - void *data; - size_t bytes = sizeof(uint32_t); - - res = tee_fs_rpc_read_init(&op, OPTEE_MSG_RPC_CMD_FS, fdp->fd, 0, - bytes, &data); - if (res != TEE_SUCCESS) - return res; + size_t offs; + size_t size; - res = tee_fs_rpc_read_final(&op, &bytes); + res = get_offs_size(type, idx, vers, &offs, &size); if (res != TEE_SUCCESS) return res; - if (bytes != sizeof(uint32_t)) - return TEE_ERROR_CORRUPT_OBJECT; - - memcpy(&fdp->meta_counter, data, bytes); - - return TEE_SUCCESS; + return tee_fs_rpc_read_init(op, OPTEE_MSG_RPC_CMD_FS, fdp->fd, + offs, size, data); } -static TEE_Result read_meta(struct tee_fs_fd *fdp, const char *fname) +static TEE_Result ree_fs_rpc_write_init(void *aux, + struct tee_fs_rpc_operation *op, + enum tee_fs_htree_type type, size_t idx, + uint8_t vers, void **data) { + struct tee_fs_fd *fdp = aux; TEE_Result res; + size_t offs; + size_t size; - res = tee_fs_rpc_open(OPTEE_MSG_RPC_CMD_FS, fname, &fdp->fd); - if (res != TEE_SUCCESS) - return res; - - res = read_meta_counter(fdp); - if (res != TEE_SUCCESS) - return res; - - return read_meta_file(fdp, &fdp->meta); -} - -static TEE_Result read_block(struct tee_fs_fd *fdp, int bnum, uint8_t *data) -{ - TEE_Result res; - size_t ct_size = block_size_raw(); - size_t out_size = BLOCK_SIZE; - ssize_t pos = block_pos_raw(&fdp->meta, bnum, true); - size_t bytes; - void *ct; - struct tee_fs_rpc_operation op; - - res = tee_fs_rpc_read_init(&op, OPTEE_MSG_RPC_CMD_FS, fdp->fd, pos, - ct_size, &ct); - if (res != TEE_SUCCESS) - return res; - res = tee_fs_rpc_read_final(&op, &bytes); + res = get_offs_size(type, idx, vers, &offs, &size); if (res != TEE_SUCCESS) return res; - if (!bytes) { - memset(data, 0, BLOCK_SIZE); - return TEE_SUCCESS; /* Block does not exist */ - } - return tee_fs_decrypt_file(BLOCK_FILE, ct, bytes, data, - &out_size, fdp->meta.encrypted_fek); + return tee_fs_rpc_write_init(op, OPTEE_MSG_RPC_CMD_FS, fdp->fd, + offs, size, data); } -static TEE_Result write_block(struct tee_fs_fd *fdp, size_t bnum, uint8_t *data, - struct tee_fs_file_meta *new_meta) -{ - TEE_Result res; - size_t offs = block_pos_raw(new_meta, bnum, false); - - res = encrypt_and_write_file(fdp, BLOCK_FILE, offs, data, - BLOCK_SIZE, new_meta->encrypted_fek); - if (res == TEE_SUCCESS) - toggle_backup_version_of_block(new_meta, bnum); - return res; -} - -static TEE_Result out_of_place_write(struct tee_fs_fd *fdp, const void *buf, - size_t len, struct tee_fs_file_meta *new_meta) -{ - TEE_Result res; - int start_block_num = pos_to_block_num(fdp->pos); - int end_block_num = pos_to_block_num(fdp->pos + len - 1); - size_t remain_bytes = len; - uint8_t *data_ptr = (uint8_t *)buf; - uint8_t *block; - int orig_pos = fdp->pos; - - block = malloc(BLOCK_SIZE); - if (!block) - return TEE_ERROR_OUT_OF_MEMORY; - - while (start_block_num <= end_block_num) { - int offset = fdp->pos % BLOCK_SIZE; - size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE); - - if (size_to_write + offset > BLOCK_SIZE) - size_to_write = BLOCK_SIZE - offset; - - res = read_block(fdp, start_block_num, block); - if (res == TEE_ERROR_ITEM_NOT_FOUND) - memset(block, 0, BLOCK_SIZE); - else if (res != TEE_SUCCESS) - goto exit; - - if (data_ptr) - memcpy(block + offset, data_ptr, size_to_write); - else - memset(block + offset, 0, size_to_write); - - res = write_block(fdp, start_block_num, block, new_meta); - if (res != TEE_SUCCESS) - goto exit; - - if (data_ptr) - data_ptr += size_to_write; - remain_bytes -= size_to_write; - start_block_num++; - fdp->pos += size_to_write; - } - - if (fdp->pos > (tee_fs_off_t)new_meta->info.length) - new_meta->info.length = fdp->pos; - -exit: - free(block); - if (res != TEE_SUCCESS) - fdp->pos = orig_pos; - return res; -} +static const struct tee_fs_htree_storage ree_fs_storage_ops = { + .block_size = BLOCK_SIZE, + .rpc_read_init = ree_fs_rpc_read_init, + .rpc_read_final = tee_fs_rpc_read_final, + .rpc_write_init = ree_fs_rpc_write_init, + .rpc_write_final = tee_fs_rpc_write_final, +}; -static TEE_Result open_internal(const char *file, bool create, +static TEE_Result open_internal(struct tee_pobj *po, bool create, struct tee_file_handle **fh) { TEE_Result res; - size_t len; struct tee_fs_fd *fdp = NULL; - if (!file) - return TEE_ERROR_BAD_PARAMETERS; - - len = strlen(file) + 1; - if (len > TEE_FS_NAME_MAX) - return TEE_ERROR_BAD_PARAMETERS; - fdp = calloc(1, sizeof(struct tee_fs_fd)); if (!fdp) return TEE_ERROR_OUT_OF_MEMORY; @@ -491,17 +268,22 @@ static TEE_Result open_internal(const char *file, bool create, mutex_lock(&ree_fs_mutex); if (create) - res = create_meta(fdp, file); + res = tee_fs_rpc_create(OPTEE_MSG_RPC_CMD_FS, po, &fdp->fd); else - res = read_meta(fdp, file); + res = tee_fs_rpc_open(OPTEE_MSG_RPC_CMD_FS, po, &fdp->fd); + + if (res != TEE_SUCCESS) + goto out; + res = tee_fs_htree_open(create, &ree_fs_storage_ops, fdp, &fdp->ht); +out: if (res == TEE_SUCCESS) { *fh = (struct tee_file_handle *)fdp; } else { if (fdp->fd != -1) tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_FS, fdp->fd); if (create) - tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_FS, file); + tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_FS, po); free(fdp); } @@ -509,14 +291,15 @@ static TEE_Result open_internal(const char *file, bool create, return res; } -static TEE_Result ree_fs_open(const char *file, struct tee_file_handle **fh) +static TEE_Result ree_fs_open(struct tee_pobj *po, struct tee_file_handle **fh) { - return open_internal(file, false, fh); + return open_internal(po, false, fh); } -static TEE_Result ree_fs_create(const char *file, struct tee_file_handle **fh) +static TEE_Result ree_fs_create(struct tee_pobj *po, + struct tee_file_handle **fh) { - return open_internal(file, true, fh); + return open_internal(po, true, fh); } static void ree_fs_close(struct tee_file_handle **fh) @@ -524,106 +307,53 @@ static void ree_fs_close(struct tee_file_handle **fh) struct tee_fs_fd *fdp = (struct tee_fs_fd *)*fh; if (fdp) { + tee_fs_htree_close(&fdp->ht); tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_FS, fdp->fd); free(fdp); *fh = NULL; } } -static TEE_Result ree_fs_seek(struct tee_file_handle *fh, int32_t offset, - TEE_Whence whence, int32_t *new_offs) -{ - TEE_Result res; - tee_fs_off_t new_pos; - size_t filelen; - struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; - - mutex_lock(&ree_fs_mutex); - - DMSG("offset=%d, whence=%d", (int)offset, whence); - - filelen = fdp->meta.info.length; - - switch (whence) { - case TEE_DATA_SEEK_SET: - new_pos = offset; - break; - - case TEE_DATA_SEEK_CUR: - new_pos = fdp->pos + offset; - break; - - case TEE_DATA_SEEK_END: - new_pos = filelen + offset; - break; - - default: - res = TEE_ERROR_BAD_PARAMETERS; - goto exit; - } - - if (new_pos < 0) - new_pos = 0; - - if (new_pos > TEE_DATA_MAX_POSITION) { - EMSG("Position is beyond TEE_DATA_MAX_POSITION"); - res = TEE_ERROR_BAD_PARAMETERS; - goto exit; - } - - fdp->pos = new_pos; - if (new_offs) - *new_offs = new_pos; - res = TEE_SUCCESS; -exit: - mutex_unlock(&ree_fs_mutex); - return res; -} - -/* - * To ensure atomic truncate operation, we can: - * - * - update file length to new length - * - commit new meta - * - * To ensure atomic extend operation, we can: - * - * - update file length to new length - * - allocate and fill zero data to new blocks - * - commit new meta - * - * Any failure before committing new meta is considered as - * update failed, and the file content will not be updated - */ static TEE_Result ree_fs_ftruncate_internal(struct tee_fs_fd *fdp, tee_fs_off_t new_file_len) { TEE_Result res; - size_t old_file_len = fdp->meta.info.length; - struct tee_fs_file_meta new_meta; + struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); - if (new_file_len > MAX_FILE_SIZE) - return TEE_ERROR_BAD_PARAMETERS; + if ((size_t)new_file_len > meta->length) { + size_t ext_len = new_file_len - meta->length; - new_meta = fdp->meta; - new_meta.info.length = new_file_len; + res = out_of_place_write(fdp, meta->length, NULL, ext_len); + if (res != TEE_SUCCESS) + return res; + } else { + size_t offs; + size_t sz; + + res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK, + ROUNDUP(new_file_len, BLOCK_SIZE) / + BLOCK_SIZE, 1, &offs, &sz); + if (res != TEE_SUCCESS) + return res; - if ((size_t)new_file_len > old_file_len) { - size_t ext_len = new_file_len - old_file_len; - int orig_pos = fdp->pos; + res = tee_fs_htree_truncate(&fdp->ht, + new_file_len / BLOCK_SIZE); + if (res != TEE_SUCCESS) + return res; - fdp->pos = old_file_len; - res = out_of_place_write(fdp, NULL, ext_len, &new_meta); - fdp->pos = orig_pos; + res = tee_fs_rpc_truncate(OPTEE_MSG_RPC_CMD_FS, fdp->fd, + offs + sz); if (res != TEE_SUCCESS) return res; + + meta->length = new_file_len; } - return commit_meta_file(fdp, &new_meta); + return tee_fs_htree_sync_to_storage(&fdp->ht); } -static TEE_Result ree_fs_read(struct tee_file_handle *fh, void *buf, - size_t *len) +static TEE_Result ree_fs_read(struct tee_file_handle *fh, size_t pos, + void *buf, size_t *len) { TEE_Result res; int start_block_num; @@ -632,16 +362,15 @@ static TEE_Result ree_fs_read(struct tee_file_handle *fh, void *buf, uint8_t *data_ptr = buf; uint8_t *block = NULL; struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; + struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); mutex_lock(&ree_fs_mutex); remain_bytes = *len; - if ((fdp->pos + remain_bytes) < remain_bytes || - fdp->pos > (tee_fs_off_t)fdp->meta.info.length) + if ((pos + remain_bytes) < remain_bytes || pos > meta->length) remain_bytes = 0; - else if (fdp->pos + (tee_fs_off_t)remain_bytes > - (tee_fs_off_t)fdp->meta.info.length) - remain_bytes = fdp->meta.info.length - fdp->pos; + else if (pos + remain_bytes > meta->length) + remain_bytes = meta->length - pos; *len = remain_bytes; @@ -650,8 +379,8 @@ static TEE_Result ree_fs_read(struct tee_file_handle *fh, void *buf, goto exit; } - start_block_num = pos_to_block_num(fdp->pos); - end_block_num = pos_to_block_num(fdp->pos + remain_bytes - 1); + start_block_num = pos_to_block_num(pos); + end_block_num = pos_to_block_num(pos + remain_bytes - 1); block = malloc(BLOCK_SIZE); if (!block) { @@ -660,24 +389,21 @@ static TEE_Result ree_fs_read(struct tee_file_handle *fh, void *buf, } while (start_block_num <= end_block_num) { - tee_fs_off_t offset = fdp->pos % BLOCK_SIZE; + size_t offset = pos % BLOCK_SIZE; size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE); if (size_to_read + offset > BLOCK_SIZE) size_to_read = BLOCK_SIZE - offset; - res = read_block(fdp, start_block_num, block); - if (res != TEE_SUCCESS) { - if (res == TEE_ERROR_MAC_INVALID) - res = TEE_ERROR_CORRUPT_OBJECT; + res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block); + if (res != TEE_SUCCESS) goto exit; - } memcpy(data_ptr, block + offset, size_to_read); data_ptr += size_to_read; remain_bytes -= size_to_read; - fdp->pos += size_to_read; + pos += size_to_read; start_block_num++; } @@ -688,27 +414,10 @@ exit: return res; } -/* - * To ensure atomicity of write operation, we need to - * do the following steps: - * (The sequence of operations is very important) - * - * - Create a new backup version of meta file as a copy - * of current meta file. - * - For each blocks to write: - * - Create new backup version for current block. - * - Write data to new backup version. - * - Update the new meta file accordingly. - * - Write the new meta file. - * - * (Any failure in above steps is considered as update failed, - * and the file content will not be updated) - */ -static TEE_Result ree_fs_write(struct tee_file_handle *fh, const void *buf, - size_t len) +static TEE_Result ree_fs_write(struct tee_file_handle *fh, size_t pos, + const void *buf, size_t len) { TEE_Result res; - struct tee_fs_file_meta new_meta; struct tee_fs_fd *fdp = (struct tee_fs_fd *)fh; size_t file_size; @@ -717,31 +426,31 @@ static TEE_Result ree_fs_write(struct tee_file_handle *fh, const void *buf, mutex_lock(&ree_fs_mutex); - file_size = fdp->meta.info.length; + file_size = tee_fs_htree_get_meta(fdp->ht)->length; - if ((fdp->pos + len) > MAX_FILE_SIZE || (fdp->pos + len) < len) { + if ((pos + len) < len) { res = TEE_ERROR_BAD_PARAMETERS; goto exit; } - if (file_size < (size_t)fdp->pos) { - res = ree_fs_ftruncate_internal(fdp, fdp->pos); + if (file_size < pos) { + res = ree_fs_ftruncate_internal(fdp, pos); if (res != TEE_SUCCESS) goto exit; } - new_meta = fdp->meta; - res = out_of_place_write(fdp, buf, len, &new_meta); + res = out_of_place_write(fdp, pos, buf, len); if (res != TEE_SUCCESS) goto exit; - res = commit_meta_file(fdp, &new_meta); exit: + if (res == TEE_SUCCESS) + res = tee_fs_htree_sync_to_storage(&fdp->ht); mutex_unlock(&ree_fs_mutex); return res; } -static TEE_Result ree_fs_rename(const char *old, const char *new, +static TEE_Result ree_fs_rename(struct tee_pobj *old, struct tee_pobj *new, bool overwrite) { TEE_Result res; @@ -753,12 +462,12 @@ static TEE_Result ree_fs_rename(const char *old, const char *new, return res; } -static TEE_Result ree_fs_remove(const char *file) +static TEE_Result ree_fs_remove(struct tee_pobj *po) { TEE_Result res; mutex_lock(&ree_fs_mutex); - res = tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_FS, file); + res = tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_FS, po); mutex_unlock(&ree_fs_mutex); return res; @@ -782,7 +491,6 @@ const struct tee_file_operations ree_fs_ops = { .close = ree_fs_close, .read = ree_fs_read, .write = ree_fs_write, - .seek = ree_fs_seek, .truncate = ree_fs_truncate, .rename = ree_fs_rename, .remove = ree_fs_remove, diff --git a/core/tee/tee_rpmb_fs.c b/core/tee/tee_rpmb_fs.c index 229bce7..dcac98b 100644 --- a/core/tee/tee_rpmb_fs.c +++ b/core/tee/tee_rpmb_fs.c @@ -26,22 +26,24 @@ */ #include <assert.h> -#include <kernel/tee_common.h> #include <kernel/mutex.h> #include <kernel/panic.h> +#include <kernel/tee_common.h> #include <kernel/tee_common_otp.h> +#include <kernel/tee_misc.h> #include <kernel/thread.h> #include <mm/core_memprot.h> #include <mm/tee_mm.h> #include <optee_msg_supplicant.h> #include <stdlib.h> -#include <string.h> #include <string_ext.h> +#include <string.h> #include <sys/queue.h> #include <tee/tee_cryp_provider.h> -#include <tee/tee_fs_defs.h> #include <tee/tee_fs.h> #include <tee/tee_fs_key_manager.h> +#include <tee/tee_pobj.h> +#include <tee/tee_svc_storage.h> #include <trace.h> #include <util.h> @@ -89,8 +91,6 @@ struct rpmb_file_handle { char filename[TEE_RPMB_FS_FILENAME_LENGTH]; /* Address for current entry in RPMB */ uint32_t rpmb_fat_address; - /* Current position */ - uint32_t pos; }; /** @@ -106,13 +106,10 @@ struct rpmb_fs_partition { }; /** - * A node in a list of directory entries. entry->name is a - * pointer to name here. + * A node in a list of directory entries. */ struct tee_rpmb_fs_dirent { struct tee_fs_dirent entry; - char name[TEE_RPMB_FS_FILENAME_LENGTH]; - /* */ SIMPLEQ_ENTRY(tee_rpmb_fs_dirent) link; }; @@ -1581,7 +1578,6 @@ out: static void dump_fh(struct rpmb_file_handle *fh) { DMSG("fh->filename=%s", fh->filename); - DMSG("fh->pos=%u", fh->pos); DMSG("fh->rpmb_fat_address=%u", fh->rpmb_fat_address); DMSG("fh->fat_entry.start_address=%u", fh->fat_entry.start_address); DMSG("fh->fat_entry.data_size=%u", fh->fat_entry.data_size); @@ -1592,7 +1588,8 @@ static void dump_fh(struct rpmb_file_handle *fh __unused) } #endif -static struct rpmb_file_handle *alloc_file_handle(const char *filename) +static struct rpmb_file_handle *alloc_file_handle(struct tee_pobj *po, + bool temporary) { struct rpmb_file_handle *fh = NULL; @@ -1600,8 +1597,10 @@ static struct rpmb_file_handle *alloc_file_handle(const char *filename) if (!fh) return NULL; - if (filename) - strlcpy(fh->filename, filename, sizeof(fh->filename)); + if (po) + tee_svc_storage_create_filename(fh->filename, + sizeof(fh->filename), po, + temporary); return fh; } @@ -1696,7 +1695,7 @@ static TEE_Result rpmb_fs_setup(void) partition_data->fat_start_address = RPMB_FS_FAT_START_ADDRESS; /* Initial FAT entry with FILE_IS_LAST_ENTRY flag set. */ - fh = alloc_file_handle(NULL); + fh = alloc_file_handle(NULL, false); if (!fh) { res = TEE_ERROR_OUT_OF_MEMORY; goto out; @@ -1924,29 +1923,17 @@ again: return res; } -static TEE_Result rpmb_fs_open_internal(const char *file, bool create, +static TEE_Result rpmb_fs_open_internal(struct tee_pobj *po, bool create, struct tee_file_handle **ret_fh) { struct rpmb_file_handle *fh = NULL; - size_t filelen; tee_mm_pool_t p; bool pool_result; TEE_Result res = TEE_ERROR_GENERIC; mutex_lock(&rpmb_mutex); - filelen = strlen(file); - if (filelen >= TEE_RPMB_FS_FILENAME_LENGTH - 1 || filelen == 0) { - res = TEE_ERROR_BAD_PARAMETERS; - goto out; - } - - if (file[filelen - 1] == '/') { - res = TEE_ERROR_BAD_PARAMETERS; - goto out; - } - - fh = alloc_file_handle(file); + fh = alloc_file_handle(po, po->temporary); if (!fh) { res = TEE_ERROR_OUT_OF_MEMORY; goto out; @@ -1988,7 +1975,8 @@ static TEE_Result rpmb_fs_open_internal(const char *file, bool create, if ((fh->fat_entry.flags & FILE_IS_ACTIVE) == 0) { memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry)); - memcpy(fh->fat_entry.filename, file, strlen(file)); + memcpy(fh->fat_entry.filename, fh->filename, + strlen(fh->filename)); /* Start address and size are 0 */ fh->fat_entry.flags = FILE_IS_ACTIVE; @@ -2025,8 +2013,8 @@ static void rpmb_fs_close(struct tee_file_handle **tfh) *tfh = NULL; } -static TEE_Result rpmb_fs_read(struct tee_file_handle *tfh, void *buf, - size_t *len) +static TEE_Result rpmb_fs_read(struct tee_file_handle *tfh, size_t pos, + void *buf, size_t *len) { TEE_Result res; struct rpmb_file_handle *fh = (struct rpmb_file_handle *)tfh; @@ -2043,29 +2031,28 @@ static TEE_Result rpmb_fs_read(struct tee_file_handle *tfh, void *buf, if (res != TEE_SUCCESS) goto out; - if (fh->pos >= fh->fat_entry.data_size) { + if (pos >= fh->fat_entry.data_size) { *len = 0; goto out; } - size = MIN(size, fh->fat_entry.data_size - fh->pos); + size = MIN(size, fh->fat_entry.data_size - pos); if (size) { res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, - fh->fat_entry.start_address + fh->pos, buf, + fh->fat_entry.start_address + pos, buf, size, fh->fat_entry.fek); if (res != TEE_SUCCESS) goto out; } *len = size; - fh->pos += size; out: mutex_unlock(&rpmb_mutex); return res; } -static TEE_Result rpmb_fs_write(struct tee_file_handle *tfh, const void *buf, - size_t size) +static TEE_Result rpmb_fs_write(struct tee_file_handle *tfh, size_t pos, + const void *buf, size_t size) { TEE_Result res; struct rpmb_file_handle *fh = (struct rpmb_file_handle *)tfh; @@ -2108,8 +2095,8 @@ static TEE_Result rpmb_fs_write(struct tee_file_handle *tfh, const void *buf, if (fh->fat_entry.flags & FILE_IS_LAST_ENTRY) panic("invalid last entry flag"); - end = fh->pos + size; - start_addr = fh->fat_entry.start_address + fh->pos; + end = pos + size; + start_addr = fh->fat_entry.start_address + pos; if (end <= fh->fat_entry.data_size && tee_rpmb_write_is_atomic(CFG_RPMB_FS_DEV_ID, start_addr, size)) { @@ -2143,7 +2130,7 @@ static TEE_Result rpmb_fs_write(struct tee_file_handle *tfh, const void *buf, goto out; } - memcpy(newbuf + fh->pos, buf, size); + memcpy(newbuf + pos, buf, size); newaddr = tee_mm_get_smem(mm); res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, newaddr, newbuf, @@ -2158,7 +2145,6 @@ static TEE_Result rpmb_fs_write(struct tee_file_handle *tfh, const void *buf, goto out; } - fh->pos += size; out: mutex_unlock(&rpmb_mutex); if (pool_result) @@ -2169,68 +2155,14 @@ out: return res; } -static TEE_Result rpmb_fs_seek(struct tee_file_handle *tfh, int32_t offset, - TEE_Whence whence, int32_t *new_offs) - -{ - struct rpmb_file_handle *fh = (struct rpmb_file_handle *)tfh; - TEE_Result res; - tee_fs_off_t new_pos; - - mutex_lock(&rpmb_mutex); - - res = read_fat(fh, NULL); - if (res != TEE_SUCCESS) - goto out; - - switch (whence) { - case TEE_DATA_SEEK_SET: - new_pos = offset; - break; - - case TEE_DATA_SEEK_CUR: - new_pos = fh->pos + offset; - break; - - case TEE_DATA_SEEK_END: - new_pos = fh->fat_entry.data_size + offset; - break; - - default: - res = TEE_ERROR_BAD_PARAMETERS; - goto out; - } - - if (new_pos < 0) - new_pos = 0; - - if (new_pos > TEE_DATA_MAX_POSITION) { - EMSG("Position is beyond TEE_DATA_MAX_POSITION"); - res = TEE_ERROR_BAD_PARAMETERS; - goto out; - } - - fh->pos = new_pos; - if (new_offs) - *new_offs = new_pos; -out: - mutex_unlock(&rpmb_mutex); - return res; -} - -static TEE_Result rpmb_fs_remove(const char *filename) +static TEE_Result rpmb_fs_remove(struct tee_pobj *po) { TEE_Result res = TEE_ERROR_GENERIC; struct rpmb_file_handle *fh = NULL; mutex_lock(&rpmb_mutex); - if (!filename || strlen(filename) >= TEE_RPMB_FS_FILENAME_LENGTH - 1) { - res = TEE_ERROR_BAD_PARAMETERS; - goto out; - } - - fh = alloc_file_handle(filename); + fh = alloc_file_handle(po, po->temporary); if (!fh) { res = TEE_ERROR_OUT_OF_MEMORY; goto out; @@ -2250,39 +2182,33 @@ out: return res; } -static TEE_Result rpmb_fs_rename(const char *old_name, const char *new_name, +static TEE_Result rpmb_fs_rename(struct tee_pobj *old, struct tee_pobj *new, bool overwrite) { TEE_Result res = TEE_ERROR_GENERIC; struct rpmb_file_handle *fh_old = NULL; struct rpmb_file_handle *fh_new = NULL; - uint32_t old_len; - uint32_t new_len; mutex_lock(&rpmb_mutex); - if (!old_name || !new_name) { + if (!old) { res = TEE_ERROR_BAD_PARAMETERS; goto out; } - old_len = strlen(old_name); - new_len = strlen(new_name); - - if ((old_len >= TEE_RPMB_FS_FILENAME_LENGTH - 1) || - (new_len >= TEE_RPMB_FS_FILENAME_LENGTH - 1) || (new_len == 0)) { - - res = TEE_ERROR_BAD_PARAMETERS; - goto out; - } - - fh_old = alloc_file_handle(old_name); + if (new) + fh_old = alloc_file_handle(old, old->temporary); + else + fh_old = alloc_file_handle(old, true); if (!fh_old) { res = TEE_ERROR_OUT_OF_MEMORY; goto out; } - fh_new = alloc_file_handle(new_name); + if (new) + fh_new = alloc_file_handle(new, new->temporary); + else + fh_new = alloc_file_handle(old, false); if (!fh_new) { res = TEE_ERROR_OUT_OF_MEMORY; goto out; @@ -2307,7 +2233,8 @@ static TEE_Result rpmb_fs_rename(const char *old_name, const char *new_name, } memset(fh_old->fat_entry.filename, 0, TEE_RPMB_FS_FILENAME_LENGTH); - memcpy(fh_old->fat_entry.filename, new_name, new_len); + memcpy(fh_old->fat_entry.filename, fh_new->filename, + strlen(fh_new->filename)); res = write_fat_entry(fh_old, false); @@ -2477,15 +2404,20 @@ static TEE_Result rpmb_fs_dir_populate(const char *path, goto out; } - memset(next, 0, sizeof(*next)); - next->entry.d_name = next->name; - memcpy(next->name, - &filename[pathlen], - filelen - pathlen); + next->entry.oidlen = tee_hs2b( + (uint8_t *)&filename[pathlen], + next->entry.oid, + filelen - pathlen, + sizeof(next->entry.oid)); + if (next->entry.oidlen) { + SIMPLEQ_INSERT_TAIL(&dir->next, + next, link); + current = next; + } else { + free(next); + next = NULL; + } - SIMPLEQ_INSERT_TAIL(&dir->next, next, - link); - current = next; } } @@ -2514,32 +2446,25 @@ out: return res; } -static TEE_Result rpmb_fs_opendir(const char *path, struct tee_fs_dir **dir) +static TEE_Result rpmb_fs_opendir(const TEE_UUID *uuid, struct tee_fs_dir **dir) { uint32_t len; - uint32_t max_size; char path_local[TEE_RPMB_FS_FILENAME_LENGTH]; TEE_Result res = TEE_ERROR_GENERIC; struct tee_fs_dir *rpmb_dir = NULL; - if (!path || !dir) { + if (!uuid || !dir) { res = TEE_ERROR_BAD_PARAMETERS; goto out; } - /* - * There must be room for at least the NULL char and a char for the - * filename after the path. - */ - max_size = TEE_RPMB_FS_FILENAME_LENGTH - 2; - len = strlen(path); - if (len > max_size || len == 0) { + memset(path_local, 0, sizeof(path_local)); + if (tee_svc_storage_create_dirname(path_local, sizeof(path_local) - 1, + uuid) != TEE_SUCCESS) { res = TEE_ERROR_BAD_PARAMETERS; goto out; } - - memset(path_local, 0, sizeof(path_local)); - memcpy(path_local, path, len); + len = strlen(path_local); /* Add a slash to correctly match the full directory name. */ if (path_local[len - 1] != '/') @@ -2591,14 +2516,15 @@ static void rpmb_fs_closedir(struct tee_fs_dir *dir) } } -static TEE_Result rpmb_fs_open(const char *file, struct tee_file_handle **fh) +static TEE_Result rpmb_fs_open(struct tee_pobj *po, struct tee_file_handle **fh) { - return rpmb_fs_open_internal(file, false, fh); + return rpmb_fs_open_internal(po, false, fh); } -static TEE_Result rpmb_fs_create(const char *file, struct tee_file_handle **fh) +static TEE_Result rpmb_fs_create(struct tee_pobj *po, + struct tee_file_handle **fh) { - return rpmb_fs_open_internal(file, true, fh); + return rpmb_fs_open_internal(po, true, fh); } const struct tee_file_operations rpmb_fs_ops = { @@ -2607,7 +2533,6 @@ const struct tee_file_operations rpmb_fs_ops = { .close = rpmb_fs_close, .read = rpmb_fs_read, .write = rpmb_fs_write, - .seek = rpmb_fs_seek, .truncate = rpmb_fs_truncate, .rename = rpmb_fs_rename, .remove = rpmb_fs_remove, diff --git a/core/tee/tee_sql_fs.c b/core/tee/tee_sql_fs.c index e38e1bc..bfc04f6 100644 --- a/core/tee/tee_sql_fs.c +++ b/core/tee/tee_sql_fs.c @@ -29,15 +29,6 @@ * This file implements the tee_file_operations structure for a secure * filesystem based on an SQLite database in normal world. * The atomicity of each operation is ensured by using SQL transactions. - * The main purpose of the code below is to perform block encryption and - * authentication of the file data, and properly handle seeking through the - * file. One file (in the sense of struct tee_file_operations) maps to one - * file in the SQL filesystem, and has the following structure: - * - * [ File meta-data ][ Block #0 ][Block #1]... - * [meta_header|sql_fs_file_meta][block_header|user data][ ]... - * - * meta_header and block_header are defined in tee_fs_key_manager.h. */ #include <assert.h> @@ -45,13 +36,12 @@ #include <optee_msg_supplicant.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <string_ext.h> +#include <string.h> #include <sys/queue.h> +#include <tee/fs_htree.h> #include <tee/tee_cryp_provider.h> #include <tee/tee_fs.h> -#include <tee/tee_fs_defs.h> -#include <tee/tee_fs_key_manager.h> #include <tee/tee_fs_rpc.h> #include <trace.h> #include <utee_defines.h> @@ -61,22 +51,10 @@ #define BLOCK_SHIFT 12 #define BLOCK_SIZE (1 << BLOCK_SHIFT) -struct sql_fs_file_meta { - size_t length; -}; - /* File descriptor */ struct sql_fs_fd { - struct sql_fs_file_meta meta; - uint8_t encrypted_fek[TEE_FS_KM_FEK_SIZE]; - tee_fs_off_t pos; + struct tee_fs_htree *ht; int fd; /* returned by normal world */ - int flags; /* open flags */ -}; - -struct tee_fs_dir { - int nw_dir; - struct tee_fs_dirent d; }; static struct mutex sql_fs_mutex = MUTEX_INITIALIZER; @@ -96,9 +74,10 @@ static TEE_Result sql_fs_end_transaction_rpc(bool rollback) rollback); } -static TEE_Result sql_fs_opendir_rpc(const char *name, struct tee_fs_dir **d) +static TEE_Result sql_fs_opendir_rpc(const TEE_UUID *uuid, + struct tee_fs_dir **d) { - return tee_fs_rpc_opendir(OPTEE_MSG_RPC_CMD_SQL_FS, name, d); + return tee_fs_rpc_opendir(OPTEE_MSG_RPC_CMD_SQL_FS, uuid, d); } static TEE_Result sql_fs_readdir_rpc(struct tee_fs_dir *d, @@ -107,15 +86,15 @@ static TEE_Result sql_fs_readdir_rpc(struct tee_fs_dir *d, return tee_fs_rpc_readdir(OPTEE_MSG_RPC_CMD_SQL_FS, d, ent); } -static TEE_Result sql_fs_remove_rpc(const char *file) +static TEE_Result sql_fs_remove_rpc(struct tee_pobj *po) { - return tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, file); + return tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, po); } -static TEE_Result sql_fs_rename_rpc(const char *old, const char *nw, +static TEE_Result sql_fs_rename_rpc(struct tee_pobj *old, struct tee_pobj *new, bool overwrite) { - return tee_fs_rpc_rename(OPTEE_MSG_RPC_CMD_SQL_FS, old, nw, overwrite); + return tee_fs_rpc_rename(OPTEE_MSG_RPC_CMD_SQL_FS, old, new, overwrite); } static void sql_fs_closedir_rpc(struct tee_fs_dir *d) @@ -128,21 +107,6 @@ static void sql_fs_closedir_rpc(struct tee_fs_dir *d) * End of interface with tee-supplicant */ -static size_t meta_size(void) -{ - return tee_fs_get_header_size(META_FILE) + - sizeof(struct sql_fs_file_meta); -} - -static size_t block_header_size(void) -{ - return tee_fs_get_header_size(BLOCK_FILE); -} - -static size_t block_size_raw(void) -{ - return block_header_size() + BLOCK_SIZE; -} /* Return the block number from a position in the user data */ static ssize_t block_num(tee_fs_off_t pos) @@ -150,131 +114,108 @@ static ssize_t block_num(tee_fs_off_t pos) return pos / BLOCK_SIZE; } -/* Return the position of a block in the DB file */ -static ssize_t block_pos_raw(size_t block_num) +static TEE_Result get_offs_size(enum tee_fs_htree_type type, size_t idx, + size_t *offs, size_t *size) { - return meta_size() + block_num * block_size_raw(); -} - -static TEE_Result write_meta(struct sql_fs_fd *fdp) -{ - TEE_Result res; - size_t ct_size = meta_size(); - void *ct; - struct tee_fs_rpc_operation op; - - res = tee_fs_rpc_write_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, 0, - ct_size, &ct); - if (res != TEE_SUCCESS) - return res; - - - res = tee_fs_encrypt_file(META_FILE, - (const uint8_t *)&fdp->meta, - sizeof(fdp->meta), ct, &ct_size, - fdp->encrypted_fek); - if (res != TEE_SUCCESS) - return res; - - return tee_fs_rpc_write_final(&op); + const size_t node_size = sizeof(struct tee_fs_htree_node_image); + const size_t block_nodes = BLOCK_SIZE / node_size; + size_t pbn; + size_t bidx; + + + /* + * File layout + * + * phys block 0: + * tee_fs_htree_image @ offs = 0 + * + * phys block 1: + * tee_fs_htree_node_image 0 @ offs = 0 + * tee_fs_htree_node_image 1 @ offs = node_size * 2 + * ... + * tee_fs_htree_node_image 61 @ offs = node_size * 122 + * + * phys block 2: + * data block 0 + * + * ... + * + * phys block 64: + * data block 61 + * + * phys block 65: + * tee_fs_htree_node_image 62 @ offs = 0 + * tee_fs_htree_node_image 63 @ offs = node_size * 2 + * ... + * tee_fs_htree_node_image 121 @ offs = node_size * 123 + * + * ... + */ + + switch (type) { + case TEE_FS_HTREE_TYPE_HEAD: + *offs = 0; + *size = sizeof(struct tee_fs_htree_image); + return TEE_SUCCESS; + case TEE_FS_HTREE_TYPE_NODE: + pbn = 1 + ((idx / block_nodes) * block_nodes); + *offs = pbn * BLOCK_SIZE + node_size * (idx % block_nodes); + *size = node_size; + return TEE_SUCCESS; + case TEE_FS_HTREE_TYPE_BLOCK: + bidx = idx; + pbn = 2 + bidx + bidx / (block_nodes - 1); + *offs = pbn * BLOCK_SIZE; + *size = BLOCK_SIZE; + return TEE_SUCCESS; + default: + return TEE_ERROR_GENERIC; + } } -static TEE_Result create_meta(struct sql_fs_fd *fdp, const char *fname) +static TEE_Result sql_fs_rpc_read_init(void *aux, + struct tee_fs_rpc_operation *op, + enum tee_fs_htree_type type, size_t idx, + uint8_t vers __unused, void **data) { + struct sql_fs_fd *fdp = aux; TEE_Result res; + size_t offs; + size_t size; - memset(&fdp->meta, 0, sizeof(fdp->meta)); - - res = tee_fs_generate_fek(fdp->encrypted_fek, TEE_FS_KM_FEK_SIZE); - if (res != TEE_SUCCESS) - return res; - - res = tee_fs_rpc_create(OPTEE_MSG_RPC_CMD_SQL_FS, fname, &fdp->fd); + res = get_offs_size(type, idx, &offs, &size); if (res != TEE_SUCCESS) return res; - return write_meta(fdp); + return tee_fs_rpc_read_init(op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, + offs, size, data); } -static TEE_Result read_meta(struct sql_fs_fd *fdp, const char *fname) +static TEE_Result sql_fs_rpc_write_init(void *aux, + struct tee_fs_rpc_operation *op, + enum tee_fs_htree_type type, size_t idx, + uint8_t vers __unused, void **data) { + struct sql_fs_fd *fdp = aux; TEE_Result res; - size_t msize = meta_size(); - size_t out_size = sizeof(fdp->meta); - void *meta; - size_t bytes; - struct tee_fs_rpc_operation op; - - res = tee_fs_rpc_open(OPTEE_MSG_RPC_CMD_SQL_FS, fname, &fdp->fd); - if (res != TEE_SUCCESS) - return res; - - res = tee_fs_rpc_read_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, 0, - msize, &meta); - if (res != TEE_SUCCESS) - return res; - - res = tee_fs_rpc_read_final(&op, &bytes); - if (res != TEE_SUCCESS) - return res; + size_t offs; + size_t size; - return tee_fs_decrypt_file(META_FILE, meta, msize, - (uint8_t *)&fdp->meta, &out_size, - fdp->encrypted_fek); -} - -/* - * Read one block of user data. - * Returns: - * < 0: read error - * 0: block does not exist (reading past last block) - * > 0: success - */ -static TEE_Result read_block(struct sql_fs_fd *fdp, size_t bnum, uint8_t *data) -{ - TEE_Result res; - size_t ct_size = block_size_raw(); - size_t out_size = BLOCK_SIZE; - ssize_t pos = block_pos_raw(bnum); - size_t bytes; - void *ct; - struct tee_fs_rpc_operation op; - - res = tee_fs_rpc_read_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, pos, - ct_size, &ct); + res = get_offs_size(type, idx, &offs, &size); if (res != TEE_SUCCESS) return res; - res = tee_fs_rpc_read_final(&op, &bytes); - if (res != TEE_SUCCESS) - return res; - if (!bytes) - return TEE_SUCCESS; /* Block does not exist */ - return tee_fs_decrypt_file(BLOCK_FILE, ct, bytes, data, - &out_size, fdp->encrypted_fek); + return tee_fs_rpc_write_init(op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, + offs, size, data); } -/* Write one block of user data */ -static TEE_Result write_block(struct sql_fs_fd *fdp, size_t bnum, uint8_t *data) -{ - TEE_Result res; - size_t ct_size = block_size_raw(); - ssize_t pos = block_pos_raw(bnum); - void *ct; - struct tee_fs_rpc_operation op; - - res = tee_fs_rpc_write_init(&op, OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd, pos, - ct_size, &ct); - if (res != TEE_SUCCESS) - return res; - - res = tee_fs_encrypt_file(BLOCK_FILE, data, BLOCK_SIZE, ct, - &ct_size, fdp->encrypted_fek); - if (res != TEE_SUCCESS) - return res; - - return tee_fs_rpc_write_final(&op); -} +static const struct tee_fs_htree_storage sql_fs_storage_ops = { + .block_size = BLOCK_SIZE, + .rpc_read_init = sql_fs_rpc_read_init, + .rpc_read_final = tee_fs_rpc_read_final, + .rpc_write_init = sql_fs_rpc_write_init, + .rpc_write_final = tee_fs_rpc_write_final, +}; /* * Partial write (< BLOCK_SIZE) into a block: read/update/write @@ -296,16 +237,21 @@ static TEE_Result write_block_partial(struct sql_fs_fd *fdp, size_t bnum, if (!buf) return TEE_ERROR_OUT_OF_MEMORY; - res = read_block(fdp, bnum, buf); - if (res != TEE_SUCCESS) - goto exit; + if (bnum * BLOCK_SIZE < + ROUNDUP(tee_fs_htree_get_meta(fdp->ht)->length, BLOCK_SIZE)) { + res = tee_fs_htree_read_block(&fdp->ht, bnum, buf); + if (res != TEE_SUCCESS) + goto exit; + } else { + memset(buf, 0, BLOCK_SIZE); + } if (data) memcpy(buf + offset, data, len); else memset(buf + offset, 0, len); - res = write_block(fdp, bnum, buf); + res = tee_fs_htree_write_block(&fdp->ht, bnum, buf); exit: free(buf); return res; @@ -315,32 +261,42 @@ static TEE_Result sql_fs_ftruncate_internal(struct sql_fs_fd *fdp, tee_fs_off_t new_length) { TEE_Result res; - tee_fs_off_t old_length; - - old_length = (tee_fs_off_t)fdp->meta.length; + struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); - if (new_length == old_length) + if ((size_t)new_length == meta->length) return TEE_SUCCESS; sql_fs_begin_transaction_rpc(); - if (new_length < old_length) { + if ((size_t)new_length < meta->length) { /* Trim unused blocks */ - int old_last_block = block_num(old_length); + int old_last_block = block_num(meta->length); int last_block = block_num(new_length); - tee_fs_off_t off; if (last_block < old_last_block) { - off = block_pos_raw(last_block); + size_t offs; + size_t sz; + + res = get_offs_size(TEE_FS_HTREE_TYPE_BLOCK, + ROUNDUP(new_length, BLOCK_SIZE) / + BLOCK_SIZE, &offs, &sz); + if (res != TEE_SUCCESS) + goto exit; + + res = tee_fs_htree_truncate(&fdp->ht, + new_length / BLOCK_SIZE); + if (res != TEE_SUCCESS) + goto exit; + res = tee_fs_rpc_truncate(OPTEE_MSG_RPC_CMD_SQL_FS, - fdp->fd, off); + fdp->fd, offs + sz); if (res != TEE_SUCCESS) goto exit; } } else { /* Extend file with zeroes */ - tee_fs_off_t off = old_length % BLOCK_SIZE; - size_t bnum = block_num(old_length); + tee_fs_off_t off = meta->length % BLOCK_SIZE; + size_t bnum = block_num(meta->length); size_t end_bnum = block_num(new_length); while (bnum <= end_bnum) { @@ -354,70 +310,28 @@ static TEE_Result sql_fs_ftruncate_internal(struct sql_fs_fd *fdp, } } - fdp->meta.length = new_length; - res = write_meta(fdp); + meta->length = new_length; + res = TEE_SUCCESS; exit: + if (res == TEE_SUCCESS) + res = tee_fs_htree_sync_to_storage(&fdp->ht); sql_fs_end_transaction_rpc(res != TEE_SUCCESS); return res; } -static TEE_Result sql_fs_seek(struct tee_file_handle *fh, int32_t offset, - TEE_Whence whence, int32_t *new_offs) -{ - TEE_Result res; - struct sql_fs_fd *fdp = (struct sql_fs_fd *)fh; - tee_fs_off_t pos; - - mutex_lock(&sql_fs_mutex); - - switch (whence) { - case TEE_DATA_SEEK_SET: - pos = offset; - break; - - case TEE_DATA_SEEK_CUR: - pos = fdp->pos + offset; - break; - - case TEE_DATA_SEEK_END: - pos = fdp->meta.length + offset; - break; - - default: - res = TEE_ERROR_BAD_PARAMETERS; - goto exit_ret; - } - - if (pos > TEE_DATA_MAX_POSITION) { - EMSG("Position is beyond TEE_DATA_MAX_POSITION"); - res = TEE_ERROR_BAD_PARAMETERS; - goto exit_ret; - } - - if (pos < 0) - pos = 0; - - fdp->pos = pos; - if (new_offs) - *new_offs = pos; - res = TEE_SUCCESS; -exit_ret: - mutex_unlock(&sql_fs_mutex); - return res; -} - static void sql_fs_close(struct tee_file_handle **fh) { struct sql_fs_fd *fdp = (struct sql_fs_fd *)*fh; if (fdp) { - tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd); + tee_fs_htree_close(&fdp->ht); + tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd); free(fdp); *fh = NULL; } } -static TEE_Result open_internal(const char *file, bool create, +static TEE_Result open_internal(struct tee_pobj *po, bool create, struct tee_file_handle **fh) { TEE_Result res; @@ -432,36 +346,41 @@ static TEE_Result open_internal(const char *file, bool create, mutex_lock(&sql_fs_mutex); if (create) - res = create_meta(fdp, file); + res = tee_fs_rpc_create(OPTEE_MSG_RPC_CMD_SQL_FS, po, &fdp->fd); else - res = read_meta(fdp, file); + res = tee_fs_rpc_open(OPTEE_MSG_RPC_CMD_SQL_FS, po, &fdp->fd); + if (res != TEE_SUCCESS) + goto out; + res = tee_fs_htree_open(create, &sql_fs_storage_ops, fdp, &fdp->ht); +out: if (res == TEE_SUCCESS) { *fh = (struct tee_file_handle *)fdp; } else { if (fdp && fdp->fd != -1) tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_SQL_FS, fdp->fd); if (created) - tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, file); + tee_fs_rpc_remove(OPTEE_MSG_RPC_CMD_SQL_FS, po); free(fdp); } mutex_unlock(&sql_fs_mutex); return res; } -static TEE_Result sql_fs_open(const char *file, struct tee_file_handle **fh) +static TEE_Result sql_fs_open(struct tee_pobj *po, struct tee_file_handle **fh) { - return open_internal(file, false, fh); + return open_internal(po, false, fh); } -static TEE_Result sql_fs_create(const char *file, struct tee_file_handle **fh) +static TEE_Result sql_fs_create(struct tee_pobj *po, + struct tee_file_handle **fh) { - return open_internal(file, true, fh); + return open_internal(po, true, fh); } -static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf, - size_t *len) +static TEE_Result sql_fs_read(struct tee_file_handle *fh, size_t pos, + void *buf, size_t *len) { TEE_Result res; struct sql_fs_fd *fdp = (struct sql_fs_fd *)fh; @@ -470,14 +389,15 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf, uint8_t *block = NULL; int start_block_num; int end_block_num; + size_t file_size; mutex_lock(&sql_fs_mutex); - if ((fdp->pos + remain_bytes) < remain_bytes || - fdp->pos > (tee_fs_off_t)fdp->meta.length) + file_size = tee_fs_htree_get_meta(fdp->ht)->length; + if ((pos + remain_bytes) < remain_bytes || pos > file_size) remain_bytes = 0; - else if (fdp->pos + remain_bytes > fdp->meta.length) - remain_bytes = fdp->meta.length - fdp->pos; + else if (pos + remain_bytes > file_size) + remain_bytes = file_size - pos; *len = remain_bytes; @@ -486,8 +406,8 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf, goto exit; } - start_block_num = block_num(fdp->pos); - end_block_num = block_num(fdp->pos + remain_bytes - 1); + start_block_num = block_num(pos); + end_block_num = block_num(pos + remain_bytes - 1); block = malloc(BLOCK_SIZE); if (!block) { @@ -496,17 +416,13 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf, } while (start_block_num <= end_block_num) { - tee_fs_off_t offset = fdp->pos % BLOCK_SIZE; + size_t offset = pos % BLOCK_SIZE; size_t size_to_read = MIN(remain_bytes, (size_t)BLOCK_SIZE); if (size_to_read + offset > BLOCK_SIZE) size_to_read = BLOCK_SIZE - offset; - /* - * REVISIT: implement read_block_partial() since we have - * write_block_partial()? - */ - res = read_block(fdp, start_block_num, block); + res = tee_fs_htree_read_block(&fdp->ht, start_block_num, block); if (res != TEE_SUCCESS) goto exit; @@ -514,7 +430,7 @@ static TEE_Result sql_fs_read(struct tee_file_handle *fh, void *buf, data_ptr += size_to_read; remain_bytes -= size_to_read; - fdp->pos += size_to_read; + pos += size_to_read; start_block_num++; } @@ -525,11 +441,12 @@ exit: return res; } -static TEE_Result sql_fs_write(struct tee_file_handle *fh, const void *buf, - size_t len) +static TEE_Result sql_fs_write(struct tee_file_handle *fh, size_t pos, + const void *buf, size_t len) { TEE_Result res; struct sql_fs_fd *fdp = (struct sql_fs_fd *)fh; + struct tee_fs_htree_meta *meta = tee_fs_htree_get_meta(fdp->ht); size_t remain_bytes = len; const uint8_t *data_ptr = buf; int start_block_num; @@ -542,18 +459,18 @@ static TEE_Result sql_fs_write(struct tee_file_handle *fh, const void *buf, sql_fs_begin_transaction_rpc(); - if (fdp->meta.length < (size_t)fdp->pos) { + if (meta->length < pos) { /* Fill hole */ - res = sql_fs_ftruncate_internal(fdp, fdp->pos); + res = sql_fs_ftruncate_internal(fdp, pos); if (res != TEE_SUCCESS) goto exit; } - start_block_num = block_num(fdp->pos); - end_block_num = block_num(fdp->pos + len - 1); + start_block_num = block_num(pos); + end_block_num = block_num(pos + len - 1); while (start_block_num <= end_block_num) { - tee_fs_off_t offset = fdp->pos % BLOCK_SIZE; + size_t offset = pos % BLOCK_SIZE; size_t size_to_write = MIN(remain_bytes, (size_t)BLOCK_SIZE); if (size_to_write + offset > BLOCK_SIZE) @@ -566,16 +483,17 @@ static TEE_Result sql_fs_write(struct tee_file_handle *fh, const void *buf, data_ptr += size_to_write; remain_bytes -= size_to_write; - fdp->pos += size_to_write; + pos += size_to_write; start_block_num++; } - if (fdp->meta.length < (size_t)fdp->pos) { - fdp->meta.length = fdp->pos; - res = write_meta(fdp); - } + if (pos > meta->length) + meta->length = pos; + exit: + if (res == TEE_SUCCESS) + res = tee_fs_htree_sync_to_storage(&fdp->ht); sql_fs_end_transaction_rpc(res != TEE_SUCCESS); mutex_unlock(&sql_fs_mutex); return res; @@ -599,7 +517,6 @@ const struct tee_file_operations sql_fs_ops = { .close = sql_fs_close, .read = sql_fs_read, .write = sql_fs_write, - .seek = sql_fs_seek, .truncate = sql_fs_truncate, .opendir = sql_fs_opendir_rpc, diff --git a/core/tee/tee_svc_storage.c b/core/tee/tee_svc_storage.c index 916ddca..d852be1 100644 --- a/core/tee/tee_svc_storage.c +++ b/core/tee/tee_svc_storage.c @@ -32,7 +32,6 @@ #include <string.h> #include <tee_api_defines_extensions.h> #include <tee_api_defines.h> -#include <tee/tee_fs_defs.h> #include <tee/tee_fs.h> #include <tee/tee_obj.h> #include <tee/tee_pobj.h> @@ -99,12 +98,6 @@ struct tee_storage_enum { const struct tee_file_operations *fops; }; -/* - * Protect TA storage directory: avoid race conditions between (create - * directory + create file) and (remove directory) - */ -static struct mutex ta_dir_mutex = MUTEX_INITIALIZER; - static TEE_Result tee_svc_storage_get_enum(struct user_ta_ctx *utc, uint32_t enum_id, struct tee_storage_enum **e_out) @@ -140,114 +133,77 @@ static TEE_Result tee_svc_close_enum(struct user_ta_ctx *utc, } /* "/TA_uuid/object_id" or "/TA_uuid/.object_id" */ -char *tee_svc_storage_create_filename(struct tee_ta_session *sess, - void *object_id, - uint32_t object_id_len, - bool transient) +TEE_Result tee_svc_storage_create_filename(void *buf, size_t blen, + struct tee_pobj *po, bool transient) { - uint8_t *file; + uint8_t *file = buf; uint32_t pos = 0; uint32_t hslen = 1 /* Leading slash */ - + TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID) + object_id_len) + + TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID) + po->obj_id_len) + 1; /* Intermediate slash */ /* +1 for the '.' (temporary persistent object) */ if (transient) hslen++; - file = malloc(hslen); - if (!file) - return NULL; + if (blen < hslen) + return TEE_ERROR_SHORT_BUFFER; file[pos++] = '/'; - pos += tee_b2hs((uint8_t *)&sess->ctx->uuid, &file[pos], + pos += tee_b2hs((uint8_t *)&po->uuid, &file[pos], sizeof(TEE_UUID), hslen); file[pos++] = '/'; if (transient) file[pos++] = '.'; - tee_b2hs(object_id, file + pos, object_id_len, hslen - pos); + tee_b2hs(po->obj_id, file + pos, po->obj_id_len, hslen - pos); - return (char *)file; + return TEE_SUCCESS; } /* "/TA_uuid" */ -char *tee_svc_storage_create_dirname(struct tee_ta_session *sess) +TEE_Result tee_svc_storage_create_dirname(void *buf, size_t blen, + const TEE_UUID *uuid) { - uint8_t *dir; + uint8_t *dir = buf; uint32_t hslen = TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID)) + 1; - dir = malloc(hslen); - if (!dir) - return NULL; + if (blen < hslen) + return TEE_ERROR_SHORT_BUFFER; dir[0] = '/'; - tee_b2hs((uint8_t *)&sess->ctx->uuid, dir + 1, sizeof(TEE_UUID), - hslen); + tee_b2hs((uint8_t *)uuid, dir + 1, sizeof(TEE_UUID), hslen); - return (char *)dir; + return TEE_SUCCESS; } static TEE_Result tee_svc_storage_remove_corrupt_obj( struct tee_ta_session *sess, struct tee_obj *o) { - TEE_Result res; - char *file = NULL; - const struct tee_file_operations *fops = o->pobj->fops; - - file = tee_svc_storage_create_filename(sess, - o->pobj->obj_id, - o->pobj->obj_id_len, - false); - if (file == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - + o->pobj->fops->remove(o->pobj); tee_obj_close(to_user_ta_ctx(sess->ctx), o); - fops->remove(file); - free(file); - res = TEE_SUCCESS; - -exit: - return res; + return TEE_SUCCESS; } -static TEE_Result tee_svc_storage_read_head(struct tee_ta_session *sess, - struct tee_obj *o) +static TEE_Result tee_svc_storage_read_head(struct tee_obj *o) { TEE_Result res = TEE_SUCCESS; size_t bytes; struct tee_svc_storage_head head; - char *file = NULL; - const struct tee_file_operations *fops; + const struct tee_file_operations *fops = o->pobj->fops; void *attr = NULL; - if (o == NULL || o->pobj == NULL) - return TEE_ERROR_BAD_PARAMETERS; - - fops = o->pobj->fops; - - file = tee_svc_storage_create_filename(sess, - o->pobj->obj_id, - o->pobj->obj_id_len, - false); - if (file == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - assert(!o->fh); - res = fops->open(file, &o->fh); + res = fops->open(o->pobj, &o->fh); if (res != TEE_SUCCESS) goto exit; /* read head */ bytes = sizeof(struct tee_svc_storage_head); - res = fops->read(o->fh, &head, &bytes); + res = fops->read(o->fh, 0, &head, &bytes); if (res != TEE_SUCCESS) { if (res == TEE_ERROR_CORRUPT_OBJECT) EMSG("Head corrupt\n"); @@ -263,6 +219,7 @@ static TEE_Result tee_svc_storage_read_head(struct tee_ta_session *sess, if (res != TEE_SUCCESS) goto exit; + o->ds_pos = sizeof(struct tee_svc_storage_head) + head.meta_size; if (head.meta_size) { attr = malloc(head.meta_size); if (!attr) { @@ -272,7 +229,8 @@ static TEE_Result tee_svc_storage_read_head(struct tee_ta_session *sess, /* read meta */ bytes = head.meta_size; - res = fops->read(o->fh, attr, &bytes); + res = fops->read(o->fh, sizeof(struct tee_svc_storage_head), + attr, &bytes); if (res != TEE_SUCCESS || bytes != head.meta_size) { res = TEE_ERROR_CORRUPT_OBJECT; goto exit; @@ -291,7 +249,6 @@ static TEE_Result tee_svc_storage_read_head(struct tee_ta_session *sess, exit: free(attr); - free(file); return res; } @@ -299,63 +256,22 @@ exit: static TEE_Result tee_svc_storage_update_head(struct tee_obj *o, uint32_t ds_size) { - TEE_Result res; - const struct tee_file_operations *fops; - int32_t old_off; + size_t pos = offsetof(struct tee_svc_storage_head, ds_size); - fops = o->pobj->fops; - - /* save original offset */ - res = fops->seek(o->fh, 0, TEE_DATA_SEEK_CUR, &old_off); - if (res != TEE_SUCCESS) - return res; - - /* update head.ds_size */ - res = fops->seek(o->fh, offsetof(struct tee_svc_storage_head, - ds_size), TEE_DATA_SEEK_SET, NULL); - if (res != TEE_SUCCESS) - return res; - - res = fops->write(o->fh, &ds_size, sizeof(uint32_t)); - if (res != TEE_SUCCESS) - return res; - - /* restore original offset */ - res = fops->seek(o->fh, old_off, TEE_DATA_SEEK_SET, NULL); - return res; + return o->pobj->fops->write(o->fh, pos, &ds_size, sizeof(uint32_t)); } -static TEE_Result tee_svc_storage_init_file(struct tee_ta_session *sess, - struct tee_obj *o, +static TEE_Result tee_svc_storage_init_file(struct tee_obj *o, struct tee_obj *attr_o, void *data, uint32_t len) { TEE_Result res = TEE_SUCCESS; struct tee_svc_storage_head head; - char *tmpfile = NULL; - const struct tee_file_operations *fops; + const struct tee_file_operations *fops = o->pobj->fops; void *attr = NULL; size_t attr_size = 0; - if (o == NULL || o->pobj == NULL) - return TEE_ERROR_BAD_PARAMETERS; - - fops = o->pobj->fops; - - /* create temporary persistent object filename */ - tmpfile = tee_svc_storage_create_filename(sess, - o->pobj->obj_id, - o->pobj->obj_id_len, - true); - - if (tmpfile == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - - mutex_lock(&ta_dir_mutex); - res = fops->create(tmpfile, &o->fh); - mutex_unlock(&ta_dir_mutex); + res = fops->create(o->pobj, &o->fh); if (res != TEE_SUCCESS) goto exit; @@ -389,6 +305,8 @@ static TEE_Result tee_svc_storage_init_file(struct tee_ta_session *sess, goto exit; } + o->ds_pos = sizeof(struct tee_svc_storage_head) + attr_size; + /* write head */ head.magic = TEE_SVC_STORAGE_MAGIC; head.head_size = sizeof(struct tee_svc_storage_head); @@ -401,12 +319,13 @@ static TEE_Result tee_svc_storage_init_file(struct tee_ta_session *sess, head.have_attrs = o->have_attrs; /* write head */ - res = fops->write(o->fh, &head, sizeof(struct tee_svc_storage_head)); + res = fops->write(o->fh, 0, &head, sizeof(struct tee_svc_storage_head)); if (res != TEE_SUCCESS) goto exit; /* write meta */ - res = fops->write(o->fh, attr, attr_size); + res = fops->write(o->fh, sizeof(struct tee_svc_storage_head), + attr, attr_size); if (res != TEE_SUCCESS) goto exit; @@ -415,11 +334,10 @@ static TEE_Result tee_svc_storage_init_file(struct tee_ta_session *sess, /* write data to fs if needed */ if (data && len) - res = fops->write(o->fh, data, len); + res = fops->write(o->fh, o->ds_pos, data, len); exit: free(attr); - free(tmpfile); fops->close(&o->fh); return res; @@ -462,7 +380,7 @@ TEE_Result syscall_storage_obj_open(unsigned long storage_id, void *object_id, goto err; res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, - object_id_len, flags, fops, &po); + object_id_len, flags, false, fops, &po); if (res != TEE_SUCCESS) goto err; @@ -479,7 +397,7 @@ TEE_Result syscall_storage_obj_open(unsigned long storage_id, void *object_id, o->pobj = po; tee_obj_add(utc, o); - res = tee_svc_storage_read_head(sess, o); + res = tee_svc_storage_read_head(o); if (res != TEE_SUCCESS) { if (res == TEE_ERROR_CORRUPT_OBJECT) { EMSG("Object corrupt"); @@ -496,11 +414,6 @@ TEE_Result syscall_storage_obj_open(unsigned long storage_id, void *object_id, if (res != TEE_SUCCESS) goto oclose; - res = fops->seek(o->fh, sizeof(struct tee_svc_storage_head) + attr_size, - TEE_DATA_SEEK_SET, NULL); - if (res != TEE_SUCCESS) - goto err; - goto exit; oclose: @@ -528,9 +441,7 @@ TEE_Result syscall_storage_obj_create(unsigned long storage_id, void *object_id, struct tee_ta_session *sess; struct tee_obj *o = NULL; struct tee_obj *attr_o = NULL; - char *file = NULL; struct tee_pobj *po = NULL; - char *tmpfile = NULL; struct user_ta_ctx *utc; const struct tee_file_operations *fops = file_ops(storage_id); size_t attr_size; @@ -555,7 +466,7 @@ TEE_Result syscall_storage_obj_create(unsigned long storage_id, void *object_id, goto err; res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, - object_id_len, flags, fops, &po); + object_id_len, flags, true, fops, &po); if (res != TEE_SUCCESS) goto err; @@ -588,33 +499,17 @@ TEE_Result syscall_storage_obj_create(unsigned long storage_id, void *object_id, goto err; } - res = tee_svc_storage_init_file(sess, o, attr_o, data, len); + res = tee_svc_storage_init_file(o, attr_o, data, len); if (res != TEE_SUCCESS) goto err; - /* create persistent object filename */ - file = tee_svc_storage_create_filename(sess, object_id, - object_id_len, false); - if (file == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto err; - } - - /* create temporary persistent object filename */ - tmpfile = tee_svc_storage_create_filename(sess, object_id, - object_id_len, - true); - if (tmpfile == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto err; - } - /* rename temporary persistent object filename */ - res = fops->rename(tmpfile, file, !!(flags & TEE_DATA_FLAG_OVERWRITE)); + po->temporary = false; + res = fops->rename(po, NULL, !!(flags & TEE_DATA_FLAG_OVERWRITE)); if (res != TEE_SUCCESS) goto rmfile; - res = fops->open(file, &o->fh); + res = fops->open(po, &o->fh); if (res != TEE_SUCCESS) goto err; @@ -628,35 +523,26 @@ TEE_Result syscall_storage_obj_create(unsigned long storage_id, void *object_id, if (res != TEE_SUCCESS) goto oclose; - res = fops->seek(o->fh, sizeof(struct tee_svc_storage_head) + attr_size, - TEE_DATA_SEEK_SET, NULL); - if (res != TEE_SUCCESS) - goto oclose; - - goto exit; + return TEE_SUCCESS; oclose: tee_obj_close(utc, o); - goto exit; + return res; rmfile: - fops->remove(tmpfile); + fops->remove(po); err: if (res == TEE_ERROR_NO_DATA || res == TEE_ERROR_BAD_FORMAT) res = TEE_ERROR_CORRUPT_OBJECT; - if (res == TEE_ERROR_CORRUPT_OBJECT && file) - fops->remove(file); + if (res == TEE_ERROR_CORRUPT_OBJECT && po) + fops->remove(po); if (o) fops->close(&o->fh); if (po) tee_pobj_release(po); free(o); -exit: - free(file); - free(tmpfile); - return res; } @@ -665,9 +551,7 @@ TEE_Result syscall_storage_obj_del(unsigned long obj) TEE_Result res; struct tee_ta_session *sess; struct tee_obj *o; - char *file; struct user_ta_ctx *utc; - const struct tee_file_operations *fops; res = tee_ta_get_current_session(&sess); if (res != TEE_SUCCESS) @@ -684,16 +568,9 @@ TEE_Result syscall_storage_obj_del(unsigned long obj) if (o->pobj == NULL || o->pobj->obj_id == NULL) return TEE_ERROR_BAD_STATE; - file = tee_svc_storage_create_filename(sess, o->pobj->obj_id, - o->pobj->obj_id_len, false); - if (file == NULL) - return TEE_ERROR_OUT_OF_MEMORY; - - fops = o->pobj->fops; + res = o->pobj->fops->remove(o->pobj); tee_obj_close(utc, o); - res = fops->remove(file); - free(file); return res; } @@ -743,31 +620,16 @@ TEE_Result syscall_storage_obj_rename(unsigned long obj, void *object_id, if (res != TEE_SUCCESS) goto exit; - /* get new ds name */ - new_file = tee_svc_storage_create_filename(sess, object_id, - object_id_len, false); - if (new_file == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - - old_file = tee_svc_storage_create_filename(sess, o->pobj->obj_id, - o->pobj->obj_id_len, false); - if (old_file == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - /* reserve dest name */ fops = o->pobj->fops; res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, object_id_len, TEE_DATA_FLAG_ACCESS_WRITE_META, - fops, &po); + false, fops, &po); if (res != TEE_SUCCESS) goto exit; /* move */ - res = fops->rename(old_file, new_file, false /* no overwrite */); + res = fops->rename(o->pobj, po, false /* no overwrite */); if (res == TEE_ERROR_GENERIC) goto exit; @@ -843,48 +705,39 @@ TEE_Result syscall_storage_reset_enum(unsigned long obj_enum) if (res != TEE_SUCCESS) return res; - e->fops->closedir(e->dir); - e->fops = NULL; - e->dir = NULL; + if (e->fops) { + e->fops->closedir(e->dir); + e->fops = NULL; + e->dir = NULL; + } + assert(!e->dir); return TEE_SUCCESS; } -static TEE_Result tee_svc_storage_set_enum(char *d_name, +static TEE_Result tee_svc_storage_set_enum(struct tee_fs_dirent *d, const struct tee_file_operations *fops, struct tee_obj *o) { - TEE_Result res; - uint32_t blen; - uint32_t hslen; - o->info.handleFlags = TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED; o->info.objectUsage = TEE_USAGE_DEFAULT; - hslen = strlen(d_name); - blen = TEE_HS2B_BBUF_SIZE(hslen); - o->pobj->obj_id = malloc(blen); - if (!o->pobj->obj_id) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - tee_hs2b((uint8_t *)d_name, o->pobj->obj_id, hslen, blen); - o->pobj->obj_id_len = blen; - o->pobj->fops = fops; - - res = TEE_SUCCESS; + o->pobj->obj_id = malloc(d->oidlen); + if (!o->pobj->obj_id) + return TEE_ERROR_OUT_OF_MEMORY; -exit: - return res; + memcpy(o->pobj->obj_id, d->oid, d->oidlen); + o->pobj->obj_id_len = d->oidlen; + o->pobj->fops = fops; + return TEE_SUCCESS; } TEE_Result syscall_storage_start_enum(unsigned long obj_enum, unsigned long storage_id) { struct tee_storage_enum *e; - char *dir; TEE_Result res; struct tee_ta_session *sess; const struct tee_file_operations *fops = file_ops(storage_id); @@ -902,17 +755,8 @@ TEE_Result syscall_storage_start_enum(unsigned long obj_enum, return TEE_ERROR_ITEM_NOT_FOUND; e->fops = fops; - dir = tee_svc_storage_create_dirname(sess); - if (dir == NULL) { - res = TEE_ERROR_OUT_OF_MEMORY; - goto exit; - } - assert(!e->dir); - res = fops->opendir(dir, &e->dir); - free(dir); -exit: - return res; + return fops->opendir(&sess->ctx->uuid, &e->dir); } TEE_Result syscall_storage_next_enum(unsigned long obj_enum, @@ -975,11 +819,12 @@ TEE_Result syscall_storage_next_enum(unsigned long obj_enum, goto exit; } - res = tee_svc_storage_set_enum(d->d_name, e->fops, o); + o->pobj->uuid = sess->ctx->uuid; + res = tee_svc_storage_set_enum(d, e->fops, o); if (res != TEE_SUCCESS) goto exit; - res = tee_svc_storage_read_head(sess, o); + res = tee_svc_storage_read_head(o); if (res != TEE_SUCCESS) goto exit; @@ -1040,7 +885,8 @@ TEE_Result syscall_storage_obj_read(unsigned long obj, void *data, size_t len, goto exit; bytes = len; - res = o->pobj->fops->read(o->fh, data, &bytes); + res = o->pobj->fops->read(o->fh, o->ds_pos + o->info.dataPosition, + data, &bytes); if (res != TEE_SUCCESS) { EMSG("Error code=%x\n", (uint32_t)res); if (res == TEE_ERROR_CORRUPT_OBJECT) { @@ -1092,7 +938,8 @@ TEE_Result syscall_storage_obj_write(unsigned long obj, void *data, size_t len) if (res != TEE_SUCCESS) goto exit; - res = o->pobj->fops->write(o->fh, data, len); + res = o->pobj->fops->write(o->fh, o->ds_pos + o->info.dataPosition, + data, len); if (res != TEE_SUCCESS) goto exit; @@ -1163,39 +1010,50 @@ TEE_Result syscall_storage_obj_seek(unsigned long obj, int32_t offset, TEE_Result res; struct tee_ta_session *sess; struct tee_obj *o; - int32_t off; size_t attr_size; + tee_fs_off_t new_pos; res = tee_ta_get_current_session(&sess); if (res != TEE_SUCCESS) - goto exit; + return res; res = tee_obj_get(to_user_ta_ctx(sess->ctx), tee_svc_uref_to_vaddr(obj), &o); if (res != TEE_SUCCESS) - goto exit; + return res; - if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { - res = TEE_ERROR_BAD_STATE; - goto exit; - } + if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) + return TEE_ERROR_BAD_STATE; res = tee_obj_attr_to_binary(o, NULL, &attr_size); if (res != TEE_SUCCESS) - goto exit; + return res; - off = offset; - if (whence == TEE_DATA_SEEK_SET) - off += sizeof(struct tee_svc_storage_head) + attr_size; + switch (whence) { + case TEE_DATA_SEEK_SET: + new_pos = offset; + break; + case TEE_DATA_SEEK_CUR: + new_pos = o->info.dataPosition + offset; + break; + case TEE_DATA_SEEK_END: + new_pos = o->info.dataSize + offset; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } - res = o->pobj->fops->seek(o->fh, off, whence, &off); - if (res != TEE_SUCCESS) - goto exit; - o->info.dataPosition = off - (sizeof(struct tee_svc_storage_head) + - attr_size); + if (new_pos < 0) + new_pos = 0; -exit: - return res; + if (new_pos > TEE_DATA_MAX_POSITION) { + EMSG("Position is beyond TEE_DATA_MAX_POSITION"); + return TEE_ERROR_BAD_PARAMETERS; + } + + o->info.dataPosition = new_pos; + + return TEE_SUCCESS; } void tee_svc_storage_close_all_enum(struct user_ta_ctx *utc) |