ILD

linux user and group
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2024-9-24 站点:Inside Linux Development

linux用户信息存储在/etc/passwd、/etc/shadow文件里面。linux组信息存储在/etc/group里面。

/etc/passwd

用来存储用户除密码以外的信息。一行一个用户。比如:


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查看具体的格式说明

/etc/shadow

用来存储用户的密码信息。

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文件的详细格式信息。

/etc/group

存储用户组信息

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 Library API

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);



id

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)

groups

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


users/who

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)


useradd/groupadd

这是一个底层的添加用户的命令,所有的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.


维护用户和组的其它命令还有:

passwdchange user password
userdeldelete a user account and related files
usermodmodify a user account
groupdeldelete a group
groupmodmodify a group definition on the system
groupmemsadminister members of a user's primary group
newusersupdate and create new users in batch
chpasswdupdate 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


adduser/addgroup

这个debian平台的一个工具,是一个Perl脚本,相比useradd,更加high level。它是交互式的。


其它命令还有

deluser/delgroup


参考

https://unix.stackexchange.com/questions/18198/gid-current-primary-supplementary-effective-and-real-group-ids


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.