//写在前面,我本来想好好写篇blog的,可是实践时才发现自个儿实在是太懒了,鉴于APUE已经讲的十分详细易懂,我也就干脆直接抄原文了,哈哈!
话说unix里面有几个ID,总是难以分清楚,我在这里也试着说一说。
1) 首先需要清楚的是每个进程里至少有6个ID,分别是
real user ID |
who we really are |
effective user ID |
used for file access permission checks |
saved set-user-ID |
saved by exec functions |
struct stat {
mode_t st_mode; /* file type & mode (permissions) */
ino_t st_ino; /* i-node number (serial number) */
dev_t st_dev; /* device number (file system) */
dev_t st_rdev; /* device number for special files */
nlink_t st_nlink; /* number of links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
off_t st_size; /* size in bytes, for regular files */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last file status change */
blksize_t st_blksize; /* best I/O block size */
blkcnt_t st_blocks; /* number of disk blocks allocated */
};
3) 同时在st_mode里还有一个位叫作set-user-ID bit,作用是当执行此文件时,
将进程的有效用户ID设置为文件的所有者。同理也有set-group-ID bit
下面开始说说这些ID的用处:
• The real user ID and real group ID identify who we really are. These two fields are taken from our entry in the password file when we log in. Normally, these values don't change during a login session, although there are ways for a superuser process to change them.
• The effective user ID, effective group ID, and supplementary group IDs determine our file access permissions.
• The saved set-user-ID and saved set-group-ID contain copies of the effective user ID and the effective group ID when a program is executed. (最后例子说明)
文件i结点里的:(上面已经说过)
Every file has an owner and a group owner. The owner is specified by the st_uid member of the stat structure; the group owner, by the st_gid member.
然后set-user-ID bit到底怎么使用呢?看下面的例子:
For example, if the owner of the file is the superuser and if the file's set-user-ID bit is set, then while that program file is running as a process, it has superuser privileges. This happens regardless of the real user ID of the process that executes the file. As an example, the UNIX System program that allows anyone to change his or her password, passwd(1), is a set-user-ID program. This is required so that the program can write the new password to the password file, typically either /etc/passwd or /etc/shadow, files that should be writable only by the superuser. Because a process that is running set-user-ID to some other user usually assumes extra permissions, it must be written carefully.(这里有篇不错的blog).
最后来看一个saved set-user-ID的例子:
(为了能看懂这个例子,需要了解setuid()函数,若如你不清楚,可以参考文章最后的补充)
Example
To see the utility of the saved set-user-ID feature, let's examine the operation of a program that uses it. We'll look at the man(1) program, which is used to display online manual pages. The man program can be installed either set-user-ID or set-group-ID to a specific user or group, usually one reserved for man itself. The man program can be made to read and possibly overwrite files in locations that are chosen either through a configuration file (usually /etc/man.config or /etc/manpath.config) or using a command-line option.
The man program might have to execute several other commands to process the files containing the manual page to be displayed. To prevent being tricked into running the wrong commands or overwriting the wrong files, the man command has to switch between two sets of privileges: those of the user running the man command and those of the user that owns the man executable file. The following steps take place.
1. Assuming that the man program file is owned by the user name man and has its set-user-ID bit set, when we exec it, we have
real user ID = our user ID
effective user ID = man
saved set-user-ID = man
2. The man program accesses the required configuration files and manual pages. These files are owned by the user name man, but because the effective user ID is man, file access is allowed.
3. Before man runs any command on our behalf, it calls setuid(getuid()). Because we are not a superuser process, this changes only the effective user ID. We have
real user ID = our user ID (unchanged)
effective user ID = our user ID
saved set-user-ID = man (unchanged)
Now the man process is running with our user ID as its effective user ID. This means that we can access only the files to which we have normal access. We have no additional permissions. It can safely execute any filter on our behalf.
4. When the filter is done, man calls setuid(euid), where euid is the numerical user ID for the user name man. (This was saved by man by calling geteuid.) This call is allowed because the argument to setuid equals the saved set-user-ID. (This is why we need the saved set-user-ID.) Now we have
real user ID = our user ID (unchanged)
effective user ID = man
saved set-user-ID = man (unchanged)
5. The man program can now operate on its files, as its effective user ID is man.
By using the saved set-user-ID in this fashion, we can use the extra privileges granted to us by the set-user-ID of the program file at the beginning of the process and at the end of the process. In between, however, the process runs with our normal permissions. If we weren't able to switch back to the saved set-user-ID at the end, we might be tempted to retain the extra permissions the whole time we were running (which is asking for trouble).
补充:
int setuid(uid_t uid);
We can set the real user ID and effective user ID with the setuid function. There are rules for who can change the IDs. Let's consider only the user ID for now. (Everything we describe for the user ID also applies to the group ID.)
1. If the process has superuser privileges, the setuid function sets the real user ID, effective user ID, and saved set-user-ID to uid.
2. If the process does not have superuser privileges, but uid equals either the real user ID or the saved set-user-ID, setuid sets only the effective user ID to uid. The real user ID and the saved set-user-ID are not changed.
3. If neither of these two conditions is true, errno is set to EPERM, and 1 is returned.