openGauss源码解析(191)

openGauss源码解析:安全管理源码解析(2)

9.2 安全认证

安全认证是数据库对外提供的第一道防线,数据库访问者只有完成身份识别、通过认证校验机制,才可以建立访问通道从事数据库管理活动。在整个安全认证过程中,涉及用户身份管理识别、用户口令安全存储以及完善的认证机制3大模块,而对于系统内部的进程间通信(主备),则需要调用业界通用的Kerberos认证机制,下面将主要围绕这4个子模块进行涉及原理介绍和代码解析。

9.2.1 身份认证

安全认证机制要解决的核心问题是谁可以访问数据库的问题。因此在定义身份时,除了描述访问用户,还要清晰定义整个过程中以何种方法访问、从何处访问、访问哪个数据库的问题,因此本小节重点介绍身份认证概念及源码。

身份认证是一个广义的概念,实际上定义了数据库系统的访问规则。openGauss的访问规则信息主要被记录在配置文件HBA(host-based authentication file,主机认证)中,HBA文件中的每一行代表一个访问规则,其书写格式如下:

hostssl DATABASE USER ADDRESS METHOD [OPTIONS]

其中第1个字段代表套接字方法,第两个字段代表允许被访问的数据库,第3个字段代表允许被访问的用户,第4个字段代表允许访问的IP地址,第5个字段代表访问的认证方式,第6个字段则作为对第5个字段认证信息的补充。在定义访问规则时,需要按照访问的优先级来组织信息,对于访问需求高的规则建议写在前面。

在openGauss源码中,定义了存储访问规则的关键数据结构HbaLine,核心元素代码如下所示:

typedef struct HbaLine

{

int linenumber; /* 规则行号 */

ConnType conntype; /* 连接套接字方法 */

List* databases; /* 允许访问的数据库集合*/

List* roles; /* 允许访问的用户组 */

char* hostname; /* 允许访问的IP地址 */

UserAuth auth_method; /* 认证方法 */

} HbaLine;

其中字段conntype、database、roles、hostname以及auth_method分别对应HBA配置文件中的套接字方法、允许被访问的数据库、允许被访问的用户、IP地址以及当前该规则的认证方法。

HBA文件在系统管理员配置完后存放在数据库服务侧。当某个用户通过数据库用户发起认证请求时,连接相关的信息都存放在关键数据结构Port中,代码如下所示:

typedef struct Port {

SockAddr laddr; /* 本地进程IP(internet protocol,互联网协议)地址信息 */

SockAddr raddr; /* 远端客户端进程IP地址信息 */

char* remote_host; /* 远端host(主机)名称字符串或IP地址*/

char* remote_hostname; /* 可选项,远程host名称字符串或IP地址*/

/* 发送给backend(后端)的数据包信息,包括访问的数据库名称、用户名、配置参数*/

char* database_name;

char* user_name;

char* cmdline_options;

List* guc_options;

/* 认证相关的配置信息*/

HbaLine* hba;

/* SSL(secure sockets layer,安全套接层,工作于套接字层的安全协议。)认证信息*/

#ifdef USE_SSL

SSL* ssl;

X509* peer;

char* peer_cn;

unsigned long count;

#endif

/* Kerberos认证数据结构信息*/

#ifdef ENABLE_GSS

char* krbsrvname; /* Kerberos服务进程名称*/

gss_ctx_id_t gss_ctx; /* GSS(generic security service,通用安全服务)数据内容*/

gss_cred_id_t gss_cred; /* 凭证信息*/

gss_name_t gss_name;

gss_buffer_desc gss_outbuf; /* GSS token信息*/

#endif

} Port;

其中Port结构中的user_name、database_name、raddr以及对应的HBA等字段就是认证相关的用户信息、访问数据库信息以及IP地址信息。与此同时Port结构中还包含了SSL认证相关的信息以及节点间做Kerberos认证相关的信息。有了Port信息,后台服务线程会根据前端传入的信息与HbaLine中记录的信息逐一比较,完成对应的身份识别。完整的身份认证过程见check_hba函数,其核心逻辑代码如下所示:

/**扫描HBA文件,寻找匹配连接请求的规则项 */

static void check_hba(hbaPort* port)

{

……

/* 获取当前连接用户的id */

roleid = get_role_oid(port->user_name, true);

foreach (line, t_thrd.libpq_cxt.parsed_hba_lines) {

hba = (HbaLine*)lfirst(line);

/* 认证连接行为分为本地连接行为和远程连接行为,需分开考虑 */

if (hba->conntype == ctLocal) {

/* 对于local套接字,仅允许初始安装用户本地登录 */

if (roleid == INITIAL_USER_ID) {

char sys_user[SYS_USERNAME_MAX + 1];

……

/* 基于本地环境的uid(user identity,用户身份标识)信息获取当前系统用户名 */

(void)getpwuid_r(uid, &pwtmp, pwbuf, pwbufsz, &pw);

……

/* 记录当前系统用户名 */

securec_check(strncpy_s(sys_user,SYS_USERNAME_MAX+1, pw->pw_name, SYS_USERNAME_MAX), "\0", "\0");

/* 对于访问用户与本地系统用户不相匹配的场景,均需提供密码 */

if (strcmp(port->user_name, sys_user) != 0)

hba->auth_method = uaSHA256;

} else if (hba->auth_method == uaTrust) {

hba->auth_method = uaSHA256;

}

……

} else {

/* 访问行为是远端访问行为,需要逐条判断包括认证方式在内的信息正确性 */

if (IS_AF_UNIX(port->raddr.addr.ss_family))

continue;

/* SSL连接请求套接字判断 */

#ifdef USE_SSL

if (port->ssl != NULL) {

if (hba->conntype == ctHostNoSSL)

continue;

} else {

if (hba->conntype == ctHostSSL)

continue;

}

#else

if (hba->conntype == ctHostSSL)

continue;

#endif

/* IP白名单校验 */

switch (hba->ip_cmp_method) {

case ipCmpMask:

if (hba->hostname != NULL) {

if (!check_hostname(port, hba->hostname))

continue;

} else {

if (!check_ip(&port->raddr, (struct sockaddr*)&hba->addr, (struct sockaddr*)&hba->mask))

continue;

}

break;

case ipCmpAll:

break;

case ipCmpSameHost:

case ipCmpSameNet:

if (!check_same_host_or_net(&port->raddr, hba->ip_cmp_method))

continue;

break;

default:

/* shouldn't get here, but deem it no-match if so */

continue;

}

} /* != ctLocal */

/* 校验数据库信息和用户信息 */

if (!check_db(port->database_name, port->user_name, roleid, hba->databases))

continue;

if (!check_role(port->user_name, roleid, hba->roles))

continue;

……

port->hba = hba;

return;

}

/* 没有匹配则拒绝当前连接请求 */

hba = (HbaLine*)palloc0(sizeof(HbaLine));

hba->auth_method = uaImplicitReject;

port->hba = hba;

}

posted @ 2024-05-06 10:45  openGauss-bot  阅读(2)  评论(0编辑  收藏  举报