文件访问权限:更改用户ID

本文来探讨一下通过更改用户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,只有自己才能读写。该文件的访问权限如下所示:

[email protected]:~$ ls -l test.txt
-rw------- 1 Jim Jim 13 Sep 13 17:07 /home/Jim/test.txt

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

[email protected]:~$ cat /home/Jim/test.txt
cat: /home/Jim/test.txt: Permission denied

[email protected]:~$ 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文件。代码如下:

  1 #include <stdio.h>
  2 #include <unistd.h>
  3
  4 #define BUF_LEN (256)
  5
  6 int main(void)
  7 {
  8     FILE * fp = NULL;
  9     char buf[BUF_LEN] = {0};
 10
 11     uid_t real_uid, effect_uid, saved_uid;
 12
 13     if (getresuid(&real_uid, &effect_uid, &saved_uid) == -1) {
 14         perror("getresuid");
 15         return -1;
 16     }
 17
 18     printf("===== before setuid =====\n");
 19
 20     printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid);
 21
 22     /*
 23      * the first important thing this program does is reduce its privilege by using setuid function.
 24      * otherwise we can not protect our data file.
 25      */
 26
 27     // we reduce this program‘s privilege to real_uid.
 28     if (setuid(real_uid) == -1) {
 29         perror("setuid");
 30         return -1;
 31     }
 32
 33     printf("====== after setuid with real_uid =====\n");
 34
 35     if (getresuid(&real_uid, &effect_uid, &saved_uid) == -1) {
 36         perror("getresuid");
 37         return -1;
 38     }
 39
 40     printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid);
 41
 42     fp = fopen("/home/Jim/test.txt", "r");
 43     if (fp == NULL) {
 44         printf("\n##### error #####\n");
 45         perror("fopen");
 46         printf("##### error #####\n\n");
 47     }
 48
 49     // now we raise this program‘s privilege to saved_uid.
 50     if (setuid(saved_uid) == -1) {
 51         perror("setuid");
 52         return -1;
 53     }
 54
 55     printf("====== after setuid with saved_uid =====\n");
 56
 57     if (getresuid(&real_uid, &effect_uid, &saved_uid) == -1) {
 58         perror("getresuid");
 59         return -1;
 60     }
 61
 62     printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid);
 63
 64     fp = fopen("/home/Jim/test.txt", "r");
 65     if (fp == NULL) {
 66         printf("\n##### error #####\n");
 67         perror("fopen");
 68         printf("##### error #####\n\n");
 69
 70         return -1;
 71     }
 72
 73     printf("\n----- file read -----\n");
 74     while (fgets(buf, BUF_LEN, fp))
 75         printf("%s", buf);
 76
 77     if (feof(fp))
 78         printf("\nfile reach end!\n");
 79     else
 80         ferror(fp);
 81     printf("----- file close -----\n\n");
 82
 83     fclose(fp);
 84
 85     // reduce this program‘s privilege by setting effect_uid to saved_uid
 86     if (seteuid(saved_uid) == -1) {
 87         perror("seteuid");
 88         return -1;
 89     }
 90
 91     printf("====== after seteuid with saved_uid =====\n");
 92
 93     if (getresuid(&real_uid, &effect_uid, &saved_uid) == -1) {
 94         perror("getresuid");
 95         return -1;
 96     }
 97
 98     printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid);
 99
100     fp = fopen("/home/Jim/test.txt", "r");
101     if (fp == NULL) {
102         printf("\n##### error #####\n");
103         perror("fopen");
104         printf("##### error #####\n\n");
105     } else {
106         printf("file opened!\n\n");
107         fclose(fp);
108     }
109
110     // reduce this program‘s privilege by setting effect_uid to real_uid
111     if (seteuid(real_uid) == -1) {
112         perror("seteuid");
113         return -1;
114     }
115
116     printf("====== after seteuid with real_uid =====\n");
117
118     if (getresuid(&real_uid, &effect_uid, &saved_uid) == -1) {
119         perror("getresuid");
120         return -1;
121     }
122
123     printf("real_uid = %d, effect_uid = %d, saved_uid = %d\n\n", real_uid, effect_uid, saved_uid);
124
125     fp = fopen("/home/Jim/test.txt", "r");
126     if (fp == NULL) {
127         printf("\n##### error #####\n");
128         perror("fopen");
129         printf("##### error #####\n\n");
130     } else {
131         printf("file opened!\n\n");
132         fclose(fp);
133     }
134
135     return 0;
136 }

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

[email protected]:~$ gcc uid_test.c -o uid_test -D _GNU_SOURCE
[email protected]:~$ chmod u+s uid_test

[email protected]:~$ 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文件。

[email protected]:~$ /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 #####

[email protected]:~$

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

另外注意

setuid函数与seteuid函数的区别:

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

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

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

时间: 2024-08-26 05:11:43

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

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

lienhua342014-09-10 1 文件访问权限 在文件访问权限和进程访问控制中,我们已经讲述过文件访问权限位,为了方便,我们重新列在下面, 表 1: 文件的 9 个访问权限位  st_mode 屏蔽  意义  S_IRUSR  用户 -读  S_IWUSR  用户 -写  S_IXUSR  用户 -执行  S_IRGRP   组 -读  S_IWGRP  组 -写  S_IXGRP  组 -执行  S_IROTH  其他 -读  S_IWOTH  其他 -写  S_IXOTH  其他

更改用户id 和组id

转自 http://blog.csdn.net/todd911/article/details/16370577 在unix系统中,特权是基于用户和组ID的,当程序需要增加特权,或需要访问当前并不允许访问的资源时,我们需要更换自己 用户ID或组ID,使的新ID具有合适的特权或访问权限.与此类似,当程序需要降低其特权或阻止对某些资源的访问时,也需要 更换用户ID或组ID,从而使新ID不具有相应特权或访问这些资源的能力. 可以用setuid函数设置实际用户ID和有效ID,与此类似,可以使用setgi

UNIX环境编程学习笔记(7)——文件I/O之文件访问权限与进程访问控制

lienhua342014-09-02 1 文件的设置用户 ID位 和设置组 ID位 与进程相关联的 ID 如下表所示, 表 1: 与进程相关联的用户 ID 和组 ID 实际用户 ID 我们实际上是谁 实际组 ID 有效用户 ID 用于文件访问权限检查 有效组 ID 附加组 ID 保存的设置用户 ID 由 exec 函数保存 保存的设置组 ID 保存的设置用户 ID 和保存的设置组 ID 在执行一个程序时包含了有效用户 ID 和有效组 ID 的副本,这个后面我们学习到进程时在详细学习. 此处,我

android进阶之文件访问权限

android是基于linux的,所以文件访问权限和linux一样.其中r代表可读,W代表可写,X代表可执行. 文件访问权限分三组. 1-3 当前用户(应用)访问权限. 4-6 当前用户所在组的访问权限. 7-9 其他用户(应用)访问权限. android应用是一个独立的用户,对应一个独立的组. 可以通过如下指令来查看用户组和用户ID: 强制修改文件访问权限: - --- --- --- (第一个-代表目录,后面是三组的权限- 代表0) - rw- --- --- (代表当前用户可读可写)(对应

Android文件访问权限

文件访问权限 Android的文件访问权限控制和linux一样. 文件访问权限:谁能访问这个文件.使用drwxrwxrwx这是个字符控制 第一个字母 d:表示文件夹 -:表示文件 后面每三个字母分为一组 第一组: rwx 是文件拥有者的权限 r:read,读 w:write,写 x:execute,执行 -:表示没有该权限 第二组:rwx表示的是和文件拥有者同属于一组的用户对文件拥有的权限 r:read,读 w:write,写 x:execute,执行 -:表示没有该权限 第三组:rwx表示其他

[Z] Linux下进程的文件访问权限

原文链接:http://blog.csdn.net/chosen0ne/article/details/10581883 对进程校验文件访问权限包括两个部分,一是确定进程的角色(属于哪个用户或者组),二是确定对应的角色是否具有该操作的权限. 首先看第一部分.默认情况下,进程的有效角色就是当前执行这个进程的用户及其所在组.但是,文件具有设置用户ID位和设置组ID位(stat.st_mode的S_ISUID和S_ISGID),用于改变这种默认的行为,将进程的有效角色设置为文件所属的用户和组.这主要用

Linux的文件访问权限及修改权限命令chmod

Linux的文件访问权限及修改权限命令chmod Mxx000 Mxx000 11 人赞同了该文章 Linux的文件访问权限可以使用ls -l进行查看,如下图这样操作就可以了. 一.访问权限 访问权限分为读(read).写(write).执行(execute)三种, 并且涉及到文件所有者(user).文件所属组(group).其他人(other)三个主体. 选取上图红框中的两行为例,结合下面的表格先讲一下基本结构: 第一位表示文件类型 第二-十位表示权限:每三位一组,共三组,分别表示三个主体对应

Android基础--文件访问权限

1.Android 底层是Linux内核,因此文件访问权限与Linux中文件访问权限类似 d   rwx   rwx   rwx 文件类型 owner group other 文件类型   d 代表文件夹,-代表文件,l 代表链接 owner文件创建的用户 group 与文件创建者在同一组的其他用户 other 与文件创建者不在同一组的其他用户 Android中每一个应用都对应独立的用户,不同应用所在组是不同的,可以通过设置是两个应用在同一个组中   2.以下是在当前应用的私有空间内创建文件时指

Linux之文件访问权限管理

概述:在linux中的每一个文件或目录都包含有访问权限,这些访问权限决定了谁能访问和如何访问这些文件和目录. 有以下三种访问方式限制访问权限: 只允许用户自己访问: 允许一个预先指定的用户组中的用户访问: 允许系统中的任何用户访问. 同时,用户能够控制一个给定的文件或目录的访问程度.一个文件活目录可能有读(r).写(w)及执行权限(x).当创建一个文件时,系统会自动地赋予文件所有者读和写的权限,这样可以允许所有者能够显示文件内容和修改文件. 三种不同的用户类型访问一个目录或者文件:所有者(own