zoukankan      html  css  js  c++  java
  • 文件访问权限:更改用户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,只有自己才能读写。该文件的访问权限如下所示:

    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文件。代码如下:

      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 =====
    ");
     19 
     20     printf("real_uid = %d, effect_uid = %d, saved_uid = %d
    
    ", 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 =====
    ");
     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
    
    ", real_uid, effect_uid, saved_uid);
     41 
     42     fp = fopen("/home/Jim/test.txt", "r");
     43     if (fp == NULL) {
     44         printf("
    ##### error #####
    ");
     45         perror("fopen");
     46         printf("##### error #####
    
    ");
     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 =====
    ");
     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
    
    ", real_uid, effect_uid, saved_uid);
     63 
     64     fp = fopen("/home/Jim/test.txt", "r");
     65     if (fp == NULL) {
     66         printf("
    ##### error #####
    ");
     67         perror("fopen");
     68         printf("##### error #####
    
    ");
     69 
     70         return -1;
     71     }
     72 
     73     printf("
    ----- file read -----
    ");
     74     while (fgets(buf, BUF_LEN, fp))
     75         printf("%s", buf);
     76 
     77     if (feof(fp))
     78         printf("
    file reach end!
    ");
     79     else
     80         ferror(fp);
     81     printf("----- file close -----
    
    ");
     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 =====
    ");
     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
    
    ", real_uid, effect_uid, saved_uid);
     99 
    100     fp = fopen("/home/Jim/test.txt", "r");
    101     if (fp == NULL) {
    102         printf("
    ##### error #####
    ");
    103         perror("fopen");
    104         printf("##### error #####
    
    ");
    105     } else {
    106         printf("file opened!
    
    ");
    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 =====
    ");
    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
    
    ", real_uid, effect_uid, saved_uid);
    124 
    125     fp = fopen("/home/Jim/test.txt", "r");
    126     if (fp == NULL) {
    127         printf("
    ##### error #####
    ");
    128         perror("fopen");
    129         printf("##### error #####
    
    ");
    130     } else {
    131         printf("file opened!
    
    ");
    132         fclose(fp);
    133     }
    134 
    135     return 0;
    136 }

    编译该程序的时候要加上_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程序的进程通常会得到额外的权限,所以编写这种程序时要特别谨慎。

  • 相关阅读:
    windows&nbsp;下搭建apache&nbsp;php&nbsp;mysql&nbsp;p…
    PHP&nbsp;VC9和VC6以及Thread&nbsp;Safe和Non…
    mysql数据库插入中文时出现ERROR&nbsp;1…
    phpmyadmin&nbsp;配置
    Linux系统安装时分区的选择【转】
    汉字 Unicode 编码范围
    Linux 程序后台运行
    添加用户username到sudo组
    nautilus
    记录一下自己的.tmux.conf,.vimrc
  • 原文地址:https://www.cnblogs.com/pyhou/p/7526756.html
Copyright © 2011-2022 走看看