用户数据库将登录名和数字用户 ID 映射到账户记录。/etc/passwd 是常见表示,
但应用不能假设该文件是权威来源。glibc 通过名称服务切换(NSS)解析 passwd 数据库;
/etc/nsswitch.conf 中的 passwd: 条目可以查询本地文件、systemd、LDAP、NIS 或其他已安装提供程序。
PAM 不执行这些查询。PAM 处理身份验证和账户管理策略,而 NSS 回答身份数据库查询。
账户记录 #
<pwd.h> 定义公共记录:
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 或其他标记。
查找用户 #
两个函数执行键控查询:
#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(),因为它们将字符串放入调用方提供的存储:
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 按配置提供程序暴露的顺序访问账户记录:
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 上阅读或评论(发表评论需要登录)