DAOS 源码解析之 daos_security(上)

举报
debugzhang 发表于 2021/04/10 10:38:36 2021/04/10
【摘要】 DAOS (Distributed Asynchronous Object Storage) 是一个开源的对象存储系统,专为大规模分布式非易失性内存设计,利用了 SCM 和 NVMe 等的下一代 NVM 技术。 DAOS 同时在硬件之上提供了键值存储接口,提供了诸如事务性非阻塞 I/O、具有自我修复的高级数据保护、端到端数据完整性、细粒度数据控制和弹性存储的高级数据保护,从而优化性能并降低成本。

DAOS (Distributed Asynchronous Object Storage) 是一个开源的对象存储系统,专为大规模分布式非易失性内存设计,利用了 SCM 和 NVMe 等的下一代 NVM 技术。 DAOS 同时在硬件之上提供了键值存储接口,提供了诸如事务性非阻塞 I/O、具有自我修复的高级数据保护、端到端数据完整性、细粒度数据控制和弹性存储的高级数据保护,从而优化性能并降低成本。

本文以 Release 1.1.4 版本为标准,解析 include/daos_security.h 中的具体实现。include/daos_security.h 包含了 DAOS 安全和访问控制 API。有关访问控制的具体信息可参考 DAOS 分布式异步对象存储|安全模型

访问控制列表

结构体 daos_acl 表示访问控制列表 (Access Control List, ACL) 的表头,后跟可变长度的访问控制项 (Access Control Entry, ACE) 列表。可以通过将主体 (principal) 字段大小与访问控制项的结构大小相加得到当前项的大小,来遍历访问控制项列表。

struct daos_acl {
	// 列表格式的版本
	uint16_t	dal_ver;
	// 保留:用于 64 位对齐
	uint16_t	dal_reserv;
	// 访问控制项列表的总长度(字节)
	uint32_t	dal_len;
	// 可变长度的访问控制项列表(struct daos_ace)
	uint8_t		dal_ace[];
};

DAOS_ACL_VERSION 表示 ACL 格式的版本:

#define	DAOS_ACL_VERSION		(1)

访问控制项

结构体 daos_ace 表示给定主体的访问控制项。每个主体最多有一个 ACE,在给定的 ACL 中列出其所有权限:

struct daos_ace {
	// daos_acl_access_type 的位图表示,表示访问权限类型
	uint8_t		dae_access_types;
	// 主体类型
	uint8_t		dae_principal_type;
	// 主体字段字符串长度
	uint16_t	dae_principal_len;
	// daos_acl_flags 的位图表示,表示访问标志
	uint16_t	dae_access_flags;
	// 保留:用于 64 位对齐
	uint16_t	dae_reserv;
	// daos_acl_perm 的位图表示,表示 ALLOW 访问权限
	uint64_t	dae_allow_perms;
	// daos_acl_perm 的位图表示,表示 AUDIT 访问权限
	uint64_t	dae_audit_perms;
	// daos_acl_perm 的位图表示,表示 ALARM 访问权限
	uint64_t	dae_alarm_perms;
	// 以 null 结尾的字符串,表示特定用户/组的主体名称。
	// 实际分配的字节必须向上取整以 64 位对齐。
	// 表示特殊主体 OWNER, OWNER_GROUP 和 EVERYONE 时,该字符串为空。
	char		dae_principal[];
};

主体

daos_acl_principal_type 表示访问控制项的主体类型。

OWNER, OWNER_GROUP 和 EVERYONE 是不需要主体字符串的特殊主体类型。

enum daos_acl_principal_type {
	// 所属用户
	DAOS_ACL_OWNER,
	// 用户
	DAOS_ACL_USER,
	// 所属组
	DAOS_ACL_OWNER_GROUP,
	// 组
	DAOS_ACL_GROUP,
	// 任何主体
	DAOS_ACL_EVERYONE,

	// 必须是最后一个
	NUM_DAOS_ACL_TYPES
};

DAOS_ACL_MAX_PRINCIPAL_LEN 表示主体字段 user@domain 字符串的最大长度,不包括 null 终止符:

#define DAOS_ACL_MAX_PRINCIPAL_LEN	(255)

DAOS_ACL_MAX_PRINCIPAL_BUF_LEN 表示主体字段 user@domain 字符串的最大长度,包括 null 终止符:

#define DAOS_ACL_MAX_PRINCIPAL_BUF_LEN	(DAOS_ACL_MAX_PRINCIPAL_LEN + 1)

主体字段有三种特殊类型,它们没有主体名,下列宏定义了这三种类型的主体名:

#define DAOS_ACL_PRINCIPAL_OWNER		"OWNER@"
#define DAOS_ACL_PRINCIPAL_OWNER_GRP	"GROUP@"
#define DAOS_ACL_PRINCIPAL_EVERYONE		"EVERYONE@"

DAOS_ACL_MAX_ACE_LEN 表示 daos_acl::dal_ace 的最大长度,即 dal_len 的值:

#define DAOS_ACL_MAX_ACE_LEN	(65536)

DAOS_ACL_MAX_ACE_STR_LEN 表示访问控制项 (Access Control Entry, ACE) 字符串格式 <access>:<flags>:<principal>:<perms> 的最大长度:

#define DAOS_ACL_MAX_ACE_STR_LEN	(DAOS_ACL_MAX_PRINCIPAL_LEN + 64)

访问权限类型

daos_acl_access_type 表示要为其设置的访问权限类型位,有三种访问权限:

enum daos_acl_access_type {
	// 允许访问
	DAOS_ACL_ACCESS_ALLOW = (1U << 0),
	// 记录访问活动以供审核
	DAOS_ACL_ACCESS_AUDIT = (1U << 1),
	// 通知访问活动以报警
	DAOS_ACL_ACCESS_ALARM = (1U << 2)
};

DAOS_ACL_ACCESS_ALL 表示所有有效访问位的掩码:

#define DAOS_ACL_ACCESS_ALL	(DAOS_ACL_ACCESS_ALLOW |		\
				 DAOS_ACL_ACCESS_AUDIT |		\
				 DAOS_ACL_ACCESS_ALARM)

访问标志

daos_acl_flags 表示访问标志位,提供有关如何解释 ACE 的附加信息:

enum daos_acl_flags {
	// 表示组,而不是用户
	DAOS_ACL_FLAG_GROUP				= (1U << 0),
	// Container 应该从 Pool 中继承访问权限
	DAOS_ACL_FLAG_POOL_INHERIT		= (1U << 1),
	// 访问失败时应该进行审核/报警
	DAOS_ACL_FLAG_ACCESS_FAIL		= (1U << 2),
	// 访问成功时应该进行审核/报警
	DAOS_ACL_FLAG_ACCESS_SUCCESS	= (1U << 3)
};

DAOS_ACL_FLAG_ALL 表示所有有效标志位的掩码:

#define DAOS_ACL_FLAG_ALL	(DAOS_ACL_FLAG_GROUP |			\
				 DAOS_ACL_FLAG_POOL_INHERIT |		\
				 DAOS_ACL_FLAG_ACCESS_FAIL |		\
				 DAOS_ACL_FLAG_ACCESS_SUCCESS)

特定权限

daos_acl_perm 表示可选的特定权限位:

enum daos_acl_perm {
	DAOS_ACL_PERM_READ			= (1U << 0),
	DAOS_ACL_PERM_WRITE			= (1U << 1),
	DAOS_ACL_PERM_CREATE_CONT	= (1U << 2),
	DAOS_ACL_PERM_DEL_CONT		= (1U << 3),
	DAOS_ACL_PERM_GET_PROP		= (1U << 4),
	DAOS_ACL_PERM_SET_PROP		= (1U << 5),
	DAOS_ACL_PERM_GET_ACL		= (1U << 6),
	DAOS_ACL_PERM_SET_ACL		= (1U << 7),
	DAOS_ACL_PERM_SET_OWNER		= (1U << 8),
};

DAOS_ACL_PERM_POOL_ALL 表示 DAPS Pool 的所有有效权限位的掩码:

#define DAOS_ACL_PERM_POOL_ALL	(DAOS_ACL_PERM_READ |			\
				 DAOS_ACL_PERM_GET_PROP |		\
				 DAOS_ACL_PERM_WRITE |			\
				 DAOS_ACL_PERM_CREATE_CONT |		\
				 DAOS_ACL_PERM_DEL_CONT)

DAOS_ACL_PERM_CONT_ALL 表示 DAPS Container 的所有有效权限位的掩码:

#define DAOS_ACL_PERM_CONT_ALL	(DAOS_ACL_PERM_READ |			\
				 DAOS_ACL_PERM_WRITE |			\
				 DAOS_ACL_PERM_DEL_CONT |		\
				 DAOS_ACL_PERM_GET_PROP |		\
				 DAOS_ACL_PERM_SET_PROP |		\
				 DAOS_ACL_PERM_GET_ACL |		\
				 DAOS_ACL_PERM_SET_ACL |		\
				 DAOS_ACL_PERM_SET_OWNER)

DAOS_ACL_PERM_ALL 表示 DAOS 所有有效权限位的掩码:

#define DAOS_ACL_PERM_ALL	(DAOS_ACL_PERM_POOL_ALL |		\
				 DAOS_ACL_PERM_CONT_ALL)

daos_acl_create

daos_acl_create 函数用于分配 DAOS 访问控制列表。

参数:

  • aces [in]:指向要放入 ACL 中的 ACE 的指针数组。
  • num_aces [in]:ACE 列表的长度。

返回值:

  • 如果成功,返回分配的 daos_acl 指针。
  • 如果失败,返回 NULL。
struct daos_acl *
daos_acl_create(struct daos_ace *aces[], uint16_t num_aces)
{
	struct daos_acl * acl;
	int               ace_len;
	struct daos_ace **sorted_aces;

	// 计算添加到 daos_acl 结构上的 ACE 列表的总长度
	// 调用 daos_ace_get_siz() 计算列表中每个 ACE 的大小并求和
	// 如果其中一个 ACE 为 NULL,则返回 -DER_INVAL
	ace_len = get_flattened_ace_size(aces, num_aces);
	if (ace_len < 0) {
		// ACE 列表中包含 NULL
		return NULL;
	}

	// 复制 aces 中的 ACE,并按 ACE 的 dae_principal_type 优先级进行排序
	sorted_aces = get_copy_sorted_by_principal_type(aces, num_aces);
	if (sorted_aces == NULL) {
		// 复制时申请内存失败
		return NULL;
	}

	// 为 daos_acl::dal_ace 结构分配存储 ACE 列表的数据块
	// get_total_acl_size() 将 ACE 列表的大小 ace_len 和
	// 表头结构体 daos_acl 的大小相加
	D_ALLOC(acl, get_total_acl_size(ace_len));
	if (acl == NULL) {
		// 分配失败,释放复制的排序列表 sorted_aces
		free_ace_array(sorted_aces, num_aces);
		return NULL;
	}

	// 设置 ACL 格式的版本
	acl->dal_ver = DAOS_ACL_VERSION;
	// 设置 ACE 列表的总长度(字节)
	acl->dal_len = ace_len;

	// 将 ACE 指针数组 sorted_aces 中的每个 ACE 项数据块展平,并
	// 复制到给 acl->dal_ace 分配的单个数据块中
	// 该操作假设调用者已分配足够大的缓冲区 (D_ALLOC) 来容纳展平的列表
	flatten_aces(acl->dal_ace, acl->dal_len, sorted_aces, num_aces);

	// 释放复制的排序列表 sorted_aces
	free_ace_array(sorted_aces, num_aces);

	return acl;
}

daos_acl_dup

daos_acl_dup 函数为 DAOS 访问控制列表分配新的副本

参数:

  • acl [in]:要复制的 ACL。

返回值:

  • 如果成功,返回新分配的 ACL 副本。
  • 如果失败,返回 NULL。
struct daos_acl *
daos_acl_dup(struct daos_acl *acl)
{
	struct daos_acl *acl_copy;
	size_t           acl_len;

	if (acl == NULL) {
		return NULL;
	}

	// 获取 ACL 的总大小(以字节为单位)
	// 这包括表头的大小以及 ACE 列表的大小
	acl_len = daos_acl_get_size(acl);
  
	// 为新副本分配空间
	D_ALLOC(acl_copy, acl_len);
	if (acl_copy == NULL) {
		// 分配失败
		return NULL;
	}

	// 复制 ACL
	memcpy(acl_copy, acl, acl_len);

	return acl_copy;
}

daos_acl_free

daos_acl_free 函数释放 DAOS 访问控制列表。

参数:

  • acl [in]:指向要释放的 ACL 结构的指针
void
daos_acl_free(struct daos_acl *acl)
{
	// ACL 仅仅是一个连续的数据块,所以不需要特殊处理
	// 直接释放相应内存
	D_FREE(acl);
}

daos_acl_get_size

daos_acl_get_size 函数获取 DAOS 访问控制列表的总大小(以字节为单位),这包括表头的大小以及 ACE 列表的大小。

参数:

  • acl [in]:要获取大小的 ACL。

返回值:

  • 返回 ACL 的大小(以字节为单位)。
  • 如果输入无效,返回 -DER_INVAL
ssize_t
daos_acl_get_size(struct daos_acl *acl)
{
	if (acl == NULL) {
		// 无效输入
		return -DER_INVAL;
	}

	// ACL 的大小为 ACE 列表的大小和表头结构体 daos_acl 的大小之和
	return get_total_acl_size(acl->dal_len);
}

daos_acl_get_next_ace

daos_acl_get_next_ace 函数获取访问控制列表中的下一个访问控制项,以便在列表中进行迭代。

参数:

  • acl [in]:要遍历的 ACL。
  • current_ace [in]:确定下一项的当前的 ACE,当为 NULL 时获取第一个 ACE。

返回值:

  • ACL 中的下一个 ACE,当到达列表结尾时,返回 NULL。
struct daos_ace *
daos_acl_get_next_ace(struct daos_acl *acl, struct daos_ace *current_ace)
{
	size_t offset;

	if (acl == NULL) {
		// 无效的输入
		return NULL;
	}

	if (current_ace == NULL && acl->dal_len > 0) {
		// 当前 ACE 为 NULL,获取列表中的第一个 ACE
		return (struct daos_ace *)acl->dal_ace;
	}

	if (!is_in_ace_list((uint8_t *)current_ace, acl)) {
		// 当前项已是列表中的最后一项
		return NULL;
	}

	D_ASSERT(current_ace != NULL);
	// 当前项的大小等于主体字段大小与结构体 daos_ace 大小之和
	offset = sizeof(struct daos_ace) + current_ace->dae_principal_len;
	if (!is_in_ace_list((uint8_t *)current_ace + offset, acl)) {
		return NULL;
	}

	// 计算偏移量,得到下一项的地址
	return (struct daos_ace *)((uint8_t *)current_ace + offset);
}

daos_acl_get_ace_for_principal

daos_acl_get_ace_for_principal 函数通过主体寻找访问控制列表中的访问控制项。

参数:

  • acl [in]:待搜索的 ACL。
  • type [in]:要搜索的主体类型。
  • principal [in]:要搜索的主体名(类型为 USER 或 GROUP),NULL(其他类型)。
  • ace [ou t]:指向 ACL 中匹配的 ACE 的指针(不是副本)。

返回值:

  • 如果成功,返回 0。
  • 如果失败,返回:
    • -DER_INVAL:无效输入。
    • -DER_NONEXIST:不存在匹配的 ACE。
int
daos_acl_get_ace_for_principal(struct daos_acl *acl,
			       enum daos_acl_principal_type type,
			       const char *principal, struct daos_ace **ace)
{
	struct daos_ace *result;

	if (acl == NULL || ace == NULL || !type_is_valid(type) ||
	    (type_needs_name(type) && principal == NULL)) {
		// 无效输入
		return -DER_INVAL;
	}

	// 迭代 ACL
	result = daos_acl_get_next_ace(acl, NULL);
	while (result != NULL) {
		if (result->dae_principal_type == type &&
		    principal_name_matches_ace(result, principal)) {
			// 通过比较主体类型和主体名找到匹配的 ACE
			break;
		}

		// 获取列表中的下一个 ACE
		result = daos_acl_get_next_ace(acl, result);
	}

	if (result == NULL) {
		// 不存在匹配的 ACE
		return -DER_NONEXIST;
	}

	// 返回指向 ACL 中匹配的 ACE 的指针
	*ace = result;

	return 0;
}

daos_acl_add_ace

daos_acl_add_ace 函数在访问控制列表的适当位置插入访问控制项。

预期的顺序是按主体类型排序:Owner, Users, Owner-Group, Groups, Everyone。

访问控制列表可能需要重新分配内存,以便为新插入的访问控制项提供空间(原有内存空间将被释放)。

如果新的访问控制项是现有项的更新,它将替换旧的项。

参数:

  • acl [in]:需要修改的 ACL。
  • new_ace [in]:需要添加的新 ACE。

返回值:

  • 如果成功,返回 0。
  • 如果失败,返回:
    • -DER_INVAL:无效输入。
    • -DER_NOMEM:重新申请需要的内存失败。
int
daos_acl_add_ace(struct daos_acl **acl, struct daos_ace *new_ace)
{
	uint32_t         new_len;
	ssize_t          new_ace_len;
	struct daos_acl *new_acl;

	if (acl == NULL || *acl == NULL) {
		// 无效的 ACL
		return -DER_INVAL;
	}

	// 获取 ACE 的大小(单位为字节)
	// ACE 的大小为结构体 daos_ace 的大小加主体字段的大小
	new_ace_len = daos_ace_get_size(new_ace);
	if (new_ace_len < 0) {
		// 无效的 ACE
		return -DER_INVAL;
	}

	// 判断当前 ACL 中是否含有该主体的 ACE
	if (acl_already_has_principal(*acl, new_ace->dae_principal_type,
				      new_ace->dae_principal)) {
		// 当前列表已包含该主体的 ACE
		// 更新该 ACE,不需要重新分配空间
		overwrite_ace_for_principal(*acl, new_ace);
		return 0;
	}

	new_len = (*acl)->dal_len + new_ace_len;

	// 为 ACL 重新申请内存,以添加新 ACE
	D_ALLOC(new_acl, get_total_acl_size(new_len));
	if (new_acl == NULL) {
		// 申请内存失败
		return -DER_NOMEM;
	}

	new_acl->dal_ver = (*acl)->dal_ver;
	new_acl->dal_len = new_len;

	// 复制原 ACE 列表,同时将新 ACE 按主体类型顺序插入列表
	copy_acl_with_new_ace_inserted(*acl, new_acl, new_ace);

	// 释放原 ACL 的内存空间
	daos_acl_free(*acl);
	*acl = new_acl;

	return 0;
}

daos_acl_remove_ace

daos_acl_remove_ace 函数从访问控制列表中移除要求的访问控制项。

当访问控制项被成功移除后,访问控制列表需要重新分配内存(原有内存空间将被释放)。

参数:

  • acl [in]:要修改的 ACL。
  • type [in]:要移除的 ACE 的主体类型。
  • principal_name [in]:要移除的主体名(类型为 USER 或 GROUP),NULL(其他类型)。

返回值:

  • 如果成功,返回 0。
  • 如果失败,返回:
    • -DER_INVAL:无效输入。
    • -DER_NOMEM:重新申请需要的内存失败。
    • -DER_NONEXIST:要移除的 ACE 不在 ACL 中。
int
daos_acl_remove_ace(struct daos_acl **acl,
		    enum daos_acl_principal_type type,
		    const char *principal_name)
{
	struct daos_ace *current;
	struct daos_ace *ace_to_remove;
	struct daos_acl *new_acl;
	uint32_t         new_len;
	uint8_t *        pen;
	int              rc;

	// principal_meets_type_requirements() 判断主体类型是否有效
	// 类型为 USER/GROUP 时,主体名不为 NULL 且长度不为 0
	// 类型为其他类型时,主体名为 NULL
	if (acl == NULL || *acl == NULL || !type_is_valid(type) ||
	    !principal_meets_type_requirements(type, principal_name)) {
		// 无效输入
		return -DER_INVAL;
	}

	// 通过主体名在 ACL 中寻找要移除的 ACE
	rc = daos_acl_get_ace_for_principal(*acl, type, principal_name,
					    &ace_to_remove);
	if (rc != 0) {
		// 要移除的 ACE 不在列表中
		return rc;
	}

	new_len = (*acl)->dal_len - daos_ace_get_size(ace_to_remove);

	// 为 ACL 重新申请内存
	D_ALLOC(new_acl, get_total_acl_size(new_len));
	if (new_acl == NULL) {
		// 申请内存失败
		return -DER_NOMEM;
	}

	new_acl->dal_len = new_len;
	new_acl->dal_ver = (*acl)->dal_ver;

  // 迭代 ACL
	pen     = new_acl->dal_ace;
	current = daos_acl_get_next_ace(*acl, NULL);
	while (current != NULL) {
		// 复制 ACE 到新的 ACL 中
		// 如果当前项为要移除的项,跳过
		if (!ace_matches_principal(current, type, principal_name)) {
			pen = write_ace(current, pen);
		}

		current = daos_acl_get_next_ace(*acl, current);
	}

	// 释放原 ACL 的内存空间
	daos_acl_free(*acl);
	*acl = new_acl;

	return 0;
}

daos_acl_dump

daos_acl_dump 函数将访问控制列表按详细的可读格式打印到标准输出,帮助调试。

参数:

  • acl [in]:要打印的 ACL。
void
daos_acl_dump(struct daos_acl *acl)
{
	struct daos_ace *current;

	printf("Access Control List:\n");
	if (acl == NULL) {
		printf("\tNULL\n");
		return;
	}

	printf("\tVersion: %hu\n", acl->dal_ver);
	printf("\tLength: %u\n", acl->dal_len);

	// 迭代 ACL
	current = daos_acl_get_next_ace(acl, NULL);
	while (current != NULL) {
		// 将 ACE 按详细的可读格式打印到标准输出
		daos_ace_dump(current, 1);

		current = daos_acl_get_next_ace(acl, current);
	}
}

daos_acl_validate

daos_acl_validate 函数解析并检查整个访问控制列表的有效性和内部一致性。

参数:

  • acl [in]:要检查的 ACL。

返回值:

  • 如果 ACL 是有效的,返回 0。
  • 如果 ACL 无效,返回 -DER_INVAL
  • 如果检查过程中内存不足,返回 -DER_NOMEM
int
daos_acl_validate(struct daos_acl *acl)
{
	int rc;

	if (acl == NULL) {
		// 当前 ACL 为空,无效
		return -DER_INVAL;
	}

	if (acl->dal_ver != DAOS_ACL_VERSION) {
		// 当前 ACL 格式与预期格式不匹配,无效
		return -DER_INVAL;
	}

	if (acl->dal_len > 0 && (acl->dal_len < sizeof(struct daos_ace) ||
				 (acl->dal_len > DAOS_ACL_MAX_ACE_LEN))) {
		// 当前 ACL 长度超过最大长度限制,无效
		D_ERROR("invalid dal_len %d, should with in [%zu, %d].\n",
			acl->dal_len, sizeof(struct daos_ace),
			DAOS_ACL_MAX_ACE_LEN);
		return -DER_INVAL;
	}

	// 整体结构必须是 64 位对齐的
	if (acl->dal_len % 8 != 0) {
		// 当前 ACL 结构不是 64 位对齐的,无效
		D_ERROR("invalid dal_len %d, not 8 bytes aligned.\n",
			acl->dal_len);
		return -DER_INVAL;
	}

	// 遍历 ACL 检查 ACE 的有效性
	rc = validate_aces(acl);
	if (rc != 0) {
		// 存在无效的 ACE
		return rc;
	}

	return 0;
}

daos_acl_pool_validate

daos_acl_pool_validate 函数检查访问控制列表是否可用于 DAOS Pool。

该检查调用了 daos_acl_validate 函数进行相应的检查。

参数:

  • acl [in]:要检查的 ACL。

返回值:

  • 如果 ACL 可用于 Pool,返回 0。
  • 如果 ACL 无效,返回 -DER_INVAL
  • 如果检查过程中内存不足,返回 -DER_NOMEM
int
daos_acl_pool_validate(struct daos_acl *acl)
{
	// 首先调用 daos_acl_validate() 进行检查
	// 其次检查 DAPS Pool 的有效权限位
	return validate_acl_with_special_perms(acl, DAOS_ACL_PERM_POOL_ALL);
}

daos_acl_cont_validate

daos_acl_cont_validate 函数检查访问控制列表是否可用于 DAOS Container。

该检查调用了 daos_acl_validate 函数进行相应的检查。

参数:

  • acl [in]:要检查的 ACL。

返回值:

  • 如果 ACL 可用于 Container,返回 0。
  • 如果 ACL 无效,返回 -DER_INVAL
  • 如果检查过程中内存不足,返回 -DER_NOMEM
int
daos_acl_cont_validate(struct daos_acl *acl)
{
	// 首先调用 daos_acl_validate() 进行检查
	// 其次检查 DAPS Container 的有效权限位
	return validate_acl_with_special_perms(acl, DAOS_ACL_PERM_CONT_ALL);
}

daos_ace_create

daos_ace_create 函数分配新的访问控制项(主体名内存对齐)。

只有类型为 USER 或 GROUP 的主体包含主体名。

参数:

  • type [in]:ACE 的主体类型。
  • principal_name [in]:主体名,将添加到结构的末尾(对于没有主体名的类型,传递 NULL)。

返回值:

  • 如果成功,返回设置了预期主体名、长度和类型的新 ACE 结构。
  • 如果失败,返回 NULL。
struct daos_ace *
daos_ace_create(enum daos_acl_principal_type type, const char *principal_name)
{
	struct daos_ace *ace;
	size_t           principal_array_len = 0;

	if (!type_is_valid(type)) {
		// 主体类型不是有效类型
		return NULL;
	}

	if (type_needs_name(type)) {
		if (principal_name == NULL || strlen(principal_name) == 0) {
			// 主体类型是 USER/GROUP,但主体名无效
			return NULL;
		}

		// 需要满足 64 位对齐
		principal_array_len = D_ALIGNUP(strlen(principal_name) + 1, 8);
	}

	// 分配新 ACE 的内存空间
	D_ALLOC(ace, sizeof(struct daos_ace) + principal_array_len);
	if (ace != NULL) {
		ace->dae_principal_type = type;
		ace->dae_principal_len  = principal_array_len;
		strncpy(ace->dae_principal, principal_name,
			principal_array_len);

		if (type_is_group(type)) {
			// 设置组的访问标志位
			ace->dae_access_flags |= DAOS_ACL_FLAG_GROUP;
		}
	}

	return ace;
}

daos_ace_free

daos_ace_free 函数释放由 daos_ace_alloc 函数分配的访问控制项。

参数:

  • ace [in]:要释放的 ACE。
void
daos_ace_free(struct daos_ace *ace)
{
	D_FREE(ace);
}

daos_ace_get_size

daos_ace_get_size 函数获取访问控制项的大小(单位字节)。访问控制项的长度是可变的。

参数:

  • ace [in]:要获取大小的 ACE。

返回值:

  • 如果成功,返回目标 ACE 的字节大小。
  • 如果输入无效的 ACE,返回 -DER_INVAL
ssize_t
daos_ace_get_size(struct daos_ace *ace)
{
	if (ace == NULL) {
		// 无效输入
		return -DER_INVAL;
	}

	// ACE 的大小为结构体 daos_ace 的大小加主体字段的大小
	return sizeof(struct daos_ace) + ace->dae_principal_len;
}

daos_ace_dump

daos_ace_dump 函数将访问控制项按详细的可读格式打印到标准输出。

参数:

  • ace [in]:要打印的 ACE。
  • tabs [in]:要在顶层缩进的制表符数量。
void
daos_ace_dump(struct daos_ace *ace, uint32_t tabs)
{
	// 打印相应数量的制表符
	indent(tabs);
	printf("Access Control Entry:\n");

	if (ace == NULL) {
		indent(tabs + 1);
		printf("NULL\n");
		return;
	}

	// 打印各字段,制表符数量相对于顶层加一
	print_principal(tabs + 1, ace);
	print_all_access_types(tabs + 1, ace);
	print_all_flags(tabs + 1, ace);
	print_all_perm_types(tabs + 1, ace);
}

daos_ace_is_valid

daos_ace_is_valid 函数检查访问控制项结构的有效性和内部一致性。

参数:

  • ace [in]:要检查的 ACE。

返回值:

  • 如果 ACE 有效,返回 true,否则返回 false。
bool
daos_ace_is_valid(struct daos_ace *ace)
{
	uint8_t  valid_types = DAOS_ACL_ACCESS_ALL;
	uint16_t valid_flags = DAOS_ACL_FLAG_ALL;
	uint64_t valid_perms = DAOS_ACL_PERM_ALL;
	bool     name_exists;
	bool     flag_exists;

	// 检查输入有效性
	if (ace == NULL)
		return false;

	// 检查访问权限类型字段是否包含无效位
	if (ace->dae_access_types & ~valid_types)
		return false;

	// 检查是否定义访问权限类型
	if (ace->dae_access_types == 0)
		return false;
  
	// 检查访问权限标志字段是否包含无效位
	if (ace->dae_access_flags & ~valid_flags)
		return false;

	// 检查访问权限字段是否包含无效位
	if (!perms_valid_for_ace(ace, valid_perms))
		return false;
  
	// 主体为要求主体名的类型 USER/GROUP 时,主体名不得为空
	name_exists = ace->dae_principal_len != 0;
	if (type_needs_name(ace->dae_principal_type) != name_exists)
		return false;

	// 只有在主体类型为组时,访问权限标志才能设置组标志
	flag_exists = (ace->dae_access_flags & DAOS_ACL_FLAG_GROUP) != 0;
	if (type_is_group(ace->dae_principal_type) != flag_exists)
		return false;

	// 整体结构必须保持 64 位对齐
	if (ace->dae_principal_len % 8 != 0)
		return false;

	// 主体名结尾需包含结束符 '\0'
	if (ace->dae_principal_len > 0 && !principal_is_null_terminated(ace))
		return false;

	// 主体名长度不得超过最大长度限制
	// 主体名格式须符合规定
	if (ace->dae_principal_len > 0 &&
	    !daos_acl_principal_is_valid(ace->dae_principal))
		return false;

	// 检查是否定义访问权限
	if (!permissions_match_access_type(ace, DAOS_ACL_ACCESS_ALLOW) ||
	    !permissions_match_access_type(ace, DAOS_ACL_ACCESS_AUDIT) ||
	    !permissions_match_access_type(ace, DAOS_ACL_ACCESS_ALARM))
		return false;

	// 检查访问权限类型与访问权限标志是否匹配
	if (!access_matches_flags(ace))
		return false;

	return true;
}

daos_acl_principal_is_valid

daos_acl_principal_is_valid 函数检查访问控制列表中访问控制项的主体名是否使用正确格式的字符串。

检查不是非常严格,该函数验证主体名是否为 name@[domain] 格式,但不验证名称的字符合法,也不验证主体是否实际存在。

参数:

  • name [in]:要验证的主体名。

返回值:

  • 如果主题名格式正确,返回 true,否则返回 false。
bool
daos_acl_principal_is_valid(const char *name)
{
	size_t              len;
	size_t              i;
	enum validity_state state = STATE_START;

	if (name == NULL) {
		// 无效输入
		D_INFO("Name was NULL\n");
		return false;
	}

	len = strnlen(name, DAOS_ACL_MAX_PRINCIPAL_BUF_LEN);
	if (len == 0 || len > DAOS_ACL_MAX_PRINCIPAL_LEN) {
		// 主体名长度超过最大长度限制
		D_INFO("Invalid len: %lu\n", len);
		return false;
	}

	// 利用有限状态机验证主体名是否符合正确格式
	for (i = 0; i < (len + 1); i++) {
		state = next_validity_state(state, name[i]);
		if (state == STATE_INVALID) {
			// 格式错误
			D_INFO("Name was badly formatted: %s\n", name);
			return false;
		}
	}

	return true;
}

下一部分

请参阅 DAOS 源码解析之 daos_security(下)

相关信息

Emai: debugzhang@163.com

DAOS: https://github.com/daos-stack/daos

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。