openGauss源码解析(204)
openGauss源码解析:安全管理源码解析(15)
2. 关键执行流程
1) 系统变更类审计执行
pgaudit_system_recovery_ok
pgaudit_system_start_ok
pgaudit_system_stop_ok
pgaudit_user_login
pgaudit_user_logout
pgaudit_system_switchover_ok
pgaudit_user_no_privileges
pgaudit_lock_or_unlock_user
以上为openGauss支持系统变更类的审计执行函数,对于此类审计函数均嵌入内核相应调用流程中,下面以审计用户登入退出pgaudit_user_login函数为例说明其主体流程。
图9-26 登入审计执行流程
图9-26为服务端校验客户端登入时的主要流程。以登录失败场景为例,首先根据配置文件和客户端IP和用户信息确认采用的认证方式(包括sha256和SSL认证等);然后根据不同的认证方式采用不同的认证流程和客户端进行交互完成认证身份流程;如果认证失败,则线程进入退出流程上报客户端,此时调用pgaudit_user_login获取当前访问数据库名称和详细信息,并记录登录失败相关的审计日志。关键代码如下:
/* 拼接登录失败时候的详细信息,包括数据库名称和用户名 */
rc = snprintf_s(details,
PGAUDIT_MAXLENGTH,
PGAUDIT_MAXLENGTH - 1,
"login db(%s)failed,authentication for user(%s)failed",
port->database_name,
port->user_name);
securec_check_ss(rc, "\0", "\0");
/* 调用登入审计函数,记录审计日志 */
pgaudit_user_login(FALSE, port->database_name, details);
/* 退出当前线程 */
ereport(FATAL, (errcode(errcode_return), errmsg(errstr, port->user_name)))
登入审计日志接口pgaudit_user_login则主要完成审计日志记录接口需要参数的拼接,相关代码如下:
void pgaudit_user_login(bool login_ok, const char* object_name, const char* detaisinfo)
{
AuditType audit_type;
AuditResult audit_result;
Assert(detaisinfo);
/* 审计类型和审计结果拼装 */
if (login_ok) {
audit_type = AUDIT_LOGIN_SUCCESS;
audit_result = AUDIT_OK;
} else {
audit_type = AUDIT_LOGIN_FAILED;
audit_result = AUDIT_FAILED;
}
/* 直接调用审计日志记录接口 */
audit_report(audit_type, audit_result, object_name, detaisinfo);
}
2) DDL、DML语句审计执行
依据“1. 执行原理”节的描述,DDL、DML语句的执行分别由于pgaudit_ProcessUtility函数、pgaudit_ExecutorEnd函数来承载。此处首先介绍函数pgaudit_ProcessUtility函数,其主体结构代码如下:
static void pgaudit_ProcessUtility(Node* parsetree, const char* queryString, ...)
{
/* 适配不同编译选项 */
...
/* 开始匹配不同的DDL语句 */
switch (nodeTag(parsetree)) {
case T_CreateStmt: {
/* CREATE table语句审计执行 */
CreateStmt* createtablestmt = (CreateStmt*)(parsetree);
pgaudit_ddl_table(createtablestmt->relation->relname, queryString);
} break;
case T_AlterTableStmt: {
AlterTableStmt* altertablestmt = (AlterTableStmt*)(parsetree); /* Audit alter table */
if (altertablestmt->relkind == OBJECT_SEQUENCE) {
pgaudit_ddl_sequence(altertablestmt->relation->relname, queryString);
} else {
pgaudit_ddl_table(altertablestmt->relation->relname, queryString);
}
} break;
/* 匹配其他DDL类型语句逻辑 */
...
}}
DDL审计执行函数关键入参parsetree用于识别审计日志类型(create/drop/alter等操作)。入参queryString保存原始执行SQL语句,用于记录审计日志,略去非关键流程。此函数主要根据判断nodeTag所归属的DDL操作类型,进入不同的审计执行逻辑。以T_CreateStmt为例,识别当前语句CREATE table则进入pgaudit_ddl_table逻辑进行审计日志执行并最终记录审计日志。
图9-27 DDL审计执行流程
如图9-27所示,首先从当前SQL语句中获取执行对象类别校验其相应的审计开关是否开启(可以通过GUC参数audit_system_object控制)。当前支持开启的全量对象代码如下:
typedef enum {
DDL_DATABASE = 0,
DDL_SCHEMA,
DDL_USER,
DDL_TABLE,
DDL_INDEX,
DDL_VIEW,
DDL_TRIGGER,
DDL_FUNCTION,
DDL_TABLESPACE,
DDL_RESOURCEPOOL,
DDL_WORKLOAD,
DDL_SERVERFORHADOOP,
DDL_DATASOURCE,
DDL_NODEGROUP,
DDL_ROWLEVELSECURITY,
DDL_TYPE,
DDL_TEXTSEARCH,
DDL_DIRECTORY,
DDL_SYNONYM
} DDLType;
如果DDL操作的对象审计已开启则进行审计日志记录流程,在调用审计日志记录函数audit_report之前需要对包含密码的SQL语句进行脱敏处理。将包含密码的语句中(CREATE role/user)密码替换成‘********’用于隐藏敏感信息,至此针对CREATE DDL语句的审计执行完成。其他类型DDL语句主体流程一致,不做赘述。
下面介绍针对DML语句审计执行逻辑pgaudit_ExecutorEnd函数,整体调用流程如图9-28所示。
图9-28 DML审计执行流程
首先判断SQL查询语句所归属的查询类型。以CMD_SELECT类型为例,先获取查询对象的object_name用于审计日志记录中访问对象的记录,然后调用pgaudit_dml_table函数。相关代码如下:
case CMD_SELECT:
object_name = pgaudit_get_relation_name(queryDesc->estate->es_range_table);
pgaudit_dml_table_select(object_name, queryDesc->sourceText);
和DDL的记录一样,同样会对敏感信息进行脱敏后调用审计日志记录接口audit_report,至此对DML语句的审计日志执行完成。