Add storage partition authorizer

To allow for deployment specific authorization policy and lazy
resolution of a partition owner to a client ID, per-partition
access control can now use an authorizer function for cases where
no ACL has been configured during initialisation. This facility
may be used for SP deployments of the block storage service
provider to enable an FFA_GET_PARTITION_INFO to be performed
as a way to authorize a client on opening a partition.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I53ee3d3961fe5d2ce10bea8a692c87ae60ed1f1f
diff --git a/components/service/block_storage/block_store/component.cmake b/components/service/block_storage/block_store/component.cmake
index b69ca00..66a3242 100644
--- a/components/service/block_storage/block_store/component.cmake
+++ b/components/service/block_storage/block_store/component.cmake
@@ -11,4 +11,5 @@
 target_sources(${TGT} PRIVATE
 	"${CMAKE_CURRENT_LIST_DIR}/block_store.c"
 	"${CMAKE_CURRENT_LIST_DIR}/storage_partition.c"
+	"${CMAKE_CURRENT_LIST_DIR}/storage_partition_acl.c"
 	)
diff --git a/components/service/block_storage/block_store/device/block_device.c b/components/service/block_storage/block_store/device/block_device.c
index b3e74b1..f45215a 100644
--- a/components/service/block_storage/block_store/device/block_device.c
+++ b/components/service/block_storage/block_store/device/block_device.c
@@ -48,8 +48,10 @@
 {
 	psa_status_t status = PSA_ERROR_NOT_PERMITTED;
 
-	if (storage_partition_is_guid_matched(&block_device->storage_partition, partition_guid) &&
-		storage_partition_is_access_permitted(&block_device->storage_partition, client_id)) {
+	if (storage_partition_is_guid_matched(&block_device->storage_partition,
+			partition_guid) &&
+		storage_partition_is_open_permitted(&block_device->storage_partition,
+			client_id, NULL)) {
 
 		*handle = BLOCK_DEVICE_PARTITION_HANDLE;
 		status = PSA_SUCCESS;
diff --git a/components/service/block_storage/block_store/partitioned/partitioned_block_store.c b/components/service/block_storage/block_store/partitioned/partitioned_block_store.c
index 3c5c1fd..f6c8355 100644
--- a/components/service/block_storage/block_store/partitioned/partitioned_block_store.c
+++ b/components/service/block_storage/block_store/partitioned/partitioned_block_store.c
@@ -84,7 +84,7 @@
 	const struct uuid_octets *partition_guid,
 	storage_partition_handle_t *handle)
 {
-	const struct partitioned_block_store *partitioned_block_store =
+	struct partitioned_block_store *partitioned_block_store =
 		(struct partitioned_block_store*)context;
 
 	size_t partition_index = 0;
@@ -95,10 +95,12 @@
 
 	if (status == PSA_SUCCESS) {
 
-		const struct storage_partition *partition =
+		struct storage_partition *partition =
 			&partitioned_block_store->storage_partition[partition_index];
 
-		if (storage_partition_is_access_permitted(partition, client_id)) {
+		if (storage_partition_is_open_permitted(partition,
+				client_id,
+				partitioned_block_store->authorizer)) {
 
 			*handle = (storage_partition_handle_t)partition_index;
 		}
@@ -271,7 +273,8 @@
 	struct partitioned_block_store *partitioned_block_store,
 	uint32_t local_client_id,
 	const struct uuid_octets *back_store_guid,
-	struct block_store *back_store)
+	struct block_store *back_store,
+	storage_partition_authorizer authorizer)
 {
 	/* Define concrete block store interface */
 	static const struct block_store_interface interface =
@@ -291,6 +294,9 @@
 	/* Note the local client ID - this corresponds to the local environment */
 	partitioned_block_store->local_client_id = local_client_id;
 
+	/* Note the environment specific authorizer function */
+	partitioned_block_store->authorizer = authorizer;
+
 	/* Initially no partitions. */
 	partitioned_block_store->num_partitions = 0;
 
diff --git a/components/service/block_storage/block_store/partitioned/partitioned_block_store.h b/components/service/block_storage/block_store/partitioned/partitioned_block_store.h
index adf5fd8..306a3db 100644
--- a/components/service/block_storage/block_store/partitioned/partitioned_block_store.h
+++ b/components/service/block_storage/block_store/partitioned/partitioned_block_store.h
@@ -11,6 +11,7 @@
 #include <stdbool.h>
 #include "service/block_storage/block_store/block_store.h"
 #include "service/block_storage/block_store/storage_partition.h"
+#include "service/block_storage/block_store/storage_partition_acl.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -33,6 +34,7 @@
 {
 	struct block_store base_block_store;
 	uint32_t local_client_id;
+	storage_partition_authorizer authorizer;
 	size_t num_partitions;
 	struct storage_partition storage_partition[PARTITIONED_BLOCK_STORE_MAX_PARTITIONS];
 	storage_partition_handle_t back_store_handle;
@@ -51,6 +53,7 @@
  * \param[in]  local_client_id   Client ID corresponding to the current environment
  * \param[in]  back_store_guid   The partition GUID to use in the underlying back store
  * \param[in]  back_store        The associated back store
+ * \param[in]  authorizer		 Optional authorizer function for authorizing clients
  *
  * \return Pointer to block_store or NULL on failure
  */
@@ -58,7 +61,8 @@
 	struct partitioned_block_store *partitioned_block_store,
 	uint32_t local_client_id,
 	const struct uuid_octets *back_store_guid,
-	struct block_store *back_store);
+	struct block_store *back_store,
+	storage_partition_authorizer authorizer);
 
 /**
  * \brief De-initialize a partitioned_block_store
diff --git a/components/service/block_storage/block_store/partitioned/test/partitioned_block_store_tests.cpp b/components/service/block_storage/block_store/partitioned/test/partitioned_block_store_tests.cpp
index 3bafb1b..efce19e 100644
--- a/components/service/block_storage/block_store/partitioned/test/partitioned_block_store_tests.cpp
+++ b/components/service/block_storage/block_store/partitioned/test/partitioned_block_store_tests.cpp
@@ -31,7 +31,8 @@
 			&m_partitioned_store,
 			LOCAL_CLIENT_ID,
 			&back_store_guid,
-			back_store);
+			back_store,
+			NULL);
 
 		CHECK_TRUE(back_store);
 
diff --git a/components/service/block_storage/block_store/storage_partition.c b/components/service/block_storage/block_store/storage_partition.c
index 5489e6b..a3b1331 100644
--- a/components/service/block_storage/block_store/storage_partition.c
+++ b/components/service/block_storage/block_store/storage_partition.c
@@ -15,11 +15,12 @@
 	size_t num_blocks,
 	size_t block_size)
 {
-	memset(partition, 0, sizeof(struct storage_partition));
-
 	partition->partition_guid = *partition_guid;
 	partition->block_size = block_size;
 	partition->num_blocks = num_blocks;
+
+	partition->base_lba = 0;
+	storage_partition_acl_init(&partition->acl);
 }
 
 void storage_partition_deinit(
@@ -28,15 +29,18 @@
 	memset(partition, 0, sizeof(struct storage_partition));
 }
 
-void storage_partition_extend_whitelist(
+bool storage_partition_grant_access(
 	struct storage_partition *partition,
 	uint32_t client_id)
 {
-	if (partition->whitelist_len < STORAGE_PARTITION_WHITELIST_LEN) {
+	return storage_partition_acl_add(&partition->acl, client_id);
+}
 
-		partition->whitelist[partition->whitelist_len] = client_id;
-		++partition->whitelist_len;
-	}
+bool storage_partition_assign_ownership(
+	struct storage_partition *partition,
+	const char *owner_id)
+{
+	return storage_partition_acl_set_owner_id(&partition->acl, owner_id);
 }
 
 bool storage_partition_is_guid_matched(
@@ -46,16 +50,19 @@
 	return (memcmp(&partition->partition_guid, partition_guid, sizeof(struct uuid_octets)) == 0);
 }
 
+bool storage_partition_is_open_permitted(
+	struct storage_partition *partition,
+	uint32_t client_id,
+	storage_partition_authorizer authorizer)
+{
+	return storage_partition_acl_authorize(&partition->acl, client_id, authorizer);
+}
+
 bool storage_partition_is_access_permitted(
 	const struct storage_partition *partition,
 	uint32_t client_id)
 {
-	bool is_permitted = (partition->whitelist_len == 0);
-
-	for (size_t i = 0; !is_permitted && i < partition->whitelist_len; ++i)
-		is_permitted = (client_id == partition->whitelist[i]);
-
-	return is_permitted;
+	return storage_partition_acl_check(&partition->acl, client_id);
 }
 
 bool storage_partition_is_lba_legal(
diff --git a/components/service/block_storage/block_store/storage_partition.h b/components/service/block_storage/block_store/storage_partition.h
index 848b060..8123fc1 100644
--- a/components/service/block_storage/block_store/storage_partition.h
+++ b/components/service/block_storage/block_store/storage_partition.h
@@ -12,16 +12,12 @@
 #include <stddef.h>
 #include <stdint.h>
 #include "common/uuid/uuid.h"
+#include "storage_partition_acl.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* Default maximum configurable whitelist length */
-#ifndef STORAGE_PARTITION_WHITELIST_LEN
-#define STORAGE_PARTITION_WHITELIST_LEN    (4)
-#endif
-
 /**
  * \brief Common storage partition structure
  *
@@ -43,18 +39,15 @@
 	/* Backend storage block that corresponds to LBA zero */
 	uint32_t base_lba;
 
-	/* Whitelist of client IDs corresponding to clients that are permitted
-	 * to access the partition. A zero length list is treated as a wildcard
-	 * where any client is permitted access. */
-	size_t whitelist_len;
-	uint32_t whitelist[STORAGE_PARTITION_WHITELIST_LEN];
+	/* Access control list for controlling access to configured owner */
+	struct storage_partition_acl acl;
 };
 
 /**
  * \brief Default storage_partition initialization function
  *
- * Initializes a storage_partition with an empty whitelist and a one-to-one
- * LBA mapping to backend storage.
+ * Initializes a storage_partition with a one-to-one LBA mapping to backend
+ * storage and open access for any client.
  *
  * \param[in]  partition       The subject storage_partition
  * \param[in]  partition_guid  The unique partition GUID
@@ -78,16 +71,28 @@
 	struct storage_partition *partition);
 
 /**
- * \brief Extend the whitelist
+ * \brief Grant access to a specific client
  *
  * \param[in]  partition    The subject storage_partition
- * \param[in]  client_id    The client ID to add
+ * \param[in]  client_id    The client ID to grant access to
+ * \return True if successful
  */
-void storage_partition_extend_whitelist(
+bool storage_partition_grant_access(
 	struct storage_partition *partition,
 	uint32_t client_id);
 
 /**
+ * \brief Assign ownership using a resolvable owner ID string
+ *
+ * \param[in]  partition    The subject storage_partition
+ * \param[in]  owner_id     Owner ID string
+ * \return True if successful
+ */
+bool storage_partition_assign_ownership(
+	struct storage_partition *partition,
+	const char *owner_id);
+
+/**
  * \brief Check if unique partition GUID matches
  *
  * \param[in]  partition       The subject storage_partition
@@ -99,6 +104,19 @@
 	const struct uuid_octets *partition_guid);
 
 /**
+ * \brief Perform checks on opening a partition
+ *
+ * \param[in]  partition    The subject storage_partition
+ * \param[in]  client_id    The requesting client ID
+ * \param[in]  authorizer	Optional authorizer function
+ * \return     True if access permitted
+ */
+bool storage_partition_is_open_permitted(
+	struct storage_partition *partition,
+	uint32_t client_id,
+	storage_partition_authorizer authorizer);
+
+/**
  * \brief Check if access to the storage partition is permitted
  *
  * \param[in]  partition    The subject storage_partition
diff --git a/components/service/block_storage/block_store/storage_partition_acl.c b/components/service/block_storage/block_store/storage_partition_acl.c
new file mode 100644
index 0000000..127fc2e
--- /dev/null
+++ b/components/service/block_storage/block_store/storage_partition_acl.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <string.h>
+#include "storage_partition_acl.h"
+
+void storage_partition_acl_init(
+	struct storage_partition_acl *acl)
+{
+	memset(acl, 0, sizeof(struct storage_partition_acl));
+}
+
+bool storage_partition_acl_add(
+	struct storage_partition_acl *acl,
+	uint32_t client_id)
+{
+	if (acl->allowlist_len < STORAGE_PARTITION_ACL_ALLOWLIST_LEN) {
+
+		acl->allowlist[acl->allowlist_len] = client_id;
+		++acl->allowlist_len;
+
+		return true;
+	}
+
+	return false;
+}
+
+bool storage_partition_acl_set_owner_id(
+	struct storage_partition_acl *acl,
+	const char *owner_id)
+{
+	size_t len = strlen(owner_id);
+
+	if (len < STORAGE_PARTITION_ACL_MAX_OWNER_ID_LEN) {
+
+		strcpy(acl->owner_id, owner_id);
+		return true;
+	}
+
+	return false;
+}
+
+bool storage_partition_acl_check(
+	const struct storage_partition_acl *acl,
+	uint32_t client_id)
+{
+	bool is_access_permitted = false;
+
+	for (size_t i = 0; !is_access_permitted && (i < acl->allowlist_len); ++i)
+		is_access_permitted = (client_id == acl->allowlist[i]);
+
+	return is_access_permitted;
+}
+
+bool storage_partition_acl_authorize(
+	struct storage_partition_acl *acl,
+	uint32_t client_id,
+	storage_partition_authorizer authorizer)
+{
+	bool is_access_permitted = storage_partition_acl_check(acl, client_id);
+
+	if (!is_access_permitted) {
+
+		/* Client ID is not currently allowlisted. Allow a deployment
+		 * specific authorizer to implement its own policy or check that
+		 * the present client ID resolves to the configured owner ID for
+		 * the storage partition.
+		 */
+		if (authorizer) {
+
+			if (authorizer(client_id, acl->owner_id))
+				is_access_permitted = storage_partition_acl_add(acl, client_id);
+		} else {
+
+			/* If no authorizer is configured, and no owner_id string has been
+			 * configured, allow access to the first client to request access.
+			 * This allows for an access policy where clients request access during
+			 * initialization and the permission granted on the initial request
+			 * persists. This policy could be suitable for secure world clients
+			 * where access permission to partitions can be claimed prior to
+			 * normal world boot-up.
+			 */
+			if (!acl->allowlist_len && !acl->owner_id[0])
+				is_access_permitted = storage_partition_acl_add(acl, client_id);
+		}
+	}
+
+	return is_access_permitted;
+}
diff --git a/components/service/block_storage/block_store/storage_partition_acl.h b/components/service/block_storage/block_store/storage_partition_acl.h
new file mode 100644
index 0000000..d3c5219
--- /dev/null
+++ b/components/service/block_storage/block_store/storage_partition_acl.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef STORAGE_PARTITION_ACL_H
+#define STORAGE_PARTITION_ACL_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* By default, the ACL allowlist only accommodates a single client for
+ * a storage partition. This may be overridden per deployment at build-time
+ * if multiple clients need to be supported.
+ */
+#ifndef STORAGE_PARTITION_ACL_ALLOWLIST_LEN
+#define STORAGE_PARTITION_ACL_ALLOWLIST_LEN     (1)
+#endif
+
+/* Default maximum owner ID string length */
+#ifndef STORAGE_PARTITION_ACL_MAX_OWNER_ID_LEN
+#define STORAGE_PARTITION_ACL_MAX_OWNER_ID_LEN  (40)
+#endif
+
+/* Forward declarations */
+struct storage_partition_acl;
+
+/**
+ * \brief Authorizer function
+ *
+ * Implements deployment specific authorization policy and owner ID
+ * resolution to determine if the presented client ID is allowed access.
+ * A concrete authorizer may perform an environment specific discovery operation
+ * to check if the presented client ID corresponds to the configured
+ * owner ID.
+ *
+ * \param[in]  client_id    The presented client ID
+ * \param[in]  owner_id		The configured owner ID
+ * \return True if client_id is permitted access
+ */
+typedef bool (*storage_partition_authorizer)(
+	uint32_t client_id,
+	const char *owner_id);
+
+/**
+ * \brief Storage partition access control list
+ *
+ * Access to a storage partition may be controlled by either adding specific
+ * client IDs to the access control list or by providing an authorizer function
+ * that will resolve the owner ID string to a set of client IDs. The authorizer
+ * allows for lazy resolution for an owner ID to client ID at the point that
+ * a client attempts to open a partition but its client ID is not present in
+ * the ACL.
+ */
+struct storage_partition_acl {
+	char owner_id[STORAGE_PARTITION_ACL_MAX_OWNER_ID_LEN];
+	size_t allowlist_len;
+	uint32_t allowlist[STORAGE_PARTITION_ACL_ALLOWLIST_LEN];
+};
+
+/**
+ * \brief Initialise an empty storage_partition_acl
+ *
+ * \param[in]  acl      The subject storage_partition_acl
+ */
+void storage_partition_acl_init(
+	struct storage_partition_acl *acl);
+
+/**
+ * \brief Add a client ID to the ACL allowlist
+ *
+ * \param[in]  acl       The subject storage_partition_acl
+ * \param[in]  client_id Client ID to add
+ * \return True if added successfully
+ */
+bool storage_partition_acl_add(
+	struct storage_partition_acl *acl,
+	uint32_t client_id);
+
+/**
+ * \brief Set the owner ID string
+ *
+ * \param[in]  acl       The subject storage_partition_acl
+ * \param[in]  owner_id  Owner ID string
+ * \return True if successful
+ */
+bool storage_partition_acl_set_owner_id(
+	struct storage_partition_acl *acl,
+	const char *owner_id);
+
+/**
+ * \brief Check if a client has access permission
+ *
+ * \param[in]  acl       The subject storage_partition_acl
+ * \param[in]  client_id Client ID to check
+ *
+ * \return True if access is permitted
+ */
+bool storage_partition_acl_check(
+	const struct storage_partition_acl *acl,
+	uint32_t client_id);
+
+/**
+ * \brief Authorize a requesting client
+ *
+ * \param[in]  acl        The subject storage_partition_acl
+ * \param[in]  client_id  Client ID to check
+ * \param[in]  authorizer Optional authorizer
+ *
+ * \return True if access is permitted
+ */
+bool storage_partition_acl_authorize(
+	struct storage_partition_acl *acl,
+	uint32_t client_id,
+	storage_partition_authorizer authorizer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STORAGE_PARTITION_ACL_H */
diff --git a/components/service/locator/standalone/services/block-storage/block_storage_service_context.cpp b/components/service/locator/standalone/services/block-storage/block_storage_service_context.cpp
index 4629cd7..b105c08 100644
--- a/components/service/locator/standalone/services/block-storage/block_storage_service_context.cpp
+++ b/components/service/locator/standalone/services/block-storage/block_storage_service_context.cpp
@@ -42,7 +42,8 @@
 		&m_partitioned_block_store,
 		0,
 		&back_store_guid,
-		back_store);
+		back_store,
+		NULL);
 	assert(front_store);
 
 	/* Use the reference partition configuration */