linux用户信息存储在/etc/passwd、/etc/shadow文件里面。linux组信息存储在/etc/group里面。
用来存储用户除密码以外的信息。一行一个用户。比如:
1 2 3 4 5 6 7 | # cat /etc/passwd root:x:0:0:root: /root : /bin/ash daemon:*:1:1:daemon: /var : /bin/false ftp :*:55:55: ftp : /home/ftp : /bin/false network:*:101:101:network: /var : /bin/false nobody:*:65534:65534:nobody: /var : /bin/false dnsmasq:x:453:453:dnsmasq: /var/run/dnsmasq : /bin/false |
每一行包含冒号分开的下列信息:
• login name
• optional encrypted password
• numerical user ID
• numerical group ID
• user name or comment field
• user home directory
• optional user command interpreter
对于password,如果是一个x字符,表示密码存储在/etc/shadow,如果是*或者是!等非法的加密字符,则表示这个用户无法登录。如果是空,则表示没有密码。不需要密码即可登录。
man 5 passwd查看具体的格式说明
用来存储用户的密码信息。
1 2 3 4 5 6 7 | # cat /etc/shadow root:3gDFV00sl7WEs:19971:0:99999:7::: daemon:*:0:0:99999:7::: ftp :*:0:0:99999:7::: network:*:0:0:99999:7::: nobody:*:0:0:99999:7::: dnsmasq:x:0:0:99999:7::: |
也是冒号分开,每个用户有9个字段。最关键的是前面两个字段:
login name / encrypted password。
man 5 shadow,查看shadow文件的详细格式信息。
存储用户组信息
1 2 3 4 5 6 | $ cat /etc/group | head -n 5 root:x:0: daemon:x:1: bin:x:2: sys:x:3: adm:x:4:syslog,yuan |
它也是冒号分开的的4个字段。
group name
password
GID
user list
最后一列存储属于这个组的用户列表,用逗号分开。
man 5 group,查看group文件格式说明
kernel user/group
在linux内核中,进程结构体struct task_struct的real_cred指针指向一个struct cred结构体。
这个结构体的成员定义了user id、group id等信息。capabilities也是存储在这个结构体中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | kuid_t uid; /* real UID of the task */ kgid_t gid; /* real GID of the task */ kuid_t suid; /* saved UID of the task */ kgid_t sgid; /* saved GID of the task */ kuid_t euid; /* effective UID of the task */ kgid_t egid; /* effective GID of the task */ kuid_t fsuid; /* UID for VFS ops */ kgid_t fsgid; /* GID for VFS ops */ unsigned securebits; /* SUID-less security management */ kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ kernel_cap_t cap_bset; /* capability bounding set */ kernel_cap_t cap_ambient; /* Ambient capability set */ struct group_info *group_info; /* supplementary groups for euid/fsgid */ |
user id,有下面3类:
real user id
effective user id
saved user id
group id有4类:
real group id
effective group id
saved group id
supplementary group IDs
此外还有
filesytem user id
filesystem group id
文件系统权限检查使用fsuid/fsgid,但是通常情况下fsuid等于effective user id,fsgid等于effective group id。
某些特殊场景,比如以前的Linux NFS server,两者不一样。
系统调用setfsuid/setfsgid可以修改,其它场景下,fs id和user id是相同的。下面都是讨论相同的情况。
Real user ID 和 real group ID,决定了进程的拥有者。使用getuid()、getgid()系统调用读取这两个ID。
access()系统调用,检查文件访问权限时,使用的是real user (group) id。
但是,内核以effective user id和effective group id,作为进程访问内核资源权限检查的id。
real和effective,对于普通用户运行普通进程时,这两者是相同的。比如,普通用户执行id命令(查看当前用户id的命令):
$ id
uid=1000(yuan) gid=1000(yuan) groups=1000(yuan),4(adm),24(cdrom),
如果把id的owner改成root,然后设置setuid,这样id就会以root权限运行,此时执行id:
$ cp /usr/bin/id .
$ sudo chown root id
$ sudo chmod u+s id
$ ./id
uid=1000(yuan) gid=1000(yuan) euid=0(root) groups=1000(yuan),4(adm)
可以看到euid变成了0。但是real user id还是1000
使用geteuid, getegid系统调用,可以读取effective user (group) ID。
saved user (group) id的用途是:
Having a saved user id allows you to drop your privileges (by switching the effective uid to the real one) and then regain them (by switching the effective uid to the saved one) only when needed.
saved user id的用途,是可以存储之前的euid,然后在切换回原来的。
id相关系统调用
uid_t getuid(void); uid_t geteuid(void); int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); | getuid() returns the real user ID of the calling process geteuid() returns the effective user ID of the calling process |
gid_t getgid(void); gid_t getegid(void); int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); | |
int setuid(uid_t uid); | sets the effective user ID of the calling process. If the calling process is privileged, the real UID and saved set-user-ID are also set |
int seteuid(uid_t euid); | sets the effective user ID of the calling process. Unprivileged processes may only set the effective user ID to the real user ID, the effective user ID or the saved set-user-ID. |
int setreuid(uid_t ruid, uid_t euid); | sets real and effective user IDs of the calling process -1 for either the real or effective user ID forces the system to leave that ID unchanged Unprivileged processes may only set the effective user ID to the real user ID, the effective user ID, or the saved set-user-ID. Unprivileged users may only set the real user ID to the real user ID or the effective user ID. If the real user ID is set (i.e., ruid is not -1) or the effective user ID is set to a value not equal to the previous real user ID, the saved set-user-ID will be set to the new effective user ID. |
int setresuid(uid_t ruid, uid_t euid, uid_t suid); | sets the real user ID, the effective user ID, and the saved set-user-ID of the calling process -1, the corresponding value is not changed. An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID or the current saved set-user-ID. |
int setgid(gid_t gid); int setegid(gid_t egid); int setregid(gid_t rgid, gid_t egid); int setresgid(gid_t rgid, gid_t egid, gid_t sgid); | 完全等价于对应的set uid函数。 |
int getgroups(int size, gid_t list[]); int setgroups(size_t size, const gid_t *list); | get/set list of supplementary group IDs |
int setfsuid(uid_t fsuid); int setfsgid(uid_t fsgid); | set user identity used for filesystem checks |
具体见:man 7 credentials
C库提供了接口,来访问和修改Linux用户和组数据库文件。
/usr/include/pwd.h
POSIX Standard: 9.2.2 User Database Access <pwd.h>
定义了一个结构体,表示一个用户:
1 2 3 4 5 6 7 8 9 10 11 12 | /* A record in the user database. */ struct passwd { char *pw_name; /* Username. */ char *pw_passwd; /* Hashed passphrase, if shadow database not in use (see shadow.h). */ __uid_t pw_uid; /* User ID. */ __gid_t pw_gid; /* Group ID. */ char *pw_gecos; /* Real name. */ char *pw_dir; /* Home directory. */ char *pw_shell; /* Shell program. */ }; |
passwd数据操作接口
struct passwd *getpwent (void) void setpwent (void) void endpwent (void) int getpwent_r(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); | 这一组命令,可以读取所有的用户 getpwent()可以一直读一个用户,直到读完。 读完之后调用endpwent()释放内存。 setpwent()可以重新开始从0开始读(rewind) _r()是可重入版本。 |
struct passwd *fgetpwent(FILE *stream); int putpwent(const struct passwd *p, FILE *stream); int fgetpwent_r(FILE *stream, struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp); | fgetpwent()从一个特定的stream读取用户。 putpwent()将一个用户保存到特定的stream。 _r()是可重入版本。 |
struct passwd *getpwnam(const char *name); struct passwd *getpwuid(uid_t uid); int getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result); int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result); | getpwnam()根据用户名读取一个用户条目。 getpwuid()根据用户id读取一个用户条目。 _r()是可重入版本。 |
/usr/include/shadow.h
定义和pwd.h是类似的,定义了一个struct spwd结构体。然后访问接口也是和pwd.h类似的。
/usr/include/grp.h
POSIX Standard: 9.2.1 Group Database Access <grp.h>
定义了一个grp结构体,表示一个组:
1 2 3 4 5 6 7 8 | /* The group structure. */ struct group { char *gr_name; /* Group name. */ char *gr_passwd; /* Password. */ __gid_t gr_gid; /* Group ID. */ char **gr_mem; /* Member list. */ }; |
/etc/group数据库操作接口:
struct group *getgrent(void); void setgrent(void); void endgrent(void); int getgrent_r(struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); | 这一组命令,可以读取所有的组,和getpwent()类似。 _r()是可重入版本。 |
struct group *fgetgrent(FILE *stream); int putgrent(const struct group *grp, FILE *stream); int fgetgrent_r(FILE *stream, struct group *gbuf, char *buf, size_t buflen, struct group **gbufp); | 这一组命令,从一个指定的stream,操作group数据库 |
struct group *getgrgid(gid_t gid); struct group *getgrnam(const char *name); int getgrnam_r(const char *name, struct group *grp, char *buf, size_t buflen, struct group **result); int getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result); |
GNU coreutils提供的命令。
print real and effective user and group IDs。
Print user and group information for the specified USER, or (when USER omitted) for the current user.
$ id
uid=1000(yuan) gid=1000(yuan) groups=1000(yuan),4(adm),...
id还可以查看某个用户的id信息。比如root
$ id root
uid=0(root) gid=0(root) groups=0(root)
GNU coreutils提供的命令。
Print group memberships for each USERNAME or, if no USERNAME is specified, for the current process
$ groups
yuan adm cdrom sudo dip plugdev lpadmin lxd sambashare
GNU coreutils提供的命令。
print the user names of users currently logged in to the current host
$ users
yuan yuan yuan yuan yuan yuan yuan
$ who
yuan :0 2024-09-12 09:46 (:0)
yuan pts/0 2024-09-12 16:02 (192.168.137.1)
yuan pts/1 2024-09-14 14:53 (tmux(19959).%0)
yuan pts/2 2024-09-14 14:53 (tmux(19959).%1)
yuan pts/3 2024-09-20 10:48 (tmux(19959).%7)
yuan pts/4 2024-09-14 14:58 (tmux(19959).%3)
yuan pts/5 2024-09-14 19:21 (tmux(19959).%5)
这是一个底层的添加用户的命令,所有的linux发行版都支持。shadow-utils工具集提供。
官方仓库:https://github.com/shadow-maint/shadow
debian系统上,包含在passwd package里面。使用apt-file list passwd,可以查看passwd提供的所有的命令。
man 8 useradd说:
useradd is a low level utility for adding users. On Debian, administrators should usually use adduser(8) instead.
维护用户和组的其它命令还有:
passwd | change user password |
userdel | delete a user account and related files |
usermod | modify a user account |
groupdel | delete a group |
groupmod | modify a group definition on the system |
groupmems | administer members of a user's primary group |
newusers | update and create new users in batch |
chpasswd | update passwords in batch mode |
cppw cpgr | copy with locking the given file to the password or group file |
vipw vigr | edit the password, group, shadow-password or shadow-group file |
这个debian平台的一个工具,是一个Perl脚本,相比useradd,更加high level。它是交互式的。
其它命令还有
deluser/delgroup
参考