/etc/passwd
在 Linux 和 Unix 系统中,/etc/passwd
是一个重要的系统文件,用于存储用户账户的信息。尽管文件名是 passwd
,但它并不保存用户密码(密码通常存储在 /etc/shadow
文件中),而是包含用户的基本信息。
/etc/passwd
文件结构
每行代表一个用户账户,字段由冒号 :
分隔。每行的格式如下:
ruby
复制代码
username:password:UID:GID:GECOS:home:shell
字段说明
-
username:用户名。
-
password:加密后的密码的占位符,通常是
x
或 ``,表示实际密码存储在/etc/shadow
中。 -
UID:用户的唯一标识符(User ID)。
-
GID:用户主组的唯一标识符(Group ID)。
-
GECOS:用户的全名或描述信息(可以包括其他信息,如电话号码)。
-
home:用户的主目录路径。
-
shell:用户登录后使用的默认 shell。
示例
以下是 /etc/passwd
文件中的一行示例:
johndoe:x:1001:1001:John Doe,,,:/home/johndoe:/bin/bash
在这个示例中:
-
用户名是
johndoe
。 -
密码字段是
x
,表示实际密码在/etc/shadow
中。 -
UID 和 GID 都是
1001
。 -
GECOS 字段包含了用户的全名。
-
用户的主目录是
/home/johndoe
。 -
默认 shell 是
/bin/bash
。
这些信息可以使用下面的 api 获取:
getpwuid //根据用户 ID 获取用户账户信息。
getpwent // 读取下一个用户账户信息。
getpwnam // 根据用户名获取用户账户信息。
他们返回一个 passwd 结构体。
passwd
结构体定义
struct passwd {
char *pw_name; // 用户名
char *pw_passwd; // 密码的占位符(通常是 x,表示在 /etc/shadow 中)
uid_t pw_uid; // 用户的唯一标识符(User ID)
gid_t pw_gid; // 用户主组的唯一标识符(Group ID)
char *pw_gecos; // 用户的全名或描述信息
char *pw_dir; // 用户的主目录路径
char *pw_shell; // 用户登录后使用的默认 shell
};
字段说明
-
**
pw_name
**:用户名,表示用户的登录名。 -
**
pw_passwd
**:密码字段的占位符。通常为x
,表示密码实际上存储在/etc/shadow
文件中,以提高安全性。 -
**
pw_uid
**:用户的唯一标识符(UID),在系统中唯一标识用户。 -
**
pw_gid
**:用户主组的唯一标识符(GID),表示该用户所属的主组。 -
**
pw_gecos
**:全名或描述信息,可以包括其他信息(如电话号码)。 -
**
pw_dir
**:用户的主目录路径,表示该用户的默认工作目录。 -
**
pw_shell
**:用户登录后的默认 shell,通常是/bin/bash
、/bin/sh
等。
在 Linux 系统中,/etc/passwd
文件曾经用来保存用户的基本信息,包括密码。然而,出于安全考虑,现代的 Linux 系统将密码从 /etc/passwd
移到了更安全的文件中,原因如下:
-
可读权限问题:
/etc/passwd
文件必须对所有用户可读,因为操作系统和很多程序都需要访问其中的用户信息(例如用户 ID、组 ID、用户名等)。如果密码明文或直接可解的哈希值保存在这个文件中,任何用户都可以读取并尝试破解密码,这存在严重的安全风险。 -
引入
/etc/shadow
文件:为了保护密码的安全,密码的哈希值现在存储在/etc/shadow
文件中。这个文件只有超级用户(root)或具备特殊权限的用户才能读取。这样,即使普通用户可以访问/etc/passwd
,他们也无法获取密码的哈希值,从而提高了系统的安全性。 -
分离敏感数据:通过将密码信息和其他用户信息分离,操作系统可以更好地保护敏感数据。
/etc/passwd
文件保留用户的基础信息,而密码哈希被转移到/etc/shadow
文件进行更严格的保护。
/etc/group
/etc/group
文件是 Unix 和 Linux 系统中用来定义用户组的配置文件。它是一个纯文本文件,每一行代表一个组,包含以下几个字段,并用冒号 (:
) 分隔:
-
组名:表示组的名称。
-
密码:通常为
x
或为空,表示组密码,几乎很少用到。 -
GID:组的唯一标识号(Group ID)。
-
用户列表:属于该组的用户列表,多个用户用逗号分隔。
例如,/etc/group
文件内容:
root:x:0:
sudo:x:27:user1,user2
users:x:100:
dev:x:101:user3,user4
在这个例子中:
-
root
组的 GID 为0
,没有其他用户。 -
sudo
组的 GID 为27
,成员有user1
和user2
。 -
users
组的 GID 为100
,没有列出成员。 -
dev
组的 GID 为101
,成员有user3
和user4
。
该文件主要用于管理组的成员关系,系统中的很多工具都会参考这个文件进行权限控制。
获取信息的 api:
getgrnam
getgrgid
返回一个 group 结构体:
struct group {
char *gr_name; // 组名(字符串)
char *gr_passwd; // 组密码(不常用,通常为占位符 "x")
gid_t gr_gid; // 组 ID(GID,Group ID)
char **gr_mem; // 指向用户成员列表的指针数组
};
结构体成员说明:
-
**
gr_name
**:指向一个字符串,表示组的名称。 -
**
gr_passwd
**:指向组密码的字符串(通常不使用,很多系统都用x
或者 `` 占位)。 -
**
gr_gid
**:表示组的 ID,类型是gid_t
,它是一个整数类型。 -
**
gr_mem
**:指向一个以空指针结尾的字符串数组,每个字符串是属于该组的用户名。
/etc/shadow
/etc/shadow
文件是 Linux 和 Unix 系统中存储用户密码及相关信息的文件。为了提高安全性,这个文件只能由超级用户(root)和具有适当权限的进程访问。它包含每个用户的加密密码及其他与密码相关的信息,如密码过期时间、修改时间等。
/etc/shadow
文件的每一行对应一个系统用户,字段用冒号(:
)分隔。典型的一行如下所示:
username:password:last_change:min:max:warn:inactive:expire:
字段详细说明:
-
username(用户名):表示该行对应的系统用户名。
-
password(加密密码):存储用户的加密密码。如果是空的,用户不需要密码即可登录。如果是
*
或!
,表示该账户已被禁用或锁定。 -
last_change(最后一次密码更改日期):表示从 1970 年 1 月 1 日(Unix 时间的起点)到最后一次更改密码的天数。这是一个整数。
-
min(最短密码更改间隔):表示两次密码修改之间最少需要的天数。
-
max(最长密码有效期):表示密码可以使用的最长天数,超过此天数后用户必须更改密码。
-
warn(密码过期警告天数):表示密码到期前系统警告用户需要更改密码的天数。
-
inactive(密码失效后的宽限天数):密码过期后,账户失效前允许的天数。如果设置为
1
,表示不使用此功能。 -
expire(账户到期时间):表示账户到期时间,是从 1970 年 1 月 1 日算起的天数。如果设置为
1
,表示账户永不过期。
例如,/etc/shadow
文件中的一行可能如下:
user1:$6$YdsC1...$laI7op...:18956:7:90:14:7:20000:
-
user1
是用户名。 -
$6$YdsC1...$laI7op...
是加密后的密码(采用了 SHA-512 哈希算法)。 -
18956
表示从 1970 年 1 月 1 日起算,用户最后一次更改密码的日期。 -
7
是密码最短更改间隔(7 天)。 -
90
是密码最长有效期(90 天)。 -
14
是密码到期前的警告天数。 -
7
是密码过期后的宽限天数。 -
20000
是账户到期时间。
安全性:
/etc/shadow
文件比 /etc/passwd
更安全,因为 /etc/passwd
文件是可读的,而 /etc/shadow
只有 root 和具有超级用户权限的用户才能读取。这是为了防止非授权用户访问加密的密码数据,即使密码经过加密处理,也不应向普通用户公开。
获取信息的 api:
struct spwd *getspnam(const char *name);
struct spwd *getspent(void); // 逐行读取 /etc/shadow 文件的内容
其定义如下:
struct spwd {
char *sp_namp; // 用户名
char *sp_pwdp; // 加密密码
long sp_lstchg; // 上次更改密码的日期(从 1970-01-01 起的天数)
long sp_min; // 密码最短使用期限(天数)
long sp_max; // 密码最长使用期限(天数)
long sp_warn; // 密码到期前的警告天数
long sp_inact; // 密码过期后的宽限天数
long sp_expire; // 账户到期时间(从 1970-01-01 起的天数)
unsigned long sp_flag; // 保留字段,未使用
};
可以使用 crypt 函数来校验密码:
char *crypt(const char *key, const char *salt);
拿 $6$YdsC1...$laI7op...
举例,字符串中的 $
是分隔符,该字符串被分割成3段:
-
算法 ID
-
盐
-
加密后的串
key 传递登录密码, salt 需要传 $6$YdsC1...
才行,不然 crypt 函数默认是 DES 加密。
时间
time()
gmtime()
gmtime_r()
localtime()
localtime_r()
mktime()
strftime()
asctime()
asctime_r()
ctime()
ctime_r()
这些函数其实都是在做一个变换。
time() 函数返回 time_t 类型,在大多数 64 位系统上,time_t
是 long
或 long long
类型,通常是 64 位的有符号整数。
gmtime
/localtime
将 time_t 类型转换成 struct tm 类型的指针。
struct tm {
int tm_sec; // 秒 [0, 60]
int tm_min; // 分 [0, 59]
int tm_hour; // 小时 [0, 23]
int tm_mday; // 日 [1, 31]
int tm_mon; // 月 [0, 11],0 表示 1 月
int tm_year; // 年,自 1900 起
int tm_wday; // 一周中的第几天 [0, 6],0 表示周日
int tm_yday; // 一年中的第几天 [0, 365]
int tm_isdst; // 夏令时标志,正数表示启用夏令时,0 表示不使用,负数表示未知
};
mktime
将 tm 转 time_t。
strftime
格式化时间。
asctime
/ctime
将时间转出可读字符串。
具体函数原型可看man文档。
注意:在 Linux 中,localtime()
函数将时间(通常是从 time()
函数返回的 time_t
类型)转换为本地时间,并返回一个指向 struct tm
结构体的指针。这个指针指向的内存区域是由 C 库内部维护的一个静态对象,因此它的内容可能会被后续调用的时间相关函数(例如再次调用 localtime()
或 gmtime()
)修改。
需要熟悉一下,linux API 的设计方式,多看文档,文档里面没有提到需要手动释放函数返回的指针,那么就是静态分配方式。
标签:数据文件,密码,passwd,APUE03,系统,用户,etc,shadow,pw From: https://blog.csdn.net/a5right/article/details/143358010