DAOS 源码解析之 daos_security (下)
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 源码解析之 daos_security(上)
daos_acl_uid_to_principal
daos_acl_uid_to_principal
函数将本地 UID 转换为格式正确的用户主体名,以便与访问控制列表 API 一起使用。
参数:
uid [in]
:要转换的 UID。name [out]
:新分配的以 null 结尾的字符串,包含格式化的主体名。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NONEXIST
:UID 不存在。-DER_NOMEM
:分配内存失败。
int
daos_acl_uid_to_principal(uid_t uid, char **name)
{
int rc;
struct passwd user;
struct passwd *result = NULL;
char * buf = NULL;
if (name == NULL) {
// 无效输入
D_INFO("name pointer was NULL!\n");
return -DER_INVAL;
}
// 不存在在未知平台情况下获取缓冲区最大长度的方法
// 所以尝试取一个合理的值并加倍,直到它足够大
// getpwuid_r() 逐一搜索参数 uid, 找到时便将
// 该用户的数据以 struct passwd 结构 (result) 返回
rc = TRY_UNTIL_BUF_SIZE_OK(getpwuid_r, uid, &user, buf, &result);
if (rc == -DER_NOMEM)
// 分配更大缓冲区时失败,释放原缓冲区
D_GOTO(out, rc);
if (rc != 0) {
D_ERROR("Error from getpwuid_r: %d\n", rc);
// 将系统错误号转换为 DER_* 变量
// 释放缓冲区
D_GOTO(out, rc = d_errno2der(rc));
}
if (result == NULL) {
// UID 不存在,释放缓冲区
D_INFO("No user for uid %u\n", uid);
D_GOTO(out, rc = -DER_NONEXIST);
}
// 将本地名称按格式转换为主体名
// 如果在为主体名字符串申请内存时失败,返回 -DER_NOMEM
rc = local_name_to_principal_name(result->pw_name, name);
out:
D_FREE(buf);
return rc;
}
daos_acl_gid_to_principal
daos_acl_gid_to_principal
函数将本地 GID 转换为格式正确的组主体名,以便与访问控制列表 API 一起使用。
参数:
gid [in]
:要转换的 GID。name [out]
:新分配的以 null 结尾的字符串,包含格式化的主体名。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NONEXIST
:GID 不存在。-DER_NOMEM
:分配内存失败。
int
daos_acl_gid_to_principal(gid_t gid, char **name)
{
int rc;
struct group grp;
struct group *result = NULL;
char * buf = NULL;
if (name == NULL) {
// 无效输入
D_INFO("name pointer was NULL!\n");
return -DER_INVAL;
}
// 不存在在未知平台情况下获取缓冲区最大长度的方法
// 所以尝试取一个合理的值并加倍,直到它足够大
// getgrgid_r() 逐一搜索参数 gid, 找到时便将
// 该组的数据以 struct group 结构 (result) 返回
rc = TRY_UNTIL_BUF_SIZE_OK(getgrgid_r, gid, &grp, buf, &result);
if (rc == -DER_NOMEM)
// 分配更大缓冲区时失败,释放原缓冲区
D_GOTO(out, rc);
if (rc != 0) {
D_ERROR("Error from getgrgid_r: %d\n", rc);
// 将系统错误号转换为 DER_* 变量
// 释放缓冲区
D_GOTO(out, rc = d_errno2der(rc));
}
if (result == NULL) {
// GID 不存在,释放缓冲区
D_INFO("No group for gid %u\n", gid);
D_GOTO(out, rc = -DER_NONEXIST);
}
// 将本地名称按格式转换为主体名
// 如果在为主体名字符串申请内存时失败,返回 -DER_NOMEM
rc = local_name_to_principal_name(result->gr_name, name);
out:
D_FREE(buf);
return rc;
}
daos_acl_principal_to_uid
daos_acl_principal_to_uid
函数将用户主体名从访问控制列表转换为相应的本地 UID。
参数:
principal [in]
:要转换的主体名。uid [out]
:主体名的 UID。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NONEXIST
:用户不存在。-DER_NOMEM
:分配内存失败。
int
daos_acl_principal_to_uid(const char *principal, uid_t *uid)
{
char username[DAOS_ACL_MAX_PRINCIPAL_BUF_LEN];
char * buf = NULL;
struct passwd passwd;
struct passwd *result = NULL;
int rc;
if (uid == NULL) {
// 无效输入
D_INFO("NULL uid pointer\n");
return -DER_INVAL;
}
// 从“name@[domain]”格式的主体字段中提取用户/组名
rc = get_id_name_from_principal(principal, username);
if (rc != 0)
// 主体字段格式错误,返回 -DER_INVAL
return rc;
// 不存在在未知平台情况下获取缓冲区最大长度的方法
// 所以尝试取一个合理的值并加倍,直到它足够大
// getpwnam_r() 逐一搜索参数 username, 找到时便将
// 该用户的数据以 struct passwd 结构 (result) 返回
rc = TRY_UNTIL_BUF_SIZE_OK(getpwnam_r, username, &passwd, buf, &result);
if (rc == -DER_NOMEM)
// 分配更大缓冲区时失败,释放原缓冲区
D_GOTO(out, rc);
if (rc != 0) {
D_ERROR("Error from getpwnam_r: %d\n", rc);
// 将系统错误号转换为 DER_* 变量
// 释放缓冲区
D_GOTO(out, rc = d_errno2der(rc));
}
if (result == NULL) {
// 用户不存在,释放缓冲区
D_INFO("User '%s' not found\n", username);
D_GOTO(out, rc = -DER_NONEXIST);
}
*uid = result->pw_uid;
rc = 0;
out:
D_FREE(buf);
return rc;
}
daos_acl_principal_to_gid
daos_acl_principal_to_gid
函数将组主体名从访问控制列表转换为相应的本地 GID。
参数:
principal [in]
:要转换的主体名。gid [out]
:主体名的 GID。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NONEXIST
:组不存在。-DER_NOMEM
:分配内存失败。
int
daos_acl_principal_to_gid(const char *principal, gid_t *gid)
{
char grpname[DAOS_ACL_MAX_PRINCIPAL_BUF_LEN];
char * buf = NULL;
struct group grp;
struct group *result = NULL;
int rc;
if (gid == NULL) {
// 无效输入
D_INFO("NULL gid pointer\n");
return -DER_INVAL;
}
// 从“name@[domain]”格式的主体字段中提取用户/组名
rc = get_id_name_from_principal(principal, grpname);
if (rc != 0)
// 主体字段格式错误,返回 -DER_INVAL
return rc;
// 不存在在未知平台情况下获取缓冲区最大长度的方法
// 所以尝试取一个合理的值并加倍,直到它足够大
// getgrnam_r() 逐一搜索参数 grpname, 找到时便将
// 该组的数据以 struct group 结构 (result) 返回
rc = TRY_UNTIL_BUF_SIZE_OK(getgrnam_r, grpname, &grp, buf, &result);
if (rc == -DER_NOMEM)
// 分配更大缓冲区时失败,释放原缓冲区
D_GOTO(out, rc);
if (rc != 0) {
D_ERROR("Error from getgrnam_r: %d\n", rc);
// 将系统错误号转换为 DER_* 变量
// 释放缓冲区
D_GOTO(out, rc = d_errno2der(rc));
}
if (result == NULL) {
// 组不存在,释放缓冲区
D_INFO("Group '%s' not found\n", grpname);
D_GOTO(out, rc = -DER_NONEXIST);
}
*gid = result->gr_gid;
rc = 0;
out:
D_FREE(buf);
return rc;
}
daos_ace_get_principal_str
daos_ace_get_principal_str
函数从访问控制项中获取主体名。
参数:
ace [in]
:要获取的 ACE。
返回值:
- 包含主体名的字符串,或特殊主体名称之一:OWNER@, GROUP@, EVERYONE@。
const char *
daos_ace_get_principal_str(struct daos_ace *ace)
{
// 如果是特殊主体类型 OWNER@, GROUP@, EVERYONE@
// 返回相应的宏名称
switch (ace->dae_principal_type) {
case DAOS_ACL_OWNER:
return DAOS_ACL_PRINCIPAL_OWNER;
case DAOS_ACL_OWNER_GROUP:
return DAOS_ACL_PRINCIPAL_OWNER_GRP;
case DAOS_ACL_EVERYONE:
return DAOS_ACL_PRINCIPAL_EVERYONE;
case DAOS_ACL_USER:
case DAOS_ACL_GROUP:
default:
break;
}
// User/Group 类型直接返回主体名
return ace->dae_principal;
}
daos_ace_from_str
daos_ace_from_str
函数将访问控制项的格式化字符串转换为 daos_ace
结构。
参数:
str [in]
:ACE 的格式化字符串。ace [out]
:新分配的 ACE 结构。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NOMEM
:分配内存失败。
int
daos_ace_from_str(const char *str, struct daos_ace **ace)
{
int rc;
size_t len;
char * tmpstr;
struct daos_ace *new_ace = NULL;
if (str == NULL || ace == NULL) {
// 无效输入
D_INFO("Invalid input ptr, str=%p, ace=%p\n", str, ace);
return -DER_INVAL;
}
len = strnlen(str, DAOS_ACL_MAX_ACE_STR_LEN + 1);
if (len > DAOS_ACL_MAX_ACE_STR_LEN) {
// 主体名长度超过最大长度限制
D_INFO("Input string is too long\n");
return -DER_INVAL;
}
// 将 str 复制到 tmpstr 上,复制过程中会为 tmpstr 分配空间
D_STRNDUP(tmpstr, str, len);
if (tmpstr == NULL) {
// 为临时字符串分配内存失败
D_ERROR("Couldn't allocate temporary string\n");
return -DER_NOMEM;
}
// 将 tmpstr 解析为 ACE,该辅助函数会改变 tmpstr
rc = create_ace_from_mutable_str(tmpstr, &new_ace);
D_FREE(tmpstr);
if (rc != 0)
return rc;
if (!daos_ace_is_valid(new_ace)) {
// ACE 无效
D_INFO("Finished building ACE but it's not valid\n");
// 释放 ACE
daos_ace_free(new_ace);
return -DER_INVAL;
}
*ace = new_ace;
return 0;
}
daos_ace_to_str
daos_ace_to_str
函数将 daos_ace
结构的访问控制项转换为格式化字符串。
限制:不同访问类型具有不同权限的有效访问控制项不能格式化为单个字符串,该访问控制项将被视为无效而拒绝。
参数:
ace [in]
:ACE 结构。buf [out]
:写入格式化字符串的缓冲区。buf_len [out]
:缓冲区大小。
返回值:
- 如果成功,返回 0。
- 如果输入无效,返回
-DER_INVAL
。
int
daos_ace_to_str(struct daos_ace *ace, char *buf, size_t buf_len)
{
size_t remaining_len = buf_len;
char * pen = buf;
uint64_t perms;
int rc = 0;
if (ace == NULL || buf == NULL || buf_len == 0) {
// ACE 为空或缓冲区为空
D_INFO("Invalid input, ace=%p, buf=%p, buf_len=%lu\n", ace, buf,
buf_len);
return -DER_INVAL;
}
if (!daos_ace_is_valid(ace)) {
// ACE 无效
D_INFO("ACE structure is not valid\n");
return -DER_INVAL;
}
if (!perms_unified(ace)) {
// 不同访问类型具有不同权限的有效 ACE 不能格式化为单个字符串
D_INFO("Can't create string for ACE with different perms for "
"different access types\n");
return -DER_INVAL;
}
memset(buf, 0, buf_len);
// 写入访问权限类型
WRITE_BIT(ace->dae_access_types, ACCESS_ALLOW);
WRITE_BIT(ace->dae_access_types, ACCESS_AUDIT);
WRITE_BIT(ace->dae_access_types, ACCESS_ALARM);
// 写入分隔符 ":"
WRITE_DIVIDER(&pen, &remaining_len);
// 写入访问权限标志
WRITE_BIT(ace->dae_access_flags, FLAG_GROUP);
WRITE_BIT(ace->dae_access_flags, FLAG_ACCESS_SUCCESS);
WRITE_BIT(ace->dae_access_flags, FLAG_ACCESS_FAIL);
WRITE_BIT(ace->dae_access_flags, FLAG_POOL_INHERIT);
// 写入分隔符 ":"
WRITE_DIVIDER(&pen, &remaining_len);
// 写入主体
rc = write_str(daos_ace_get_principal_str(ace), &pen, &remaining_len);
// 写入分隔符 ":"
WRITE_DIVIDER(&pen, &remaining_len);
// 写入访问权限
perms = get_perms(ace);
WRITE_BIT(perms, PERM_READ);
WRITE_BIT(perms, PERM_WRITE);
WRITE_BIT(perms, PERM_CREATE_CONT);
WRITE_BIT(perms, PERM_DEL_CONT);
WRITE_BIT(perms, PERM_GET_PROP);
WRITE_BIT(perms, PERM_SET_PROP);
WRITE_BIT(perms, PERM_GET_ACL);
WRITE_BIT(perms, PERM_SET_ACL);
WRITE_BIT(perms, PERM_SET_OWNER);
return rc;
}
daos_ace_str_get_verbose
daos_ace_str_get_verbose
函数将访问控制项的格式化字符串转换为详细字符串。
参数:
ace_str [in]
:ACE 格式化字符串。buf [out]
:输出缓冲区。buf_len [in]
:缓冲区大小。
参数:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_TRUNC
:缓冲区大小小于输出字符串大小。
int
daos_ace_str_get_verbose(const char *ace_str, char *buf, size_t buf_len)
{
const char * pch = ace_str;
enum ace_str_state state = ACE_FIRST_ACCESS_TYPE;
char * pen = buf;
size_t remaining_len = buf_len;
bool first = false;
if (buf == NULL || buf_len == 0) {
// 缓冲区无效
D_ERROR("Empty or NULL buffer\n");
return -DER_INVAL;
}
if (ace_str == NULL) {
// 输入字符串无效
D_ERROR("NULL ACE string\n");
return -DER_INVAL;
}
memset(buf, 0, buf_len);
// 利用有限状态机输出详细字符串
while (state != ACE_INVALID && state != ACE_DONE &&
state != ACE_TRUNC) {
switch (state) {
case ACE_FIRST_ACCESS_TYPE:
first = true;
// 掉落,继续执行
case ACE_ACCESS_TYPES:
state = process_verbose_access_ch(
*pch, &pen, &remaining_len, first);
break;
case ACE_FIRST_FLAG:
first = true;
// 掉落,继续执行
case ACE_FLAGS:
state = process_verbose_flag_ch(*pch, &pen,
&remaining_len, first);
break;
case ACE_IDENTITY:
state = process_verbose_identity(pch, &pen,
&remaining_len, &pch);
break;
case ACE_FIRST_PERM:
first = true;
// 掉落,继续执行
case ACE_PERMS:
state = process_verbose_perms_ch(*pch, &pen,
&remaining_len, first);
break;
default:
D_INFO("Bad state: %u\n", state);
state = ACE_INVALID;
}
pch++;
first = false;
}
if (state == ACE_INVALID) {
D_INFO("Invalid ACE string\n");
return -DER_INVAL;
}
if (state == ACE_TRUNC) {
D_INFO("String was truncated\n");
return -DER_TRUNC;
}
return 0;
}
daos_acl_from_strs
daos_acl_from_strs
函数将访问控制项列表的格式化字符串转换为 daos_acl
结构。
参数:
ace_strs [in]
:ACE 列表的格式化字符串数组。ace_nr [in]
:ACE 的大小。acl [out]
:新分配的 ACL 结构。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NOMEM
:分配内存失败。
int
daos_acl_from_strs(const char **ace_strs, size_t ace_nr, struct daos_acl **acl)
{
struct daos_ace **tmp_aces;
struct daos_acl * tmp_acl;
size_t i;
int rc;
if (ace_strs == NULL || ace_nr == 0) {
// 无效的 ACE 列表
D_ERROR("No ACE strings provided\n");
return -DER_INVAL;
}
if (acl == NULL) {
// 无效的 ACL 结构
D_ERROR("NULL ACL pointer\n");
return -DER_INVAL;
}
// 为临时 ACE 列表分配内存
D_ALLOC_ARRAY(tmp_aces, ace_nr);
if (tmp_aces == NULL)
// 分配内存失败
return -DER_NOMEM;
for (i = 0; i < ace_nr; i++) {
// 将 ACE 的格式化字符串转换为 daos_ace 结构
rc = daos_ace_from_str(ace_strs[i], &(tmp_aces[i]));
if (rc != 0) {
// 转换失败,释放内存
D_ERROR("Failed to convert string '%s' to ACE, "
"err=%d\n",
ace_strs[i], rc);
D_GOTO(out, rc);
}
}
// 创建新的 ACL 结构
tmp_acl = daos_acl_create(tmp_aces, ace_nr);
if (tmp_acl == NULL) {
// 创建失败,释放内存
D_ERROR("Failed to allocate ACL\n");
D_GOTO(out, rc = -DER_NOMEM);
}
// 检查 ACL 的有效性
rc = daos_acl_validate(tmp_acl);
if (rc != 0) {
// ACL 无效,释放内存
D_ERROR("Resulting ACL was invalid\n");
daos_acl_free(tmp_acl);
D_GOTO(out, rc);
}
*acl = tmp_acl;
rc = 0;
out:
for (i = 0; i < ace_nr; i++)
daos_ace_free(tmp_aces[i]);
D_FREE(tmp_aces);
return rc;
}
daos_acl_to_strs
daos_acl_to_strs
函数将 daos_acl
结构的访问控制列表转换为访问控制项列表的格式化字符串。
ace_strs
中的每项都是动态分配的,数组本身也是如此,所以调用者有责任释放它们。
参数:
acl [in]
:要转换的 ACL。ace_strs [out]
:新分配的字符串数组,存储 ACE 的格式化字符串。ace_nr [out]
:ace_strs
的大小。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NOMEM
:分配内存失败。
int
daos_acl_to_strs(struct daos_acl *acl, char ***ace_strs, size_t *ace_nr)
{
size_t ace_count = 0;
char ** result = NULL;
struct daos_ace *current;
int rc;
if (ace_strs == NULL || ace_nr == NULL) {
// 无效的 ACE 列表
D_ERROR("Null output params: ace_strs=%p, ace_nr=%p\n",
ace_strs, ace_nr);
return -DER_INVAL;
}
if (daos_acl_validate(acl) != 0) {
// 无效的 ACL 结构
D_ERROR("ACL is not valid\n");
return -DER_INVAL;
}
// 迭代 ACL,获取 ACE 的数量
current = daos_acl_get_next_ace(acl, NULL);
while (current != NULL) {
ace_count++;
current = daos_acl_get_next_ace(acl, current);
}
if (ace_count > 0) {
D_ALLOC_ARRAY(result, ace_count);
if (result == NULL)
// 为字符串数组分配内存失败
return -DER_NOMEM;
// 将 ACE 转换为格式化字符串,此操作包含字符串内存的分配
rc = convert_aces_to_strs(acl, ace_count, result);
if (rc != 0) {
D_FREE(result);
return rc;
}
}
*ace_strs = result;
*ace_nr = ace_count;
return 0;
}
daos_acl_principal_from_str
daos_acl_principal_from_str
函数将格式化的主体字符串转换为适合创建或查找访问控制项的主体类型和名称。
输入字符串的格式为:
- 对于 User: “u:username@”
- 对于 Group: “g:groupname@”
- 对于特殊类型:“OWNER@”,“GROUP@”,或 “EVERYONE@”
参数:
principal_str [in]
:格式化的主体字符串。type [out]
:从格式化字符串中确定的主体类型。name [out]
:从格式化字符串中确定的主体名。该字符串是新分配的内存,调用者有责任释放它。如果主体类型为特殊类型,则该字符串为 NULL。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NOMEM
:分配内存失败。
int
daos_acl_principal_from_str(const char *principal_str,
enum daos_acl_principal_type *type,
char **name)
{
int rc;
const char *p_name;
if (principal_str == NULL || type == NULL || name == NULL) {
// 无效输入
D_INFO("Null input: principal_str=%p, type=%p, name=%p\n",
principal_str, type, name);
return -DER_INVAL;
}
// 从字符串中获取主体类型
// 命名用户或组将由前缀指定
rc = get_principal_type_from_str(principal_str, type);
if (rc != 0) {
D_INFO("Badly-formatted principal string\n");
return rc;
}
// 对于特殊类型,主体名为 NULL
if (is_special_type(*type)) {
*name = NULL;
return 0;
}
// 在给字符串分配内存之前,要确保这个主体名是有效的
// 获取主体名
p_name = get_start_of_name(principal_str, *type);
if (!daos_acl_principal_is_valid(p_name)) {
// 主体名无效
D_INFO("Invalid principal name\n");
return -DER_INVAL;
}
// 将主体名复制到返回字符串中
D_STRNDUP(*name, p_name, DAOS_ACL_MAX_PRINCIPAL_LEN);
if (*name == NULL)
// 分配内存失败
return -DER_NOMEM;
return 0;
}
daos_acl_to_stream
daos_acl_to_stream
函数以访问控制列表文件格式将访问控制列表打印到流。
参数:
stream [in]
:要将 ACL 打印到的打开流。acl [in]
:要打印到 ACL。verbose [in]
:是否在输出中包含每个 ACE 的详细注释。
返回值:
- 如果成功,返回 0。
- 如果失败,返回:
-DER_INVAL
:无效输入。-DER_NOMEM
:分配内存失败。-DER_IO
:向流写入失败。
int
daos_acl_to_stream(FILE *stream, struct daos_acl *acl, bool verbose)
{
int rc = 0;
char **aces = NULL;
size_t aces_nr, i;
if (stream == NULL) {
// 无效的流
D_ERROR("Invalid stream\n");
return -DER_INVAL;
}
if (acl != NULL) {
// 将 ACL 转换为 ACE 列表的格式化字符串
rc = daos_acl_to_strs(acl, &aces, &aces_nr);
if (rc != 0)
return rc;
}
rc = D_FPRINTF(stream, "# Entries:\n");
if (rc != 0)
goto out;
if (acl == NULL || acl->dal_len == 0) {
rc = D_FPRINTF(stream, "# None\n");
goto out;
}
for (i = 0; i < aces_nr; i++) {
if (verbose) {
// 调用 daos_ace_str_get_verbose() 将 ACE 的
// 格式化字符串转换为详细字符串
rc = verbose_str_to_stream(stream, aces[i]);
if (rc != 0)
goto out;
}
rc = D_FPRINTF(stream, "%s\n", aces[i]);
if (rc != 0)
goto out;
}
out:
if (aces != NULL) {
free_strings(aces, aces_nr);
D_FREE(aces);
}
return rc;
}
相关信息
Emai: debugzhang@163.com
- 点赞
- 收藏
- 关注作者
评论(0)