glibc 使用者資料庫

最後更新: 2021-02-08 07:06:54 +0800 +0800
原文以英文撰寫。您正在閱讀AI翻譯版本。

使用者資料庫會將登入名稱與數值使用者 ID 映射至帳戶記錄。/etc/passwd 是常見表示法,但應用程式不得假設檔案是權威來源。glibc 透過 Name Service Switch(NSS)解析 passwd 資料庫,而 /etc/nsswitch.conf 中的 passwd: 項目可以查詢本機檔案、systemd、LDAP、NIS 或其他已安裝提供者。

PAM 不會執行這些查詢。PAM 處理驗證與帳戶管理原則,而 NSS 回答身分資料庫查詢。

帳戶記錄 #

<pwd.h> 定義公開記錄:

c
struct passwd {
    char  *pw_name;    /* login name */
    char  *pw_passwd;  /* password field, commonly "x" */
    uid_t  pw_uid;     /* user ID */
    gid_t  pw_gid;     /* primary group ID */
    char  *pw_gecos;   /* user information */
    char  *pw_dir;     /* home directory */
    char  *pw_shell;   /* login shell */
};

在一般 Linux 安裝中,歷史上的密碼欄位不再公開可重複使用的密碼雜湊。雜湊會保存在受另外保護的 shadow 資料庫中;公開的 passwd 記錄通常包含 x 或其他標記。

查詢使用者 #

兩個函數會執行索引查詢:

c
#include <pwd.h>

struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);

getpwnam() 依登入名稱搜尋,而 getpwuid() 依數值 UID 搜尋。成功時兩者都會回傳指向函式庫管理儲存空間的指標。後續的密碼資料庫呼叫可能覆寫該儲存空間,應用程式不得釋放它。NULL 可能代表「找不到」或錯誤,因此在呼叫前將 errno 設為零,並在需要區分時於呼叫後檢查它。

NSS 定義查詢語意。請求可能造成本機檔案存取、IPC 或網路流量,而 NSS 原則可以在每個提供者結果後停止或繼續。因此,直接剖析 /etc/passwd 不等同於呼叫這些介面。

使用可重入介面 #

多執行緒程式碼應使用 getpwnam_r() 與 getpwuid_r(),因為它們會將字串放入呼叫者提供的儲存空間:

c
struct passwd pwd;
struct passwd *result;
char *buf = malloc(buflen);

int error = getpwnam_r("build", &pwd, buf, buflen, &result);
if (error != 0) {
    /* error is an error number; ERANGE means the buffer was too small */
} else if (result == NULL) {
    /* no matching account */
} else {
    printf("%s has UID %ju\n", pwd.pw_name, (uintmax_t)pwd.pw_uid);
}

查詢完成時函數回傳零,包括找不到的情況。成功時 *result 指向所提供的 struct passwd,沒有相符記錄時則為 NULL。正的錯誤號碼會直接回報失敗,而不是透過回傳值 -1。若字元緩衝區無法容納所有被參照的字串,函數會回傳 ERANGE;請增加緩衝區後重試。sysconf() 的 _SC_GETPW_R_SIZE_MAX 是初始大小提示;回傳 -1 時,它不是保證的上限。

pwd 內的回傳指標都指向 buf 內部,因此使用記錄期間兩個物件都必須保持存活。可重入性保護函式庫儲存空間;它不會使 NSS 提供者的外部資料庫不可變,也不會使一連串獨立查詢具備原子性。

列舉資料庫 #

串流風格 API 會按照已設定提供者公開的順序造訪帳戶記錄:

c
setpwent();

struct passwd *entry;
while ((entry = getpwent()) != NULL)
    printf("%s\n", entry->pw_name);

endpwent();
  • setpwent() 會倒回列舉位置,並可能開啟提供者資源。
  • getpwent() 使用共用的內部儲存空間回傳下一個項目。
  • endpwent() 關閉列舉資源。

這些函數維護程序全域的列舉狀態,多個執行緒並行使用時並不安全。getpwent_r() 提供由呼叫者擁有的記錄與字串緩衝區,但列舉仍具有共用串流狀態;glibc 將 setpwent()、getpwent_r() 與 endpwent() 分類為:另一個執行緒操作該狀態時不安全。若其他執行緒可能同時列舉,請將完整列舉序列化。

對由網路支援的目錄而言,列舉也可能不完整或成本高昂。應用程式需要單一身分時,請使用索引查詢,不要將完整掃描視為穩定快照。

參考資料 #

評論

在 GitHub 上閱讀或評論(發表評論需要登入)