Chinaunix首页 | 论坛 | 博客
  • 博客访问: 993229
  • 博文数量: 200
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 2479
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 15:07
文章分类

全部博文(200)

文章存档

2009年(12)

2008年(190)

我的朋友

分类:

2008-11-26 18:01:56

8.11 changing user ids and group ids

本文侧重讲述了real user id, effective user id的使用接口,更改以及注意事项。并提及了saved set-user-id

#include

 

int setuid(uid_t uid);

 

int setgid(gid_t gid);

 

Both return: 0 if OK, 1 on error

 

首先,一个进程有real user id,  effective use id, saved set-user-id.

Saved set-user-id是一个进程被exec启动时,从其effective user id拷贝过来的。目的是为了对effective user id做一个备份。因为你可以在以后修改effective user id,那么日后还可以利用save set-user-id将你的原始的effective user id改回来。

而一个进程可以通过函数得到的就只有real user ideffective user id

规则:

1. 只有super user进程可以更改他的real user id.

2. 任何user的进程都可以更改它的effective user id。方法:(1)如果是通过调用exec运行程序,且程序被设置了set-user-id标记,那么新程序的effective user id就是可执行程序文件的owner user id。(2)使用setuiduid),如果一个进程是super user进程,那么通过setuid调用,不但修改了effective user id,连同real user idsave set-user-id都被修改成了uid. 来设置其effective user id。如果进程是一个普通用户进程,那么调用setuid(uid)仅仅修改他的effective user iduid,不过前提是uid等于本进程的real user id或者saved set-user-id。可见effective user id也不是随便让你舍得,设置成real user id是使你获得了你本来就具有的权限,一般来说权限是降低了。将effective user id设置成saved set-user-id其实是使你仍然获得了你当初调用exec使获得的effective user id的权限。(因为saved set-user-id本来就是在执行exec时从effective user id拷贝过来的)。

下面是apue2原文:

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

1If the process has superuser privileges, the setuid function sets the real user ID, effective user ID, and saved set-user-ID to uid.

2If 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.

3If neither of these two conditions is true, errno is set to EPERM, and 1 is returned

 

 

We can make a few statements about the three user IDs that the kernel maintains.

1Only a superuser process can change the real user ID. Normally, the real user ID is set by the login(1) program when we log in and never changes. Because login is a superuser process, it sets all three user IDs when it calls setuid.

2The effective user ID is set by the exec functions only if the set-user-ID bit is set for the program file. If the set-user-ID bit is not set, the exec functions leave the effective user ID as its current value. We can call setuid at any time to set the effective user ID to either the real user ID or the saved set-user-ID. Naturally, we can't set the effective user ID to any random value.

3The saved set-user-ID is copied from the effective user ID by exec. If the file's set-user-ID bit is set, this copy is saved after exec stores the effective user ID from the file's user ID.

 

下表其实是对上述原则的一个综述:

Figure 8.18. Ways to change the three user IDs


ID

exec

setuid(uid)

 

set-user-ID bit off

set-user-ID bit on

superuser

unprivileged user

 

real user ID

unchanged

unchanged

set to uid

unchanged

 

effective user ID

unchanged

set from user ID of program file

set to uid

set to uid uid必须==real uid或者saved set user id

 

saved set-user ID

copied from effective user ID

copied from effective user ID

set to uid

unchanged

 

 

还举了一个例子,man程序,他有一个专门的用户假设是man与其对应. Man 用户可以访问的一些文件和路径。Man程序设置了set-user-id

1.我们执行man程序,当我们exec man程序后,我们:

  real user ID = our user ID
     effective user ID = 
man
      saved set-user-ID = 
man

2Man程序以effective user id = man访问了他的配置文件如/etc/man.config和一些manual资源。

3. man为我们用户执行任何命令之前,他调用了setuid(getuid()),即将自己的effective user id设置成了real user id,此时:

real user ID = our user ID (unchanged)
   effective user ID = our user ID
  saved set-user-ID = 
man (unchanged)

4 一些工作完成之后,man调用了setuid(euid),这里的euid是对应的man用户的idEuid == saved set-user-id。所以这个设置可以成功。此时:

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

 

还有2个函数,可以同时设置real uideffective user id,而且可允许普通user 更改其real user id。可以允许将real user id设置成effective user id,当然也允许将effective user id设置成real user id。这函数就是:

#include

 

   int setreuid(uid_t ruid, uid_t euid);

 

   int setregid(gid_t rgid, gid_t egid);

 

Both return: 0 if OK, 1 on error

问题:你竟然将real use id设置成了effective user id,那么如果user调用exec执行一个设置了set-user-id的且属于root的程序,那么此时real user iduser, effective user idroot 调用setreuid()后,可以将real user ideffective user id换一下,此时进程的real user id成了root 那么如果此时spawn出一个子进程,且这个子进程是我们自己写的,那么子进程 real user id都会继承为root,那么此时,我们就可以再次将real user ideffective user id互换以使我们的程序获取rooteffective user 权限,或者根本不必替换,我们的real user id root就已经是我们具有了很高的权限了。这就是不安全的了。为了避免这样的问题,apue中有如下解释:

Be aware, however, that when programs that used this feature spawned a shell, they had to set the real user ID to the normal user ID before the exec. If they didn't do this, the real user ID could be privileged (from the swap done by setreuid) and the shell process could call setreuid to swap the two and assume the permissions of the more privileged user. As a defensive programming measure to solve this problem, programs set both the real user ID and the effective user ID to the normal user ID before the call to exec in the child

即,在你通过调用setreuid提升了real user id的权限后,一旦要spawn出一个子进程,那么在你调用exec之前,这个子进程的real user id甚至effective user id就都应该被设置成普通用户的id。而不是权限提升之后的用户的id. 但是我做的测试结果表明,系统不会自动这么做:spawn出来的进程在执行了exec后,得到新进程的real user id依然是权限提升之后的用户id .

如下为我的测试,及其结果:

程序一: 调用总程序,他的ownerroot,被设置了set-user-id,他会展示setreuid的效果,并在切换了real user id effective user id之后fork出一个子进程然后exec一个新程序,并检查新程序的real user ideffective user id

#include

#include

#include

#include

#include

 

int main()

{

       uid_t uid = getuid();

       uid_t eid = geteuid();

       printf( "uid = %d, euid = %d\n", uid, eid );

       if( setreuid( eid, uid )<0 )

              printf("error occurs\n");

       else

              printf("success\n");

       uid = getuid();

       eid = geteuid();

       printf( "uid = %d, euid = %d\n", uid, eid );

 

       fflush(stdout);

       pid_t pid = fork();

       if( pid<0 )

              puts( "fork error occurs\n" );

       else if( pid == 0 )

       {

              //child

              puts("this is child");

              uid_t uid = getuid();

              uid_t eid = geteuid();

              printf( "child uid = %d, euid = %d\n", uid, eid );

              fflush(stdout);

              execl( "./tmpexe", (char*)0 );

              exit(0);

 

       }

       puts("this is father");

       waitpid(pid,0,0);

       fflush(stdout);

       return 0;

}

程序二: exec的程序

#include

#include

#include

 

int main()

{

       uid_t uid = getuid();

       uid_t eid = geteuid();

       printf(" executed app uid=%d, eid=%d\n" , uid, eid );

       return 0;

}

 

下面是命令流程:

shaoting@desktopbj-LabSD:/home/shaoting/mytest> g++ tmpexec.cpp -o tmpexe

shaoting@desktopbj-LabSD:/home/shaoting/mytest> g++ setreuid.cpp

shaoting@desktopbj-LabSD:/home/shaoting/mytest> su

Password:

desktopbj-LabSD:/home/shaoting/mytest # chown root a.out

desktopbj-LabSD:/home/shaoting/mytest # chmod u+s a.out

desktopbj-LabSD:/home/shaoting/mytest # exit

exit

shaoting@desktopbj-LabSD:/home/shaoting/mytest> ./a.out

uid = 1003, euid = 0

success

uid = 0, euid = 1003

this is child

child uid = 0, euid = 1003

this is father

 executed app uid=0, eid=1003

 shaoting@desktopbj-LabSD:/home/shaoting/mytest>         

可见,子进程里exec后,其权限依然是real user id root

 

Linuxman 2 setreuid里,有一句话:

Linux: Unprivileged users may only set the real user ID to the real user ID or  the  effective  user ID.

Linux: If the real user ID is set 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.

可见,real user id就算设置也顶多设置成与effective user id相同。

Effective user id如果被设置成不等于real user id的另外的值且也不等于effective user id的话,saved set-user-id随之也会被更改。

 

阅读(736) | 评论(0) | 转发(0) |
0

上一篇:8.9 race condition

下一篇:8.12 interpreter files

给主人留下些什么吧!~~