今天在看nginx代码的时候发现调用initgroups这个函数. man了一下对描述不甚了解. 翻阅APUE也没太搞懂到底有什么作用.
-
if (initgroups(ccf->username, ccf->group) == -1) {
联系到该处代码的上下文, 应该是与权限相关. 于是google了一下附加组ID.
-
This form of group membership persisted until it was changed in 4.2BSD (circa 1983). With 4.2BSD, the concept of supplementary group IDs was introduced. Not only did we belong to the group corresponding to the group ID in our password file entry, but we also could belong to up to 16 additional groups. The file access permission checks were modified so that not only was the effective group ID compared to the file's group ID, but also all the supplementary group IDs were compared to the file's group ID.
摘自~eweiss/222_book/222_book/0201433079/ch06lev1sec5.html
也就是说一个用户可以最多同时属于16个其他的组. 查看系统文件/etc/group, 该文件每行有四个字段.
group_name: encrpyted_passwd: group_id: group_member
第四个字段即为其他属于这个组的用户名. 这时候再来看man手册关于initgroups就比较容易理解了.
-
The initgroups() function initializes the group access list by reading the group database /etc/group and using all groups of which user is a member. The additional group group is also added to the list.
也就是说这个函数会去查看/etc/group, 找出调用进程所属的其他组加入到附加组, 并且将指定的group参数也加入到附加组.
但是其作用依旧晦涩难懂. 为此我写了些代码做点实验来理解. 注意initgroups函数只能由root用户或者有权限的进程调用.
既然这个函数是关于权限的. 那么首先我以root用户创建一个-rw-rw----权限的普通文件.
-
# touch testfile
-
# chmod 660 testfile
该文件只有属于root用户或者root组的进程能够读写. 那么普通进程要打开这个文件一定会返回Permission denied错误. 假如一个普通进程的附加组里有root组ID.
那么该进程是可以访问这个文件的.于是我对/etc/group进行修改. 将我所使用的普通用户名加入到root组的组成员中. 不妨将用户名称为foobar.
但是foobar用户依然无法访问testfile文件. 猜想可能需要initgroups函数的介入才行了. 于是我以root用户写了一个set-user-id的程序.
-
#include <stdio.h>
-
#include <sys/stat.h>
-
#include <sys/types.h>
-
#include <fcntl.h>
-
#include <grp.h>
-
#include <unistd.h>
-
#include <errno.h>
-
static char buf[BUFSIZ];
-
/* 打印此时进程的各种ID */
-
void show_id()
-
{
-
uid_t uid = getuid();
-
gid_t gid = getgid();
-
uid_t euid = geteuid();
-
gid_t egid = getegid();
-
printf("UID: %d\n", uid);
-
printf("GID: %d\n", gid);
-
printf("EUID: %d\n", euid);
-
printf("EGID: %d\n", egid);
-
}
-
int main(int argc, char *argv[])
-
{
-
-
int fd;
-
int n;
-
int i;
-
if (argc != 2) {
-
fprintf(stderr, "Usage %s filename\n", argv[0]);
-
return -1;
-
}
-
show_id();
-
/* euid = 0才能执行initgroups函数调用 */
-
if (geteuid() == 0) {
-
if (initgroups("foobar", getgid()) == -1) {
-
perror("initgroups failed");
-
return -1;
-
}
-
/* 由于是set-user-id程序, 还原到进程的真实ID */
-
if (setuid(getuid()) == -1) {
-
perror("setuid failed");
-
return -1;
-
}
-
}
-
show_id();
-
fd = open(argv[1], O_RDONLY);
-
if (fd == -1) {
-
perror("open failed");
-
return -1;
-
}
-
while ( (n = read(fd, buf, BUFSIZ)) > 0) {
-
write(STDIN_FILENO, buf, n);
-
}
-
return 0;
-
}
以foobar身份运行这个程序可以访问并且输出testfile的内容. 说明这个进程的附加组进程中存在root组ID. 才得以通过权限的检查.
运行结果如下:
-
UID: 1001
-
GID: 1001
-
EUID: 0
-
EGID: 1001
-
UID: 1001
-
GID: 1001
-
EUID: 1001
-
EGID: 1001
-
hello world
到此: initgroups函数的意义就是讲组文件/etc/group中username参数所属的其他组ID以及group参数加入到进程的附加组ID当中. 关于组权限检查, 附加组ID和进程组ID是等效的.
阅读(4777) | 评论(0) | 转发(0) |