这一章开始看的有点懵,在网上搜索到一篇文章有助于理解链接地址Set-User-ID
1.每个进程都有一套用数字表示的用户ID和组ID。具体有实际用户ID(real user ID), 实际组ID(real group ID),有效用户ID(effective user ID),有效组ID(effective group ID),保存的set-user-ID和set-group-ID,文件系统用户ID和文件系统组ID,辅助组ID。
将上述种种ID称为进程凭证。
2.实际用户ID和实际组ID:
这两个ID确定了进程所属的用户和组。当用户登录时,会从/etc/passwd文件中读取UID和GID作为实际用户ID和组ID,后续创建的进程都从父进程处继承实际 UID和实际 GID。
3.有效用户ID和有效组ID:
进程尝试访问各种资源时,系统会依据有效用户ID和有效组ID连同辅助组ID一起来确定授予该进程的权限。
有效用户ID为0的用进程拥有的超级用户的所有权限。这样的进程称之为特权级进程,某些系统调用只能由特权级进程执行。
一般有效ID等实际ID,但会有一些操作导致这两种不一样。
4.set-user-id 和 set-group-id程序
set-user-id程序可以将进程的有效id修改为文件属主的ID,从而获得常规情况下并不具有的权限。set-group-id实现同样的功能。比如sudo就可以暂时获得root的权限。
可以使用chmod命令来设置权限位:
$ chmod u+s prog
$ chmod g+s prog
$ ls -l prog
-rwsr-sr-x 1 root root 302585 Jun 26 15:05 prog
可以发现设置set-user-id后,x标志位别替换为s,当运行set-user-id进程时,内核会将该进程的UID设置为该文件属主的UID,则该进程拥有了对该文件的所有权限。同样推理可知,如果一个文件的属主为root,并且该文件可以设置set-user-id,则某个进程调用set-user-id时,内核就会将该进程的有效ID设置为0(root),则该进程暂时取得了root权限。注意修改的是进程的ID,不是文件的ID。
5.保存set-user-id 和保存set-group-id.
当set-user-id时,会发生如下步骤:
1.若可执行文件的set-user-id权限位已开,则将进程的有效用户ID设置为文件的属主ID,若没开启,则进程的有效ID保持不变。
2.保存set-user-id和保存set-group-id的值由对应的有效ID复制而来,无论正在执行的文件是否设置了set-user-id,复制都会进行。
举例说明:
假设某进程的实际id,有效id,保存的set-user-id都为1000,当其执行了root用户的拥有set-user-id的程序后,进程的ID会发生如下变化:
real=1000 effective=0 saved=0
有不少系统调用,允许将set-user-id程序的有效用户id和实际用户id来回切换,则saved就是为了保存effective的副本。
6.文件系统用户id和组id
在linux系统中,要进行诸如打开文件,改变文件属主,修改文件权限之类的文件系统操作,决定其操作权限的是文件系统用户id和组id。一般文件系统id和有效id是一致的。只有当使用linux的两个系统调用setfsuid()和setfsgid()时才会不一样。
7.获取和修改进程凭证

示例程序

/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2015.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 9-1 */

/* idshow.c

   Display all user and group identifiers associated with a process.

   Note: This program uses Linux-specific calls and the Linux-specific
   file-system user and group IDs.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/fsuid.h>
#include <limits.h>
#include "ugid_functions.h"   /* userNameFromId() & groupNameFromId() */
#include "tlpi_hdr.h"

#define SG_SIZE (NGROUPS_MAX + 1)

int
main(int argc, char *argv[])
{
    uid_t ruid, euid, suid, fsuid;
    gid_t rgid, egid, sgid, fsgid;
    gid_t suppGroups[SG_SIZE];
    int numGroups, j;
    char *p;

    if (getresuid(&ruid, &euid, &suid) == -1)
        errExit("getresuid");
    if (getresgid(&rgid, &egid, &sgid) == -1)
        errExit("getresgid");

    /* Attempts to change the file-system IDs are always ignored
       for unprivileged processes, but even so, the following
       calls return the current file-system IDs */

    fsuid = setfsuid(0);
    fsgid = setfsgid(0);

    printf("UID: ");
    p = userNameFromId(ruid);
    printf("real=%s (%ld); ", (p == NULL) ? "???" : p, (long) ruid);
    p = userNameFromId(euid);
    printf("eff=%s (%ld); ", (p == NULL) ? "???" : p, (long) euid);
    p = userNameFromId(suid);
    printf("saved=%s (%ld); ", (p == NULL) ? "???" : p, (long) suid);
    p = userNameFromId(fsuid);
    printf("fs=%s (%ld); ", (p == NULL) ? "???" : p, (long) fsuid);
    printf("\n");

    printf("GID: ");
    p = groupNameFromId(rgid);
    printf("real=%s (%ld); ", (p == NULL) ? "???" : p, (long) rgid);
    p = groupNameFromId(egid);
    printf("eff=%s (%ld); ", (p == NULL) ? "???" : p, (long) egid);
    p = groupNameFromId(sgid);
    printf("saved=%s (%ld); ", (p == NULL) ? "???" : p, (long) sgid);
    p = groupNameFromId(fsgid);
    printf("fs=%s (%ld); ", (p == NULL) ? "???" : p, (long) fsgid);
    printf("\n");

    numGroups = getgroups(SG_SIZE, suppGroups);
    if (numGroups == -1)
        errExit("getgroups");

    printf("Supplementary groups (%d): ", numGroups);
    for (j = 0; j < numGroups; j++) {
        p = groupNameFromId(suppGroups[j]);
        printf("%s (%ld) ", (p == NULL) ? "???" : p, (long) suppGroups[j]);
    }
    printf("\n");

    exit(EXIT_SUCCESS);
}

总结:
每个进程都有一干与之相关的用户ID和组ID。实际ID定义了进程所属。在大多数UNIX实现中,进程对诸如文件之类资源的访问,其许可权限由有效ID决定。然后Linux会使用文件系统ID来决定对文件的访问权限,而将有效ID用于检查其它权限。进程辅助组ID则时由出于权限检查目的而另行设立的进程属组集合。存在各种系统调用和库函数支持进程获取和修改其用户ID和组ID。
set-user-ID程序运行时,会将进程有效用户ID设置为文件属主的用户ID。运行某个特殊程序时,这种机制支持用户假借其它用户的身份和特权。相应的,set-group-ID程序会修改运行改程序的进程的有效组ID。保存set-user-ID和保存set-group-ID允许set-user-ID和set-group-ID程序临时性的放弃特权,并在之后恢复特权。
0在用户ID中卓尔不群。通常仅为一个名为root的账号所有。有效用户ID为0的进程属特权级进程。换言之,对于进程发起的各种系统调用,可免于接受通常所要经历的诸多权限检查。