Unix/Linux编程:文件权限、目录权限

文件权限

普通文件的权限

在这里插入图片描述
stat 结构中 st_mod 字段的低 12 位定义了文件权限。

  • 其中的前 3 位为专用位,分别是 set-user-ID 位、set-group-ID 位和 sticky 位
  • 其余 9 位则构成了定义权限的掩码,分别授予访问文件的各类用户。文件权限掩码分为 3 类。
    • Owner(亦称为 user):授予文件属主的权限 (chmod(1)之类的命令使用术语 user 的缩写 u 来指代该类权限。)
    • Group:授予文件属组成员用户的权限。
    • Other:授予其他用户的权限
  • 可为每一类用户授予的权限如下所示。
    • Read:可阅读文件的内容。
    • Write:可更改文件的内容。
    • Execute:可以执行文件(亦即,文件是程序或脚本)。要执行脚本文件,需同时具备读权限和执行权限

执行 ls–l 命令,可查看文件的权限和所有权,如下所示:
在这里插入图片描述

  • 在以上输出中,将文件权限显示为“rwxr-x—”(该字符串起始处的连接号“-”表明该文件属于普通文件)
  • 在解释该字符串时,需将其一剖为三,以 3 个字符为一组,分别表示读、写、可执行权限具备与否。
  • 第一组字符用来表示文件属主的权限,在本例中,则是读、写、执行权限俱全。
  • 第二组字符用来表示属组权限,对于本例,组内用户具有读和可执行权限,但不具有写权限
  • 最后一组字符用来表示其他用户的权限,本例中的其他用户没有任何权限。

头文件< sys/stat.h>定义了可与 stat 结构中 st_mode 相与(&)的常量,用于检查特定权限
位置位与否。(<fcntl.h>为 open()系统调用提供了原型,在程序中包含该头文件也可定义这些
常量。)下表列出了这些常量:

常量其他值权限位
S_ISUID04000Set-user-ID
S_ISGID02000Set-group-ID
S_ISVTX01000Sticky
S_IRUSR0400User-read
S_IWUSR0200User-write
S_IXUSR0100User-execute
S_IRGRP040Group-read
S_IWGRP020Group-write
S_IXGRP010Group-execute
S_IROTH04Other-read
S_IWOTH02Other-write
S_IXOTH01Other-execute

除了上面常量外,还分别将各类(属主、属组及其他)权限掩码定义为常量:S_IRWXU (0700)、S_IRWXG (070)和 S_IRWXO (07)

下面程序:

  • 会针对给定的文件权限掩码返回一个静态分配的字符串,以 ls(1)所采用的风格来表示该掩码。
  • 如果在 filePermStr()的 flag 参数中设置了 FP_SPECIAL 标志,那么返回的字符串将包括set-user-ID、set-group-ID,以及 sticky 位的设置信息,其表现形式同样会沿袭 ls(1)的风格。
// 将文件权限掩码转换为字符串
char *          /* Return ls(1)-style string for file permissions mask */
filePermStr(mode_t perm, int flags)
{
    static char str[STR_SIZE];

    /* If FP_SPECIAL was specified, we emulate the trickery of ls(1) in
       returning set-user-ID, set-group-ID, and sticky bit information in
       the user/group/other execute fields. This is made more complex by
       the fact that the case of the character displayed for this bits
       depends on whether the corresponding execute bit is on or off. */

    snprintf(str, STR_SIZE, "%c%c%c%c%c%c%c%c%c",
             (perm & S_IRUSR) ? 'r' : '-', (perm & S_IWUSR) ? 'w' : '-',
             (perm & S_IXUSR) ?
             (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
             (((perm & S_ISUID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
             (perm & S_IRGRP) ? 'r' : '-', (perm & S_IWGRP) ? 'w' : '-',
             (perm & S_IXGRP) ?
             (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 's' : 'x') :
             (((perm & S_ISGID) && (flags & FP_SPECIAL)) ? 'S' : '-'),
             (perm & S_IROTH) ? 'r' : '-', (perm & S_IWOTH) ? 'w' : '-',
             (perm & S_IXOTH) ?
             (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 't' : 'x') :
             (((perm & S_ISVTX) && (flags & FP_SPECIAL)) ? 'T' : '-'));

    return str;
}

目录权限

目录与文件拥有相同的权限方案,只是对 3 种权限的含义另有所指。

  • 读权限:可列出(比如,通过 ls 命令)目录之下的内容(即目录下的文件名)。
  • 写权限:可在目录内创建、删除文件。注意,要删除文件,对文件本身无需有任何权限。
  • 可执行权限:可访问目录中的文件。因此,有时也将对目录的执行权限称为 search(搜索)权限

访问文件时,需要拥有对路径名所列所有目录的执行权限。例如,想读取文件/home/mtk/x,则需拥有对目录/、/home 以及/home/mtk 的执行权限(还要有对文件 x 自身的读权限)。若当前的工作目录为 /home/mtk/sub1 ,访问相对路径名 …/sub2/x 时,需握有 /home/mtk 和/home/mtk/sub2 这两个目录的可执行权限(不必有对/或/home 的执行权限)。

拥有对目录的读权限,用户只是能查看目录中的文件列表。要想访问目录内文件的内容或是这些文件的 i 节点信息,还需握有对目录的执行权限

反之,若拥有对目录的可执行权限,而无读权限,只要知道目录内文件的名称,仍可对其进行访问,但不能列出目录下的内容(即目录所含的其他文件名)。在控制对公共目录内容的访问时,这是一种常用技术,简单而且实用。

要想在目录中添加或删除文件,需要同时拥有对该目录的执行和写权限。

权限检测算法

  • 只要在访问文件或者目录的系统调用中指定了路径名称,内核就会去检查相应文件的权限。
  • 如果赋予系统调用的路径名还包含了目录前缀,那么内核除了会检查文件所具有的权限外,还会检查前缀所含每个目录的可执行权限。
  • 内核会使用进程的有效用户ID、有效组ID以及辅助组ID,来执行权限检查。(准确说来,Linux 内核会使用文件系统用户 ID 和组 ID,而非相应的有效用户 ID 和组 ID,来进行文件权限检查)

一旦调用 open()打开了文件,针对返回描述符的后续系统调用(比如,read()、write()、fstat()、fcntl(),以及 mmap())将不再进行任何权限检查。

针对文件检查时,内核所遵循的原则如下:

  • 对于特权级进程,授予其所有访问权限
  • 如果进程的有效用户ID和文件的UID(属主)相同,内核会根据文件的属主权限,授予进程相应的访问权限。比方说,若文件权限掩码中的属主读权限(owner-read permission)位被置位,则授予进程读权限。否则,则拒绝进程对文件的读取操作。
  • 若进程的有效组 ID 或任一附属组 ID 与文件的组 ID(属组)相匹配,内核会根据文件的属组权限,授予进程对文件的相应访问权限
  • 若以上三点皆不满足,内核会根据文件的 other(其他)权限,授予进程相应权限

其实,内核代码在实现上述检查规则时,在构造上也颇具匠心。只有当进程通过其他
测试未能获得所需要的权限时,才去检查进程是否属于特权级进程。这就省去了对 ASU 进程记账标志的设置,该标志用于标记进程是否曾利用过超级用户特权

内核会依次执行针对属主、属组以及其他用户的权限检查,只要匹配上述检查规则之一,便会停止检查。

检查对文件的访问权限:access()

  • 当进程访问文件时,系统会以其 effective(有效)用户 ID、effective(有效)组ID 以及附属组 ID 来确定权限。
  • 当然,对于程序(比如,set-user-ID 或 set-group-ID 程序)来说,根据进程的 real(真实)用户 ID 和组 ID 来检查对文件的访问权限,也并非没有可能。

系统调用 access()就是根据进程的真实用户 ID 和组 ID(以及附属组 ID),去检查对pathname 参数所指定文件的访问权限

		#include <unistd.h>

       int access(const char *path, int amode);

  • 若 pathname 为符号链接,access()将对其解引用。
  • 参数 mode 是由下表常量相或(|)而成的位掩码。
常量描述
F_OK有这个文件吗
R_OK对该文件有读权限吗
W_OK对该文件有写权限吗
X_OK对该文件有执行权限吗
  • 若由 pathname 所指定的文件具备mode 参数包含的所有权限,access()将返回 0;只要有一项权限未得到满足(或者有错误发生),access()则返回−1

由于对某一文件调用 access()与对同一文件的后续操作之间存在时间差,因此(不论间隔多么短暂)执行后续操作时,也无法保证在对文件的后续操作时由 access()所返回的信息依然正确。在某些应用程序设计中,上述情形可能会导致安全漏洞。因此,建议杜绝使用 access()

GNU C 库提供了一个功能相似的非标准函数 euidaccess()(及其同义函数 eaccess()),该函数使用进程的有效用户 ID 来检查对文件的访问权限。

Set-User-ID、Set-Group-ID 和 Sticky 位

除了 9 位用来表明属主、属组和其他用户的权限之外,文件权限掩码还另设有 3 个附加位,分别为 set-user-ID (bit 04000)、set-group-ID (bit 02000)和 sticky (bit 01000)位。

set-user-ID

它的出现是为了解决一个重要的问题,即用户如何更改自己的密码?

看起来,好像很容易,用passwd命令就可以

接下来研究一下passwd的工作原理,先来看/etc/passwd这个文件,注意这个文件的所有者和文件访问权限设置:

$ ls -l /etc/passwd
-rw-r--r--. 1 root root 2974 325 15:36 /etc/passwd

更新密码会导致上述内容的变化,但是普通用户没有修改这个文件的权限,只有root用户才可以修改它,那么为什么普通用户没有/etc/passwd的写权限,可以修改用户密码?

这是因为,passwd命令有一个特殊的权限,使得passwd命令的文件的所有者是root,而且它的特殊属性中包含了set-user-ID位

此时, 文件的所有者是root,而且设置了该文件的设置用户ID位,然后当该文件由另一个进程执行时,该进程有了root 的权限。 如/bin/passwd ,该程序是一个设置用户ID程序,普通用户使用passwd 命令更改登录口令时,shell会调用/bin/passwd ,此时shell具有root权限,所以可以修改 /etc/passwd 文件来更改用户登录口令

检查文件是否设置了SUID是使用S_ISUID

Set-Group-ID

Set-Group-ID可以给程序的某一个组的访问权限。

检查文件是否设置了SUID是使用S_ISGID

Sticky

  • 在老的Unix实现中,提供sticky的目的在于让常用程序的执行速度更快。若对某程序文件设置了 sticky 位,则首次执行程序时,系统会将其文本拷贝保存于交换区中,即“粘”(stick)在交换区内,故而能提高后续执行的加载速度
  • 在现代 UNIX 实现(包括 Linux)中,sticky 权限位所起的作用全然不同于老的 UNIX 实现。sticky位使得目录里的文件只能被创建者删除

可通过 chmod 命令(chmod +t file)或 chmod()系统调用来设置文件的 sticky 权限位。若对某文件设置了 sticky 权限位,则当执行 ls–l 命令显示该文件时,会在其他用户执行权限字段上看到字母 T,其大小写则要取决于是否对文件开启了其他用户执行权限位,如下所示:
在这里插入图片描述

进程的文件模式创建掩码:umask()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值