【TLPI读书笔记】 八、用户和组
密码文件:/etc/passwd
针对系统的每个用户账号,系统密码文件/etc/passwd
会专列一行进行描述。每行都包含7个字段,之间用冒号分割,如下所示:
- 登录名
- 经过加密的密码
- 用户ID(UID)
- 组ID(GID)
- 注释
- 主目录
- 登录shell
shadow密码文件:/etc/shadow
为了安全起见,/etc/shadow
应运而生。其理念是用户所有非敏感信息存放于“人人可读”的密码文件中,而经过加密处理的密码则由shadow
密码文件单独维护,仅供具有特权的程序读取。shadow
密码文件包含有:
- 登录名(用来匹配密码文件中的相应记录)
- 经过加密的密码
- 其他若干与安全相关的字段
组文件:/etc/group
对用户所属组信息的定义由两部分组成:
- 密码文件中相应用户记录的组ID字段
- 组文件列出的用户所属各组
系统中的每个组在组文件/etc/group
中都对应着一条记录。每条记录包含4个字段,之间以冒号分割,如下所示:
- 组名
- 经过加密处理的密码
- 组ID(GID)
- 用户列表
获取用户和组的信息
从密码文件获取记录
函数getpwnam()
和getpwuid()
的作用是从密码文件中获取记录
#include <pwd.h>
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
Both return a pointer on success, or NULL on error;
see main text for description of the "not found" case
参数
name
:用户名uid
:用户ID
返回值
- 成功:返回指向
passwd
结构(静态分配)的指针,该结构字段对应密码文件字段 - 失败:没有找到相关用户记录会返回
NULL
,且不会改变errno
;如果出错会设置errno
- 成功:返回指向
从组文件获取记录
函数getgrnam()
和getgrgid()
的作用是从组文件中获取记录
#include <grp.h>
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
Both return a pointer on success, or NULL on error;
see main text for description of the "not found" case
参数
name
:组名gid
:组ID
返回值
- 成功:返回指向
group
结构(静态分配)的指针,该结构字段对应组文件字段 - 失败:没有找到相关用户记录会返回
NULL
,且不会改变errno
;如果出错会设置errno
- 成功:返回指向
扫描密码文件和组文件中的所有记录
函数setpwent()
、getpwent()
和endpwent()
的作用是按顺序扫描密码文件中的记录
#include <pwd.h>
struct passwd *getpwent(void);
Returns pointer on success, or NULL on end of stream or error
void setpwent(void);
void endpwent(void);
函数getpwent()
能够从密码文件中逐条返回记录,当不再有记录(或出错)时,该函数返回NULL
。getpwent()
一经调用,会自动打开密码文件。当密码文件处理完毕后,可调用endpwent()
将其关闭。
函数getgrent()
、setgrent()
和endgrent()
针对组文件执行类似的任务。
从shadow
密码文件中获取记录
下列函数作用包括从shadow
密码文件中获取个别记录,以及扫描该文件中的所有记录。
#include <shadow.h>
struct spwd *getspnam(const char *name);
Returns pointer on success, or NULL on not found or error
struct spwd *getspent(void);
Returns pointer on success, or NULL on end of stream or error
void setspent(void);
void endspent(void);
参数
name
:用户名
返回值
- 成功:返回指向
spwd
结构(静态分配)的指针 - 失败:没有找到相关用户记录会返回
NULL
,且不会改变errno
;如果出错会设置errno
- 成功:返回指向
密码加密和用户认证
由于安全方面的原因,UNIX系统采用单向加密算法对密码进行加密,这意味着由密码的加密形式将无法还原出原始密码。因此,验证候选密码的唯一方法是使用同一算法对其进行加密,并将加密结果存储于/etc/shadow
中的密码进行匹配。加密算法封装于crypt()
函数之中。
#define _XOPEN_SOURCE
#include <unistd.h>
char *crypt(const char *key, const char *salt);
Returns pointer to statically allocated string containing
encrypted password on success, or NULL on error
crypt()
算法会接受一个最长可达8字符的密钥(即密码),并施之以数据加密算法(DES)的一种变体。
参数
key
:秘钥(密码)salt
:盐(指向两字符的字符串),用来扰动DES算法。该值会附加在密文之前,即该值可以在shadow
文件中密文的前两个字符取得
返回值
- 成功:返回加密后的字符指针(长度为13个字符的字符串)
在shell中输入密码一般都没有回显,这就要用到getpass()
函数
#define _BSD_SOURCE
#include <unistd.h>
char *getpass(const char *prompt);
Returns pointer to statically allocated input password string on success, or NULL on error
getpass()
函数首先会屏蔽回显功能,并停止对终端特殊字符的处理。返回结果之前会将终端设置还原。
参数
prompt
:提示信息,用来在输入密码前显示
返回值
- 成功:返回以
NULL
结尾的输入字符串
- 成功:返回以
读取密码的程序应立即加密密码,并尽快将密码的明文从内存中抹去(以空字符覆盖)
总结
本章介绍了用户相关的3个主要文件,然后介绍了获取用户信息和用户认证的系统调用。