在前面介紹過“8.7 DeepSQL、8.8 小結”,本篇我們介紹第9章 安全管理原始碼決議中“9.1 安全管理整體架構和代碼概覽、9.2 安全認證”的相關精彩內容介紹,
openGauss作為新一代自治安全資料庫,提供了豐富的資料庫基礎安全能力,并逐步完善各類高階安全能力,這些安全能力涵蓋了訪問登錄認證、用戶權限管理、審計與追溯及資料安全隱私保護等,本章節將圍繞openGauss安全機制進行原始碼解讀,以幫助資料庫內核開發者在進行內核開發時正確地理解和使用安全功能介面,持續為產品提供安全保護能力,或基于當前安全能力進一步開發新的安全能力,
9.1 安全管理整體架構和代碼概覽
不同于資料庫其他業務模塊,安全管理模塊并非邏輯集中的,安全管理模塊中的安全能力是分散化的,在資料庫整個業務邏輯的不同階段提供對應的安全能力,從而構建資料庫整體縱深安全防御能力,一個完整的安全管理整體架構如圖9-1所示,

雖然整個安全機制是分散化的,但是每一個安全子模塊都獨立負責了一個完整的安全能力,如安全認證機制模塊主要解決用戶訪問控制、登錄通道安全問題;用戶角色管理模塊解決用戶創建及用戶權限管理問題,因此整體的安全管理體系架構的代碼解讀也將根據整個體系的劃分來進行描述,
1. 認證機制
認證機制子模塊在業務流程上主要包括認證組態檔管理、用戶身份識別、口令校驗等程序,其核心流程及介面定義如圖9-2所示,

2. 用戶角色管理
用戶角色管理子模塊在業務流程上主要包括角色創建、修改、洗掉、授權和回收,由于openGauss并未嚴格區分用戶和角色,因此用戶的管理與角色管理共用一套介面,僅在部分屬性上進行區分,角色管理子模塊涉及的功能及其對應的介面如圖9-3所示,

3. 物件訪問控制
物件訪問控制子模塊在業務流程上主要包括物件授權、物件權限回收以及實際物件操作時的物件權限檢查,其核心流程及介面定義如圖9-4所示,

4. 審計機制
審計機制子模塊主要包括審計日志的創建和管理以及資料庫的各類管理活動和業務活動的審計追溯,審計日志管理包括新創建審計日志、審計日志輪轉、審計日志清理,審計日志追溯包括活動發生時的日志記錄以及審計資訊查詢介面,其核心流程及介面定義如圖9-5所示,

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;
}
9.2.2 口令存盤
口令是安全認證程序中的重要憑證,openGauss資料庫在執行創建用戶或修改用戶口令操作時,會將口令通過單向哈希方式加密后存盤在pg_authid系統表中,口令加密的方式與引數“password_encryption_type”的配置有關,目前系統支持MD5、SHA256 + MD5(同時存盤SHA256和MD5哈希值)和SHA256三種方式,默認采用SHA256方式加密,為兼容PostgreSQL社區和第三方工具,openGauss保留了MD5方式,但此方式安全性較低不推薦用戶使用,
口令的加密方式與認證方式密切相關,選擇不同的加密方式需要對應的修改“pg_hba.conf”組態檔中的認證方式,口令加密與認證方式對應關系如表9-1所示,
password_encryption_type | 加密方式 (hash演算法) | 認證方式 (pg_hba.conf) | 加密函式介面 |
0 | MD5 | MD5 | pg_md5_encrypt |
1 | SHA256 + MD5 | SHA256或MD5 | calculate_encrypted_combined_password |
2(默認值) | SHA256 | SHA256 | calculate_encrypted_sha256_password |

如圖9-6所示,通過呼叫calculate_encrypted_sha256_password函式實作sha256加密方式、通過呼叫pg_md5_encrypt函式實作md5方式,而calculate_encrypted_combined_password函式則融合了前面兩種加密方式,加密后系統表中包含了sha256和md5兩種哈希值,實作sha256加密的calculate_encrypted_sha256_password函式執行流程如圖9-7所示,

9.2.3 認證機制
整個認證程序中身份認證完成后需要完成最后的認證識別,通過用戶名和密碼來驗證資料庫用戶的身份,判斷其是否為合法用戶,openGauss使用基于RFC5802協議的口令認證方案,該方案是一套包含服務器和客戶端雙向認證的用戶認證機制,
首先客戶端知道用戶名username和密碼password,客戶端發送用戶名username給服務端,服務端檢索相應的認證資訊,例如:salt、StoredKey、ServerKey和迭代次數,然后服務端發送鹽值salt和迭代次數給客戶端,接下來客戶端需要進行一些計算,給服務端發送ClientProof認證資訊,服務端通過ClientProof對客戶端進行認證,并發送ServerSignature給客戶端,最后客戶端通過ServerSignature對服務端進行認證,具體密鑰計算代碼如下所示:
SaltedPassword := Hi(password, salt, iteration_count) /*其中,Hi()本質上是PBKDF2*/
ClientKey := HMAC(SaltedPassword, "Client Key")
StoredKey := sha256(ClientKey)
ServerKey := HMAC(SaltedPassword, "Sever Key")
ClientSignature:=HMAC(StoredKey, token)
ServerSignature:= HMAC(ServerKey, token)
ClientProof:= ClientSignature XOR ClientKey
具體密鑰衍生程序如圖9-8所示,

服務器端存盤的是StoredKey和ServerKey:
(1) StoredKey用來驗證客戶端用戶身份,
服務端認證客戶端通過計算ClientSignature與客戶端發來的ClientProof進行異或運算,從而恢復得到ClientKey,然后將其進行HMAC(hash-based message authentication code,散列資訊認證碼)運算,將得到的值與StoredKey進行對比,如果相等,證明客戶端驗證通過,其中ClientSignature通過StoredKey和token(亂數)進行HMAC計算得到,
(2) ServerKey用來向客戶端表明自己身份的,
客戶端認證服務端,通過計算ServerSignature與服務端發來的值進行比較,如果相等,則完成對服務端的認證,其中ServerSignature通過ServerKey和token(亂數)進行HMAC計算得到,
(3) 在認證程序中,服務端可以計算出來ClientKey,驗證完后直接丟棄不必存盤,
防止服務端偽造認證資訊ClientProof,從而仿冒客戶端,
接下來詳細描述在一個認證會話期間的客戶端和服務端的資訊交換程序,如圖9-9所示,

認證流程為:
(1) 客戶端發送username,
(2) 服務端回傳鹽值salt、iteration-count(迭代次數)、ServerSignature以及隨機生成的字串token給客戶端,token是隨機生成字串,服務端通過計算得到的ServerSignature回傳給客戶端,
ServerSignature := HMAC(ServerKey, token)
(3) 客戶端認證服務端并發送認證回應,回應資訊包含客戶端認證資訊ClientProof,ClientProof證明客戶端擁有ClientKey,但是不通過網路的方式發送,在收到資訊后,計算ClientProof,
客戶端利用salt和iteration-count,從password計算得到SaltedPassword,然后通過圖9-9中的公式計算得到ClientKey、StoryKey和ServerKey,
客戶端通過StoredKey和token進行哈希計算得到ClientSignature:
ClientSignature := HMAC(StoredKey,token)
通過將ClientKey和ClientSignature進行異或得到ClientProof:
ClientProof := ClientKey XOR ClientSignature
將計算得到的ClientProof和第(2)步接收的隨機字串發送給服務端進行認證,
(4) 服務端接收并校驗客戶端資訊,
使用其保存的StoredKey和token通過HMAC演算法進行計算,然后與客戶端傳來的ClientProof進行異或,恢復ClientKey;再對ClientKey進行哈希計算,得到的結果與服務端保存的StoredKey進行比較,如果相等則服務端對客戶端的認證通過,否則認證失敗,
ClientSignature := HMAC(StoredKey, token)
HMAC(ClientProof XOR ClientSignature ) = StoredKey
客戶端認證的程序通過呼叫ClientAuthentication函式完成,該函式只有一個型別Port的引數,Port結構中存盤著客戶端相關資訊,Port結構與客戶端相關的部分欄位參見“9.2.1 身份”章節介紹,完整的客戶端認證程序見ClientAuthentication函式,代碼如下所示:
void ClientAuthentication(Port* port)
{
int status = STATUS_ERROR;
char details[PGAUDIT_MAXLENGTH] = {0};
char token[TOKEN_LENGTH + 1] = {0};
errno_t rc = EOK;
GS_UINT32 retval = 0;
hba_getauthmethod(port);
……
switch (port->hba->auth_method) {
case uaReject:
……
case uaImplicitReject:
……
/* 使用MD5口令認證 */
case uaMD5:
sendAuthRequest(port, AUTH_REQ_MD5);
status = recv_and_check_password_packet(port);
break;
/* 使用sha256認證方法 */
case uaSHA256:
/* 禁止使用初始用戶進行遠程連接 */
if (isRemoteInitialUser(port)) {
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("Forbid remote connection with initial user.")));
}
rc = memset_s(port->token, TOKEN_LENGTH * 2 + 1, 0, TOKEN_LENGTH * 2 + 1);
securec_check(rc, "\0", "\0");
HOLD_INTERRUPTS();
/* 生成亂數token */
retval = RAND_priv_bytes ((GS_UCHAR*)token, (GS_UINT32)TOKEN_LENGTH);
RESUME_INTERRUPTS();
CHECK_FOR_INTERRUPTS();
if (retval != 1) {
ereport(ERROR, (errmsg("Failed to Generate the random number,errcode:%u", retval)));
}
sha_bytes_to_hex8((uint8*)token, port->token);
port->token[TOKEN_LENGTH * 2] = '\0';
/* 發送認證請求到前端,認證碼為AUTH_REQ_SHA256 */
sendAuthRequest(port, AUTH_REQ_SHA256);
/* 接收并校驗客戶端的資訊 */
status = recv_and_check_password_packet(port);
break;
……
}
……
if (status == STATUS_OK)
sendAuthRequest(port, AUTH_REQ_OK);
else {
auth_failed(port, status);
}
/* 完成認證,關閉引數ImmediateInterruptOK */
t_thrd.int_cxt.ImmediateInterruptOK = false;
}
在這個ClientAuthentication函式中通過先后呼叫hba_getauthmethod函式、check_hba函式,檢查客戶端地址、所連接資料庫、用戶名在檔案HBA中是否有能匹配的HBA記錄(具體HBA及check_hba相關內容參見“9.2.1 身份”節),如果能夠找到匹配的HBA記錄,則將Port結構中相關認證方法的欄位設定為HBA記錄中的引數,同時狀態值為STATUS_OK,然后根據不同的認證方法,進行相應的認證程序,具體認證方法如表9-2所示,在認證程序中可能需要和客戶端進行多次互動,最后回傳如果為STAUS_OK,則表示認證成功,并將認證成功的資訊發送回客戶端,否則發送認證失敗的資訊,
認證方法 | 值 | 描述 |
uaReject | 0 | 無條件的拒絕連接 |
uaTrust | 3 | 無條件的允許連接,即允許匹配HBA記錄的客戶端連入資料庫 |
uaMD5 | 5 | 要求客戶端提供一個MD5加密口令進行認證 |
uaSHA256 | 6 | 要求客戶端提供SHA256加密口令進行認證 |
uaGSS | 7 | 通過GSS-API(generic security service,通用安全服務;application programming interface,應用編程介面)認證用戶 |
static int pg_password_sendauth(PGconn* conn, const char* password, AuthRequest areq)
{
int ret;
/* 初始化變數 */
……
char h[HMAC_LENGTH + 1] = {0};
char h_string[HMAC_LENGTH * 2 + 1] = {0};
char hmac_result[HMAC_LENGTH + 1] = {0};
char client_key_bytes[HMAC_LENGTH + 1] = {0};
switch (areq) {
case AUTH_REQ_MD5:
/* pg_md5_encrypt()通過MD5Salt進行MD5加密 */
……
case AUTH_REQ_MD5_SHA256:
……
case AUTH_REQ_SHA256: {
char* crypt_pwd2 = NULL;
if (SHA256_PASSWORD == conn->password_stored_method || PLAIN_PASSWORD == conn->password_stored_method) {
/* 通過SHA256方式加密密碼 */
if (!pg_sha256_encrypt(
password, conn->salt, strlen(conn->salt), (char*)buf, client_key_buf, conn->iteration_count))
return STATUS_ERROR;
rc = strncpy_s(server_key_string,
sizeof(server_key_string),
&buf[SHA256_LENGTH + SALT_STRING_LENGTH],
sizeof(server_key_string) - 1);
securec_check_c(rc, "\0", "\0");
rc = strncpy_s(stored_key_string,
sizeof(stored_key_string),
&buf[SHA256_LENGTH + SALT_STRING_LENGTH + HMAC_STRING_LENGTH],
sizeof(stored_key_string) - 1);
securec_check_c(rc, "\0", "\0");
server_key_string[sizeof(server_key_string) - 1] = '\0';
stored_key_string[sizeof(stored_key_string) - 1] = '\0';
sha_hex_to_bytes32(server_key_bytes, server_key_string);
sha_hex_to_bytes4(token, conn->token);
/* 通過server_key和token呼叫HMAC演算法計算,得到client_server_signature_bytes,通過該變數轉為字串變數,用來驗證與服務端傳來的server_signature是否相等, */
CRYPT_hmac_ret1 = CRYPT_hmac(NID_hmacWithSHA256,
(GS_UCHAR*)server_key_bytes,
HMAC_LENGTH,
(GS_UCHAR*)token,
TOKEN_LENGTH,
(GS_UCHAR*)client_server_signature_bytes,
(GS_UINT32*)&hmac_length);
if (CRYPT_hmac_ret1) {
return STATUS_ERROR;
}
sha_bytes_to_hex64((uint8*)client_server_signature_bytes, client_server_signature_string);
/* 呼叫函式strncmp判斷計算的client_server_signature_string和服務端傳來的server_signature值是否相等 */
if (PG_PROTOCOL_MINOR(conn->pversion) < PG_PROTOCOL_GAUSS_BASE &&
0 != strncmp(conn->server_signature, client_server_signature_string, HMAC_STRING_LENGTH)) {
pwd_to_send = fail_info; /* 不相等則認證失敗 */
} else {
sha_hex_to_bytes32(stored_key_bytes, stored_key_string);
/* 通過stored_key和token計算得到hmac_result */
CRYPT_hmac_ret2 = CRYPT_hmac(NID_hmacWithSHA256,
(GS_UCHAR*)stored_key_bytes,
STORED_KEY_LENGTH,
(GS_UCHAR*)token,
TOKEN_LENGTH,
(GS_UCHAR*)hmac_result,
(GS_UINT32*)&hmac_length);
if (CRYPT_hmac_ret2) {
return STATUS_ERROR;
}
sha_hex_to_bytes32(client_key_bytes, client_key_buf);
/* hmac_result和client_key_bytes異或得到h,然后將其發送給服務端,用于驗證客戶端 */
if (XOR_between_password(hmac_result, client_key_bytes, h, HMAC_LENGTH)) {
return STATUS_ERROR;
}
sha_bytes_to_hex64((uint8*)h, h_string);
pwd_to_send = h_string; /* 設定要發送給服務端的值 */
}
}
……
break;
/* 清空變數 */
……
return ret;
}
9.2.4 Kerberos安全認證
Kerberos是一種基于對稱密鑰技術的身份認證協議,開源組件Kerberos可以解決集群內節點或者行程之間的認證問題,即當開啟kerberos之后,惡意用戶無法仿冒集群內節點或行程來登錄資料庫系統,只有內部組件才可以持有用于認證的憑證,從而保證通過Kerberos認證,消減了仿冒風險,提升了資料庫系統的安全性,Kerberos協議認證互動如圖9-10所示,

其中各角色和定義如表9-3所示(為下文描述方便均以縮寫代替),
角色 | 說明 |
|---|---|
KDC(key distribution center,密鑰分發中心) | Kerberos服務程式 |
Client | 需要訪問服務的用戶(principal),KDC和Service會對用戶的身份進行認證 |
Service | 集成了Kerberos的服務,被訪問的服務,需要對客戶端進行認證 |
AS(authentication service,認證服務) | AS服務器用于身份的校驗, 內部會存盤所有的賬號資訊 |
TGS(ticket granting service,票據授權服務) | TGT(ticket-granting ticket)票據分發服務 |
openGauss可在資料庫系統部署完畢之后開啟Kerberos模式,即Kerberos服務部署在資料庫系統機器上,部署程序中會開啟Kerberos相關的服務,并派發憑證給集群內部所有的節點,初始化一系列Kerberos需要用到的環境變數,資料庫內核中通過呼叫GSS-API來實作Kebreros標準協議的通信內容,以openGauss主備之間的認證為例,在Kerberos開啟后openGauss內部行程之間認證流程如圖9-11所示,

Kerberos提供用戶(資料庫管理員)透明的認證機制,資料庫管理員無須感知Kerberos行程/部署情況,圖9-11中分兩部分描述Kerberos互動,左側虛線框內的Kerberos協議實作部分由OM工具完成,OM工具在Kerberos初始化的時候將KDC服務拉起(krb5kdc行程),KDC服務內置了兩個服務:AS和TGS服務,客戶端(openGauss主備等資料庫服務行程)在登錄對端之前會先和KDC互動拿到TGT(ticket granting ticket,根憑證),這個步驟由OM拉起的定時任務呼叫Kerebros提供重繪票據工具來實作,默認24小時重新獲取1次,該獲取TGT的程序對應Kerberos標準協議中的AS-REQ、AS-REP、TGS-REQ和TGS-REP模塊,
右側側虛線框內的資料庫內側認證,主要是圖9-11右側虛線框內的AP-REQ流程實作,簡化流程如圖9-12所示,

資料庫內核封裝GSS-API資料結構,實作跟外部API互動認證,關鍵資料結構源代碼檔案為“src\include\libpq\auth.h”,相關代碼如下:
typedef struct GssConn {
int sock;
gss_ctx_id_t gctx; /* GSS 背景關系 */
gss_name_t gtarg_nam; /* GSS 名稱 */
gss_buffer_desc ginbuf; /* GSS 輸入token */
gss_buffer_desc goutbuf; /* GSS 輸出token */
} GssConn;
/* 客戶端、服務端介面,用于封裝標準kerberos協議呼叫,其中客戶端介面用于向服務端 */
/* 發起訪問,同時回應服務端介面GssServerAuth發起的票據請求 */
int GssClientAuth(int socket, char* server_host);
int GssServerAuth(int socket, const char* krb_keyfile);

認證互動邏輯時序如圖9-13所示,認證流程如下,
(1) 服務端通過資料庫組態檔決定使用Kerberos協議對客戶端連接進行認證,
(2) 發起認證請求,客戶端準備需要Kerberos認證的環境和票證,發’P’報文回應請求并發送票證,
(3) 服務端驗證通過后會發送回應’R’報文,完成Kerberos認證,
感謝大家學習第9章 安全管理原始碼決議中“9.1 安全管理整體架構和代碼概覽、9.2 安全認證”的精彩內容,下一篇我們開啟“9.3 角色管理”的相關內容的介紹,
敬請期待,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/306467.html
標籤:其他
上一篇:基于樹莓派的智能魔鏡,支持人臉識別、情感監測、熱詞喚醒、語音互動,以及與手機APP互動的實作
下一篇:selenium--面試官問元素定位不到怎么辦?作業中還在為元素定位不到而煩惱?看這一文就夠了--內附決議跟代碼解決案例!讓面試官對你刮目相看
