本文来探讨一下通过更改用户ID来获取合适的文件访问权限。由于更改组ID的规则与用户ID相同,我们在这里只探讨用户ID。

纸上得来终觉浅

先了解以下几个基本知识:

  1. 用户ID包括:实际用户ID、有效用户ID、保存的设置用户ID。其中保存的设置用户ID由exec函数保存。
  2. 实际用户ID标识我们究竟是谁,该字段在登录时取自口令文件中的登录项。通常,在一个登录会话期间该值不会改变,但root用户进程有方法改变它。
  3. 有效用户ID决定了我们的文件访问权限。
  4. 保存的设置用户ID在执行一个程序时包含了有效用户ID的副本。
  5. 通常,有效用户ID等于实际用户ID。
  6. 当执行一个程序文件时,进程的有效用户ID通常就是实际用户ID。但是可以在文件模式字(st_mode)中设置一个特殊标志,其含义是“当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)”。这个特殊标志被称为设置用户ID(set-user-ID)位。

更改这3个用户ID的不同方法总结如下:

更改3个用户ID的不同方法
ID exec setuid(uid)
设置用户ID位关闭 设置用户ID位打开 超级用户 非特权用户
实际用户ID 不变 不变 设为uid 不变
有效用户ID 不变 设置为程序文件的用户ID 设为uid 设为uid
保存的设置用户ID 从有效用户ID复制 从有效用户ID复制 设为uid 不变

关于以上几点,我们参考一下《Unix高级环境编程》一书中给出的一个例子:

Unix系统程序/usr/bin/passwd是一个设置用户ID程序,该文件的所有者是root用户,而且设置了该文件的设置用户ID位。那么当这个程序文件由一个进程执行时,该进程具有root用户权限。不管执行此文件的进程的实际用户ID是什么,都会是这样,因为该进程的权限不是由实际用户ID决定的,而是由有效用户ID决定的。系统程序/usr/bin/passwd应该能将用户的新口令写入口令文件(一般是/etc/passwd 或 /etc/shadow)中,而只有root用户才具有对该文件的写权限,所以需要使用设置用户ID功能。因为运行设置用户ID程序的进程通常会得到额外的权限,所以编写这种程序时要特别谨慎。

绝知此事要躬行

假设一台Linux主机上有2个用户:Jim(实际用户ID为1000)和Lucy(实际用户ID为1001)。

Jim在自己的主目录下有一个文件test.txt,只有自己才能读写。该文件的访问权限如下所示:

Jim@linux:~$ ls -l test.txt
-rw------- 1 Jim Jim 13 Sep 13 17:07 /home/Jim/test.txt

Lucy想查看该文件,却没有相应的权限,因此会被拒绝:

Lucy@linux:~$ cat /home/Jim/test.txt
cat: /home/Jim/test.txt: Permission denied

Lucy@linux:~$ sudo cat /home/Jim/test.txt
[sudo] password for Lucy:
hello world!

结果Lucy使用root权限强行查看了Jim的test.txt文件。这就没什么好说的了。不过,我们还是建议,在Linux上能不用root权限就不用root权限,用多了可能会伤及无辜。

我们自己的数据文件要由我们自己来保护。Jim可以编写一个设置用户ID程序,Lucy通过执行这个程序,就可以查看Jim的test.txt文件。代码如下:

 #include <stdio.h>
#include <unistd.h> #define BUF_LEN (256) int main(void)
{
FILE * fp = NULL;
char buf[BUF_LEN] = {}; uid_t real_uid, effect_uid, saved_uid; if (getresuid(&real_uid, &effect_uid, &saved_uid) == -) {
perror("getresuid");
return -;
} printf("===== before setuid =====\n"); printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid); /*
* the first important thing this program does is reduce its privilege by using setuid function.
* otherwise we can not protect our data file.
*/ // we reduce this program's privilege to real_uid.
if (setuid(real_uid) == -) {
perror("setuid");
return -;
} printf("====== after setuid with real_uid =====\n"); if (getresuid(&real_uid, &effect_uid, &saved_uid) == -) {
perror("getresuid");
return -;
} printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid); fp = fopen("/home/Jim/test.txt", "r");
if (fp == NULL) {
printf("\n##### error #####\n");
perror("fopen");
printf("##### error #####\n\n");
} // now we raise this program's privilege to saved_uid.
if (setuid(saved_uid) == -) {
perror("setuid");
return -;
} printf("====== after setuid with saved_uid =====\n"); if (getresuid(&real_uid, &effect_uid, &saved_uid) == -) {
perror("getresuid");
return -;
} printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid); fp = fopen("/home/Jim/test.txt", "r");
if (fp == NULL) {
printf("\n##### error #####\n");
perror("fopen");
printf("##### error #####\n\n"); return -;
} printf("\n----- file read -----\n");
while (fgets(buf, BUF_LEN, fp))
printf("%s", buf); if (feof(fp))
printf("\nfile reach end!\n");
else
ferror(fp);
printf("----- file close -----\n\n"); fclose(fp); // reduce this program's privilege by setting effect_uid to saved_uid
if (seteuid(saved_uid) == -) {
perror("seteuid");
return -;
} printf("====== after seteuid with saved_uid =====\n"); if (getresuid(&real_uid, &effect_uid, &saved_uid) == -) {
perror("getresuid");
return -;
} printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid); fp = fopen("/home/Jim/test.txt", "r");
if (fp == NULL) {
printf("\n##### error #####\n");
perror("fopen");
printf("##### error #####\n\n");
} else {
printf("file opened!\n\n");
fclose(fp);
} // reduce this program's privilege by setting effect_uid to real_uid
if (seteuid(real_uid) == -) {
perror("seteuid");
return -;
} printf("====== after seteuid with real_uid =====\n"); if (getresuid(&real_uid, &effect_uid, &saved_uid) == -) {
perror("getresuid");
return -;
} printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid); fp = fopen("/home/Jim/test.txt", "r");
if (fp == NULL) {
printf("\n##### error #####\n");
perror("fopen");
printf("##### error #####\n\n");
} else {
printf("file opened!\n\n");
fclose(fp);
} return ;
}

编译该程序的时候要加上_GNU_SOURCE标志(用于getresuid函数),编译完成之后还要打开该程序的设置用户ID标志位。

Jim@linux:~$ gcc uid_test.c -o uid_test -D _GNU_SOURCE
Jim@linux:~$ chmod u+s uid_test

Jim@linux:~$ ls -l uid_test
-rwsrwxr-x 1 Jim Jim 7726 Sep 14 17:19 /home/Jim/uid_test

从上面的命令输出中可以看到,uid_test程序的设置用户ID标志位已经打开。Lucy可以使用这个程序来查看Jim的test.txt文件。

Lucy@linux:~$ /home/Jim/uid_test
===== before setuid =====
real_uid = 1001, effect_uid = 1000, saved_uid = 1000

====== after setuid with real_uid =====
real_uid = 1001, effect_uid = 1001, saved_uid = 1000

##### error #####
fopen: Permission denied
##### error #####

====== after setuid with saved_uid =====
real_uid = 1001, effect_uid = 1000, saved_uid = 1000

----- file read -----
hello world!

file reach end!
----- file close -----

====== after seteuid with saved_uid =====
real_uid = 1001, effect_uid = 1000, saved_uid = 1000

file opened!

====== after seteuid with real_uid =====
real_uid = 1001, effect_uid = 1001, saved_uid = 1000

##### error #####
fopen: Permission denied
##### error #####

Lucy@linux:~$

从上面的输出中可以看出,通过改变用户ID可以控制程序的访问权限。

另外注意

setuid函数与seteuid函数的区别:

相同之处:对于一个非特权用户来说,两者都可以将进程的有效用户ID设置为其实际用户ID或其保存的设置用户ID。

不同之处:对于一个特权用户来说,setuid(uid)函数将实际用户ID、有效用户ID以及保存的设置用户ID设为uid;而seteuid(uid)函数只将进程的有效用户ID设为uid

还是那句话:因为运行设置用户ID程序的进程通常会得到额外的权限,所以编写这种程序时要特别谨慎。

文件访问权限:更改用户ID的更多相关文章

  1. 用户id,组id和文件访问权限

    实际用户ID和实际组ID:标示了我们究竟是谁,这两个字段在登录时取自口令文件中的登录项 有效用户ID和有效组ID以及附属组ID:决定了我们的文件的访问权限(通常有效用户ID等于实际用户ID,有效组ID ...

  2. UNIX环境编程学习笔记(9)——文件I/O之文件访问权限的屏蔽和更改

    lienhua342014-09-10 1 文件访问权限 在文件访问权限和进程访问控制中,我们已经讲述过文件访问权限位,为了方便,我们重新列在下面, 表 1: 文件的 9 个访问权限位  st_mod ...

  3. C#更改文件访问权限所有者(适用于各个Windows版本)

    前面也提到了,前段时间在做Online Judge系统,在正式上线前有几个比较老的版本,其中第一个版本使用ACL来控制权限以确保安全(但是这个版本完全建立在IIS上,所以这样做是没效果的),遇到了一些 ...

  4. 《UNIX环境高级编程》笔记--更改用户ID和组ID

    在unix系统中,特权是基于用户和组ID的,当程序需要增加特权,或需要访问当前并不允许访问的资源时,我们需要更换自己 用户ID或组ID,使的新ID具有合适的特权或访问权限.与此类似,当程序需要降低其特 ...

  5. 进程控制之更改用户ID和组ID

    在UNIX系统中,特权(例如能改变当前日期的表示法以及访问控制(例如,能否读.写一特定文件))是基于用户ID和组ID的.当程序需要增加特权,或需要访问当前并不允许访问的资源时,我们需要更换自己的用户I ...

  6. 更改用户id 和组id

    转自 http://blog.csdn.net/todd911/article/details/16370577 在unix系统中,特权是基于用户和组ID的,当程序需要增加特权,或需要访问当前并不允许 ...

  7. [apue] linux 文件访问权限那些事儿

    前言 说到 linux 上的文件权限,其实我们在说两个实体,一是文件,二是进程.一个进程能不能访问一个文件,其实由三部分内容决定: 文件的所有者.所在的组: 文件对所有者.组用户.其它用户设置的权限访 ...

  8. Linux下进程的文件访问权限

    本文转自 http://blog.csdn.net/chosen0ne/article/details/10581883 对进程校验文件访问权限包括两个部分,一是确定进程的角色(属于哪个用户或者组), ...

  9. 《UNIX环境高级编程》笔记--文件访问权限和新文件、目录所有权

    1.与进程关联的用户ID和组ID 与一个进程关联的ID有一下几个: 实际用户ID和实际组ID标识我们究竟是谁.通常在一个会话间值是不会改变的,但是超级用户进程有方法改变 他们,在以后的进程控制中会进行 ...

随机推荐

  1. 洛谷 P1027 【Car的旅行路线】

    题目描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第i个城市中高速铁路的单位里 ...

  2. Go 参数传递是传值还是传引用

    什么是传值(值传递)? 传值的意思是:函数传递的总是原来这个东西的一个副本.一个副拷贝.比如我们传递一个 int 类型的参数,传递 的其实这个参数的一个副本:传递一个指针类型的参数,其实传递的是这个指 ...

  3. async/await 的使用

    async : 使用 async 修饰符可将方法.lambda 表达式或匿名方法指定为异步. 如果对方法或表达式使用此修饰符,则其称为异步方法 await: await 运算符应用于异步方法中的任务, ...

  4. aop(execution()表达式)

    execution(* com.tp.soft.service..*.*(..)) 整个表达式可以分为五个部分: 1.execution(): 表达式主体. 2.第一个*号:表示返回类型,*号表示所有 ...

  5. 解决来自美国IP的攻击过程

    1.因为最近接口文档confluence服务总是自动关闭.   解决方法: 1.查看阿里云上的报警提示,看到来自外国的Ip的攻击.这时我选择把攻击的IP加入黑名单. 加入黑名单的方法:https:// ...

  6. Zookeeper初始(一)

    量大,服务器压力大.需要用到分布式,集群. 问题1:三台机器,一个请求如何落到一台机器上?如何协调工作 问题2:集群如何选取leader? 问题3:既然是分布式,集群,一个请求只能有一台机器接接收并处 ...

  7. 显示等待 (web自动化测试)

    from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By from sel ...

  8. reactiveCocoa使用注意点

    @RACSubject信号 注意点:1如果一个页面需要多次发送这个消息,那么似乎会暴露一个bug,信号不会被销毁,等到发送第二个信号 时,第一个信号仍然会被发送,导致错误,比如一个tableView的 ...

  9. MySQL中Decimal类型和Float Double的区别 & BigDecimal与Double使用场景

    MySQL中存在float,double等非标准数据类型,也有decimal这种标准数据类型. 其区别在于,float,double等非标准类型,在DB中保存的是近似值,而Decimal则以字符串的形 ...

  10. 【转载】DRuid 大数据分析之查询

    转载自http://yangyangmyself.iteye.com/blog/2321759 1.Druid 查询概述     上一节完成数据导入后,接下来讲讲Druid如何查询及统计分析导入的数据 ...