unix process uid

引用:Marshall Kirk McKusick, George V. Neville-Neil 的《The Design and Implementation of the FreeBSD Operating System》一书中对这个问题的论述。。。 

3.7 User, Group, and Other Identifiers

每个FreeBSD进程的状态里都有一个UID和一组GID。一个进程的文件系统访问特权就是由它的UID和GIDs来定义的。通常,这些标识符都是新进程创建的时候从父进程那儿自动继承过来的。只有超级用户才能修改一个进程的真实UID或真实GID。这个方案在各种特权之间进行了严格的区分,确保除了超级用户之外的其它任何用户都无法获得特权。

每个文件都有三组许可bit,分别用于所有者、组以及其它用户的读、写或执行许可。这些许可bit将按如下顺序进行检查:
    1、如果文件的UID和进程的UID相同,则仅应用所有者的许可,不再检查组和其它用户的许可。
    2、如果UID不匹配,但文件的GID和进程的众多GID之一匹配,则仅引用组的许可,不再检查所有者和其它用户的许可。
    3、仅当进程UID和GID与文件的UID和GID都不匹配时,才会去检查其它用户的许可。如果这些许可不允许所请求的操作,该操作就会失败。

一个进程的UID和GIDs是从它的父进程那儿继承来的。当一个用户登录的时候,login程序会在执行exec系统调用运行用户的登录shell之前设置好UID和GIDs,因此,后续的所有进程都会继承到恰当的标识符。

我们经常会想赋予一个用户有限的额外特权。......为了解决这个问题,内核允许程序在运行过程中创建被赋予特权的程序。以不同的UID运行的程序被称为setuid程序,以一个额外的组特权运行的程序被称为setgid程序。当运行一个setuid程序的时候,进程的许可将被扩展以包括与程序相关联的UID的许可。该程序的UID就被称为进程的有效UID,而进程最初的UID则被称为真实UID。同样,执行一个setgid程序会把进程的许可扩展为程序的GID的许可,相应的也有有效GID和真实GID的定义。

系统可以通过setuid和setgid程序来提供对文件或服务的受控访问。当然,这样的程序必须仔细编写,以保证它们只具有一些有限的功能。

UID和GIDs是作为每个进程的状态的一部分来维护的。由于历史原因,GIDs被实现成了一个显著的GID(即有效GID)和一个GIDs的辅助数组,不过在逻辑上则被看作是一组GIDs。在FreeBSD中,那个显著的GID就是GIDs数组中的第一个条目。辅助数组的大小是固定的(FreeBSD中是16),不过可以通过重新编译内核来修改这个数值。

FreeBSD是通过把运行setgid程序的进程的辅组数组中的第0个元素设置成文件的属组来实现setgid功能的。之后就可以像普通进程那样对许可进行检查了。由于存在额外的组,setgid程序就能够比一个运行没有特殊权限的程序的用户进程访问更多的文件。为了避免在运行一个setgid程序的时候丢失与第0个数组元素中的组相关联的特权,login程序会在初始化用户的辅组数组的时候将第0个数组元素复制到第一个数组元素中。因此,当运行的setgid程序修改第0个元素的时候,用户不会丢失任何特权,因为曾经保存在第0个数组元素中的组仍然可以从第一个数组元素中得到。

setuid功能是通过把进程的有效UID从用户的数值修改为被运行的程序的数值来实现的。和setgid一样,保护机制此时将毫不变样地允许访问,同时也不会意识到程序正在运行setuid。由于一个进程在同一时刻只能有一个UID,在运行setuid的时候就可能会丢失某些特权。在加载新的有效UID的时候,之前的真实UID将会继续作为真实UID。不过真实UID是不会用于任何确认检查的。

一个setuid进程在运行过程中可能会想临时取消它的特殊权限。比如,它可能只在运行开始和结束的时候需要访问某个受限文件的特殊权限。在其余的运行时间中,它应当只具有真实用户的权限。在BSD的早期版本中,特权的回收是通过对真实的和有效的UID进行切换来完成的。由于只有有效UID被用于访问控制,这个方法既提供了所需的语义,又提供了一个隐藏特殊权限的地方。这个方法的缺点是很容易就混淆了真实的和有效的UID。

在FreeBSD中,使用了一个额外的标识符,即saved UID来记录setuid程序的身份。当一个程序被exec之后,它的有效UID会被拷贝到saved UID中。下表中的第1行表示了一个没有特权的程序,它的真实、有效以及saved UID都是真实用户的数值。第2行正在运行中的setuid程序,它的有效UID被设置成了具有相应特权的UID,而这个特权UID也会被拷贝到saved UID中。

Actions affecting the real, effective, and saved UIDs.
_________________________________________________________________
Action            Real    Effective    Saved

1.exec-normal     R       R            R
2.exec-setuid     R       S            S
3.seteuid(R)      R       R            S
4.seteuid(S)      R       S            S
5.seteuid(R)      R       R            S
6.exec-normal     R       R            R

Key:R-real user identifier; S-special-privilege user identifier
_________________________________________________________________


seteuid系统调用只会修改有效UID,而不会影响真实的或saved UID。seteuid系统调用被允许将有效UID修改为真实的或saved UID的数值。表中的第3行和第4行表示了一个setuid程序在一直保持正确的真实UID的同时是如何放弃和重新取回它的特殊权限的。第5行和第6行表示了一个setuid程序可以运行一个子进程而不赋予它特殊权限。首先,它会把它的有效UID设置成真实UID。然后,当exec那个子进程的时候,有效UID就会被拷贝到saved UID中,从此就会失去对特权UID的所有访问。

与此类似,也有一个saved GID机制,允许进程在真实GID和最初的有效GID之间进行切换。




引用:IEEE Std 1003.1™, 2004 Edition

Standard for Information Technology —
Portable Operating System Interface (POSIX)

System Interfaces   中对setuid()的论述。。。


NAME

    setuid—set user ID

SYNOPSIS

    #include 

    int setuid(uid_t uid);

DESCRIPTION

    If the process has appropriate privileges, setuid( ) shall set the real user ID, 
    effective user ID, and the saved set-user-ID of the calling process to uid.

    If the process does not have appropriate privileges, but uid is equal to the real user
    ID or the saved set-user-ID, setuid( ) shall set the effective user ID to uid; the real
    user ID and saved set-user-ID shall remain unchanged.

    The setuid( ) function shall not affect the supplementary group list in any way.

RETURN VALUE

    Upon successful completion, 0 shall be returned. Otherwise, ?1 shall be returned and
    errno set to indicate the error.

ERRORS

    The setuid( ) function shall fail, return ?1, and set errno to the corresponding value
    if one or more of the following are true:

    [EINVAL]    The value of the uid argument is invalid and not supported by the
                implementation.

    [EPERM]     The process does not have appropriate privileges and uid does not match the
                real user ID or the saved set-user-ID.

EXAMPLES

    None.

APPLICATION USAGE

    None.

RATIONALE

    The various behaviors of the setuid( ) and setgid( ) functions when called by 
    non-privileged processes reflect the behavior of different historical implementations. 
    For portability, it is recommended that new non-privileged applications use the 
    seteuid( ) and setegid( ) functions instead.

    The saved set-user-ID capability allows a program to regain the effective user ID 
    established at the last exec call. Similarly, the saved set-group-ID capability allows 
    a program to regain the effective group ID established at the last exec call. 
    These capabilities are derived from System V. Without them, a program might have to run
    as superuser in order to perform the same functions, because superuser can write on the
    user’s files. This is a problem because such a program can write on any user’s files,
    and so must be carefully written to emulate the permissions of the calling process 
    properly. In System V, these capabilities have traditionally been implemented only via
    the setuid( ) and setgid( ) functions for non-privileged processes. The fact that the 
    behavior of those functions was different for privileged processes made them difficult 
    to use. The POSIX.1-1990 standard defined the setuid( ) function to behave differently
    for privileged and unprivileged users. When the caller had the appropriate privilege, 
    the function set the calling process’ real user ID, effective user ID, and saved 
    set-user ID on implementations that supported it. When the caller did not have the 
    appropriate privilege, the function set only the effective user ID, subject to 
    permission checks. The former use is generally needed for utilities like login and su, 
    which are not conforming applications and thus outside the scope of IEEE Std 
    1003.1-2001. These utilities wish to change the user ID irrevocably to a new value, 
    generally that of an unprivileged user. The latter use is needed for conforming 
    applications that are installed with the set-user-ID bit and need to perform operations
    using the real user ID.

    IEEE Std 1003.1-2001 augments the latter functionality with a mandatory feature named
    _POSIX_SAVED_IDS. This feature permits a set-user-ID application to switch its 
    effective user ID back and forth between the values of its exec-time real user ID and 
    effective user ID. Unfortunately, the POSIX.1-1990 standard did not permit a conforming 
    application using this feature to work properly when it happened to be executed with 
    the (implementation-defined) appropriate privilege. Furthermore, the application did 
    not even have a means to tell whether it had this privilege. Since the saved 
    set-user-ID feature is quite desirable for applications, as evidenced by the fact that 
    NIST required it in FIPS 151-2, it has been mandated by IEEE Std 1003.1-2001. However, 
    there are implementors who have been reluctant to support it given the limitation 
    described above.

    The 4.3BSD system handles the problem by supporting separate functions: setuid( ) 
    (which always sets both the real and effective user IDs, like setuid( ) in IEEE Std 
    1003.1-2001 for privileged users), and seteuid( ) (which always sets just the effective
    user ID, like setuid( ) in IEEE Std 1003.1-2001 for non-privileged users). This 
    separation of functionality into distinct functions seems desirable. 4.3BSD does not 
    support the saved set-user-ID feature. It supports similar functionality of switching 
    the effective user ID back and forth via setreuid( ), which permits reversing the real 
    and effective user IDs. This model seems less desirable than the saved set-user-ID 
    because the real user ID changes as a side effect. The current 4.4BSD includes saved
    effective IDs and uses them for seteuid( ) and setegid( ) as described above. The 
    setreuid( ) and setregid( ) functions will be deprecated or removed.

    The solution here is:

      . Require that all implementations support the functionality of the saved 
        set-user-ID, which is set by the exec functions and by privileged calls to 
        setuid( ).

      . Add the seteuid( ) and setegid( ) functions as portable alternatives to setuid( ) 
        and setgid( ) for non-privileged and privileged processes.

    Historical systems have provided two mechanisms for a set-user-ID process to change its
    effective user ID to be the same as its real user ID in such a way that it could return
    to the original effective user ID: the use of the setuid( ) function in the presence of
    a saved set-user-ID, or the use of the BSD setreuid( ) function, which was able to swap
    the real and effective user IDs. The changes included in IEEE Std 1003.1-2001 provide a
    new mechanism using seteuid( ) in conjunction with a saved set-user-ID. Thus, all 
    implementations with the new seteuid( ) mechanism will have a saved set-user-ID for 
    each process, and most of the behavior controlled by _POSIX_SAVED_IDS has been changed 
    to agree with the case where the option was defined. The kill ( ) function is an 
    exception. Implementors of the new seteuid( ) mechanism will generally be required to 
    maintain compatibility with the older mechanisms previously supported by their systems. 

    However, compatibility with this use of setreuid( ) and with the _POSIX_SAVED_IDS
    behavior of kill ( ) is unfortunately complicated. If an implementation with a saved 
    set-user-ID allows a process to use setreuid( ) to swap its real and effective user 
    IDs, but were to leave the saved set-user-ID unmodified, the process would then have an
    effective user ID equal to the original real user ID, and both real and saved 
    set-user-ID would be equal to the original effective user ID. In that state, the real 
    user would be unable to kill the process, even though the effective user ID of the 
    process matches that of the real user, if the kill ( ) behavior of _POSIX_SAVED_IDS
    was used. This is obviously not acceptable. The alternative choice, which is used in 
    at least one implementation, is to change the saved set-user-ID to the effective user 
    ID during most calls to setreuid( ). The standard developers considered that 
    alternative to be less correct than the retention of the old behavior of kill ( ) in 
    such systems. Current conforming applications shall accommodate either behavior from 
    kill ( ), and there appears to be no strong reason for kill( ) to check the saved 
    set-user-ID rather than the effective user ID.

FUTUREDIRECTIONS

    None.

SEE ALSO

    exec, getegid( ), geteuid( ), getgid( ), getuid( ), setegid( ), seteuid( ), setgid( ), 
    setregid( ), setreuid( ), the Base Definitions volume of IEEE Std 1003.1-2001, 
    <sys/types.h>, <unistd.h>

CHANGE HISTORY

    First released in Issue 1. Derived from Issue 1 of the SVID.

Issue 6

    In the SYNOPSIS, the optional include of the header is removed.
    The following new requirements on POSIX implementations derive from alignment with the
    Single UNIX Specification:

      . The requirement to include has been removed. Although 
        was required for conforming implementations of previous POSIX specifications, it 
        was not required for UNIX applications.

      . The functionality associated with _POSIX_SAVED_IDS is now mandatory. This is a FIPS
        requirement.

    The following changes were made to align with the IEEE P1003.1a draft standard:

      . The effects of setuid( ) in processes without appropriate privileges are changed.

      . A requirement that the supplementary group list is not affected is added.


flw 回复于:2006-08-31 13:56:16

一个进程的 real user ID 是指运行此进程的用户角色的 ID。
一个进程的 effective user ID 是指此进程目前实际有效的用户 ID(也就是权限的大小),effective user ID 主要用来校验权限时使用,比如打开文件、创建文件、修改文件、kill 别的进程,等等。
如果一个进程是以 root 身份来运行的,那么上面这两个 ID 可以用 setuid/seteuid 随便修改,想怎么改就怎么改,改来改去都可以。
但是如果一个进程是以普通用户身份来运行的,那么上面这两个 ID 一般来说是相同的,并且也不能随便修改。只有一种情况例外:此进程的可执行文件的权限标记中,设置了“设置用户 ID”位!
在命令行中,设置一个可执行文件的“设置用户 ID”位的最简单的方法,就是用
chmod +s /path/to/file

这个命令。
一旦用了这个命令之后,再执行这个文件,
那么生成的进程的 effective user ID 就变成了这个可执行文件的 owner user ID(属主用户 ID),
而 real user ID 仍然是启动这个程序时所用的用户的 ID。
打个比方来说,如果有这样的一个文件:
引用:-rw sr- sr-x 1 susesuse susesuse 7902 2006-08-31 13:22 tuid

注意这个文件已经用 chmod +s 命令设置过“设置用户 ID”位了。
然后我用 flw 这个用户来执行它,那么生成的进程它的 real user ID 就是 flw(因为我是用 flw 运行的),但是 effective user ID 就变成了 susesuse(因为这个可执行文件被设置了“设置用户 ID”位,并且它的 owner user ID 是 susesuse)。
这时,这个进程实际上就有两个用户权限了。只不过目前生效的是 susesuse,因此它目前能够且只能够操作 susesuse 用户的文件,如果现在我又想要操作 flw 用户的文件怎么办?
很简单,只需要 seteuid( getuid() ) 就可以了。执行完这句之后,effective user ID 就变成和 real user ID 一样了,都变成 flw 了。

可是如果过了一会儿我又想要变回来怎么办?因为 effective user ID 和 real user ID 此时都变成了 flw 了,所以操作系统必须得有一个地方保存住原来的“设置用户 ID”(也就是可执行文件的 owner user ID),不然等你再想要 seteuid 的时候,操作系统就不知道你有没有那个权利了。(总不能再去访问一次文件系统吧?那样也太没有效率了)

操作系统为了能够在设置了 seteuid 之后,再次设置回来,所以特地将原来的“设置用户 ID”保存下来了,这个保存下来的设置用户 ID 自然就叫做“保存的设置用户 ID”。

下面看一段我写的例子程序:
flw@Sleeper:~$ whoami

flw

flw@Sleeper:~$ cat tuid.c

# include <stdio.h>

# include <sys/types.h>

# include <pwd.h>



int main( void )

{

    struct passwd *pwd;



    pwd = getpwuid( geteuid() );

    printf( "effective UID: [%s]\n", pwd->pw_name );



    system( "touch /tmp/foo.txt; ls -l /tmp/foo.txt; rm -rf /tmp/foo.txt" );



    printf( "\nset EUID to `flw'..\n" );

    pwd = getpwnam( "flw" );

    seteuid(pwd->pw_uid);



    pwd = getpwuid( geteuid() );

    printf( "effective UID: [%s]\n", pwd->pw_name );



    system( "touch /tmp/foo.txt; ls -l /tmp/foo.txt; rm -rf /tmp/foo.txt" );



    printf( "\nset EUID to `root'..\n" );

    seteuid(0);



    pwd = getpwuid( geteuid() );

    printf( "effective UID: [%s]\n", pwd->pw_name );



    system( "touch /tmp/foo.txt; ls -l /tmp/foo.txt; rm -rf /tmp/foo.txt" );



    return 0;

}

flw@Sleeper:~$ su root -c "cc -o tuid tuid.c; chmod +s tuid; ls -al tuid"

Password:

-rwsr-sr-x 1 root root 7902 2006-08-31 13:55 tuid

flw@Sleeper:~$ ./tuid

effective UID: [root]

-rw-r--r-- 1 root root 0 2006-08-31 13:55 /tmp/foo.txt



set EUID to `flw'..

effective UID: [flw]

-rw-r--r-- 1 flw root 0 2006-08-31 13:55 /tmp/foo.txt



set EUID to `root'..

effective UID: [root]

-rw-r--r-- 1 root root 0 2006-08-31 13:55 /tmp/foo.txt

flw@Sleeper:~$

你可能感兴趣的:(unix,OS)