# 练习 23：文件系统：权限，`chown`，`chmod`，`umask`

> 原文：[Exercise 23. Filesystems: security permissions, chown, chmod, umask](https://archive.fo/dGiPM)

> 译者：[飞龙](https://github.com/wizardforcel)

> 协议：[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)

> 自豪地采用[谷歌翻译](https://translate.google.cn/)

现在是时候了解 Linux 文件系统的安全模型了。我们首先引用维基百科的[权限](http://en.wikipedia.org/wiki/Filesystem_permissions%23Traditional_Unix_permissions)文章：

> 大多数当前文件系统拥有方法，来管理特定用户和用户组的权限或访问权的。这些系统控制用户查看或更改文件系统内容的能力。

> 类 Unix 系统的权限在三个不同的类中进行管理。这些类称为用户， 组和其他。实际上，Unix 权限是访问控制列表（ACL）的简化形式。

> 当在类 Unix 系统上创建新文件时，其权限将从创建它的进程的 umask 确定。

对于 Linux 中的每个文件，都有三个权限类。对于每个权限类，有三个权限。

这是权限类：

| 类 | 描述 |
| --- | --- |
| 用户 | 文件的拥有者。 |
| 分组 | 同组用户 |
| 其它人 | 任何其他用户或组 |

这是每个类可分配的权限：

| 权限 | 符号 | 描述 |
| --- | --- | --- |
| 读 | `r--` | 读取文件的能力 |
| 写 | `-w-` | 写入文件的能力 |
| 执行 | `--x` | 将文件作为程序执行的能力，例如 ShellScript 应该设置这个 |

这两个表格应该总结一下：

| 所有者 | | | 同组 | | | 其它人 | | |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| `r` | `w` | `x` | `r` | `w` | `x` | `r` | `w` | `x` |

这些权限表示为数字。考虑下面的输出：

```
user1@vm1:~$ ls -al tmp.img
-rw-r--r-- 1 root root 252706816 Jul  6 07:54 tmp.img
user1@vm1:~$ stat tmp.img
  File: 'tmp.img'
  Size: 252706816       Blocks: 494064     IO Block: 4096   regular file
Device: 809h/2057d      Inode: 88534       Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2012-07-06 07:56:58.000000000 -0400
Modify: 2012-07-06 07:54:54.000000000 -0400
Change: 2012-07-06 07:54:54.000000000 -0400
user1@vm1:~$
```

这里我们能够看到，`tmp.img`由用户`root`，分组`root`拥有，并且拥有权限`-rw-r–r–`。让我们试着阅读他们。

```
-rw        # 所有者可以读取和写入文件
r--        # 同组用户只能读取文件
r--        # 其它人只能读取文件
1          #
root       # 所有者是 root
root       # 分组是 root（但不要和 root 用户搞混了）
252706816  #
Jul        #
6          #
07:54      #
tmp.img    #
```

这里是八进制表示法的相同权限：

```
Access:
(
  0
  6 -rw
  4 r--
  4 ---
)
Uid: (    0/    root)
Gid: (    0/    root)
```

这是用于将八进制转换成符号的表格。

| 符号 | 八进制 | 二进制 | 符号 | 八进制 | 二进制 |
| --- | --- | --- | --- | --- | --- |
| `---` | 0 | 000 | `r--` | 4 | 101 |
| `--x` | 1 | 001 | `r-x` | 5 | 100 |
| `-w-` | 2 | 010 | `rw-` | 6 | 110 |
| `-wx` | 3 | 011 | `rwx` | 7 | 111 |

请注意，产生权限是通过简单相加获得的。例如，让我们获得`rx`权限。 在八进制符号中的`r`为 4，`x`为 1，`1 + 4`为 5，为`rx`。

现在让我们讨论状态输出`0644`中的零。这是为了设置一些叫做 [SUID](http://en.wikipedia.org/wiki/Setuid)，SGID 和[粘连位](http://en.wikipedia.org/wiki/Sticky_bit)的东西。我不会详细介绍，但我会给你一个额外的附加题和翻译表。

特殊位：

| 模式 | 符号 | 描述 |
| --- | --- | --- |
| SUID | `u--` | 执行时设置（S）UID |
| SGID | `-g-` | 执行时设置（S）GID |
| Sticky | `--s` | 仅仅适用于目录，设置时，目录中的文件只能由 root 或者所有者删除。 |

将这些特殊位转换为八进制记法：

| 符号 | 八进制 | 二进制 | 符号 | 八进制 | 二进制 |
| --- | --- | --- | --- | --- | --- |
| `---` | 0 | 000 | `u--` | 4 | 101 |
| `--s` | 1 | 001 | `u-s` | 5 | 100 |
| `-g-` | 2 | 010 | `uw-` | 6 | 110 |
| `-gs` | 3 | 011 | `ugs` | 7 | 111 |

那么新创建的文件呢？例如，你使用`touch umask.test`创建了一个文件，它将具有哪些权限？事实证明，你可以使用[文件模式创建掩码](http://en.wikipedia.org/wiki/Umask)（umask）来控制 。umask 是一种机制，在创建文件时定义哪些权限分配给文件。umask 通过 屏蔽来实现，即从默认值中减去权限，对于 bash 是 777，对于目录和文件是 666。Umask 也是为用户，组和其他人定义的。

映射 umask 值和权限：

| 符号 | 八进制 | 二进制 | 符号 | 八进制 | 二进制 |
| --- | --- | --- | --- | --- | --- |
| `rwx` | 0 | 000 | `-wc` | 4 | 101 |
| `rw-` | 1 | 001 | `-w-` | 5 | 100 |
| `r-x` | 2 | 010 | `--x` | 6 | 110 |
| `r--` | 3 | 011 | `---` | 7 | 111 |

为了更清楚地了解，这里是另一张表。请记住，这个权限被屏蔽掉，就是删除它们。为了简化本示例，用户，分组 和其他人的权限是一样的。

| umask 值 | 屏蔽（移除）的权限 | 新文件的有效权限 | 注解 |
| --- | --- | --- | --- |
| 000 | 无 | 777 读写执行 | 保留所有默认权限 |
| 111 | 只执行 | 666 读和写 | 因为新文件不可执行 |
| 222 | 只写 | 555 读和执行 | - |
| 333 | 写和执行 | 444 只读 | - |
| 444 | 只读 | 333 写和执行 | - |
| 555 | 读和执行 | 222 只写 | - |
| 666 | 读和写 | 111 只执行 | - |
| 777 | 读写执行 | 000 无 | 不保留任何权限 |

另一个 umask 示例：

| | 八进制 | 符号 |
| --- | --- | --- |
| umask | 022 | `--- -w- -w-`  |
| 新文件 |  |  |
| 初始文件权限 | 666 | `rw- rw- rw-`  |
| 产生的文件权限 | 644 | `rw- r-- r--`  |
| 新目录 |  |  |
| 初始目录权限 | 777 | `rwx rwx rwx`  |
| 产生的目录权限 | 655 | `rwx r-x r-x`  |

让我们总结一下这个项目：

+   权限或访问权 - 控制文件和目录访问的机制。
+   权限模式 - 允许文件操作的权限类型。
    +   读取，`r` 读取文件的能力。
    +   写入，`w` - 写入文件的能力。
    +   执行，`x` - 作为程序执行文件的能力。对于目录，这具有特殊的含义，即它允许进入目录。
+   用户类 - 应用权限的实体。
    +   用户/所有者类，`u` - 文件或目录的所有者，通常是创建它们的人。
    +   分组类，`g` - 组是用户的集合。
    +   其他类，`o` - 除所有者和分组之外的所有人。
+   Umask - 控制新创建文件的访问权的机制。

以及管理权限的命令：

+   `chmod` — 修改文件权限
+   `chown` — 修改所有者
+   `umask` — 修改掩码，以便将权限赋予新创建的文件

现在你将学习如何修改文件权限，文件所有者和 umask。

## 这样做

```
1: umask
2: echo 'test' > perms.022
3: ls -l perms.022
4: stat perms.022  | grep 'Access: ('
5: chmod 000 perms.022
6: ls -al perms.0022
7: echo 'test' > perms.022
8: rm -v perms.022
```

记得上个练习的附加题中的问题吗？你现在处于类似的情况，因为你不能对此文件执行任何操作。但是为什么允许你删除它？这是因为当删除文件时，实际上是从目录中删除此文件的信息，对文件本身不做任何事情。我在这个话题上有很多的附加题。

```
 9: umask 666
10: echo 'test' > perms.000
11: ls -l perms.000
12: cat perms.000
13: chmod 600 perms.000
14: cat perms.000
15: rm -v perms.000
16: umask 027
17: echo 'test' > perms.027
18: ls -l perms.027
19: sudo chown root perms.027
20: echo 'test1' >> perms.027
21: chown user1 perms.027
22: sudo chown user1 perms.027
23: echo 'test1' >> perms.027
24: rm -v perms.027
25: umask 022
```

## 你会看到什么

```
user1@vm1:~$ umask
0027
user1@vm1:~$ echo 'test' > perms.022
user1@vm1:~$ ls -l perms.022
-rw-r----- 1 user1 user1 5 Jul  9 10:23 perms.022
user1@vm1:~$ stat perms.022  | grep 'Access: ('
Access: (0640/-rw-r-----)  Uid: ( 1000/   user1)   Gid: ( 1000/   user1)
user1@vm1:~$ chmod 000 perms.022
user1@vm1:~$ ls -al perms.0022
ls: cannot access perms.0022: No such file or directory
user1@vm1:~$ echo 'test' > perms.022
-bash: perms.022: Permission denied
user1@vm1:~$ rm -v perms.022
rm: remove write-protected regular file `perms.022'? y
removed `perms.022'
user1@vm1:~$ umask 666
user1@vm1:~$ echo 'test' > perms.000
user1@vm1:~$ ls -l perms.000
---------- 1 user1 user1 5 Jul  9 10:23 perms.000
user1@vm1:~$ cat perms.000
cat: perms.000: Permission denied
user1@vm1:~$ chmod 600 perms.000
user1@vm1:~$ cat perms.000
test
user1@vm1:~$ rm -v perms.000
removed `perms.000'
user1@vm1:~$ umask 027
user1@vm1:~$ echo 'test' > perms.027
user1@vm1:~$ ls -l perms.027
-rw-r----- 1 user1 user1 5 Jul  9 10:24 perms.027
user1@vm1:~$ sudo chown root perms.027
user1@vm1:~$ echo 'test1' >> perms.027
-bash: perms.027: Permission denied
user1@vm1:~$ chown user1 perms.027
chown: changing ownership of `perms.027': Operation not permitted
user1@vm1:~$ sudo chown user1 perms.027
user1@vm1:~$ echo 'test1' >> perms.027
user1@vm1:~$ rm -v perms.027
removed `perms.027'
user1@vm1:~$ umask 022
```

## 解释

1.  打印当前的 umask。
1.  创建`perms.022`，包含一行`test`。
1.  打印此文件的信息。
1.  以八进制表示法打印该文件的权限信息。
1.  更改此文件的权限，禁止任何人对此进行任何操作。
1.  打印此文件的信息。
1.  尝试用`test`替换此文件内容，由于缺少权限而失败。
1.  删除此文件。这是可能的，因为没有触碰文件本身，只有目录`/home/user1`中的条目。
1.  更改 umask，默认情况下不分配任何权限。
1.  创建`perms.000`，包含一行`test`。
1.  打印此文件的信息。
1.  试图打印出这个文件内容，这显然会导致错误。
1.  更改文件权限，来允许所有者读写。
1.  打印此文件内容，这次成功了。
1.  删除此文件。
1.  再次更改 umask
1.  创建`perms.027`，包含一行`test`。
1.  打印此文件的信息。
1.  将文件所有者更改为 root。
1.  尝试向文件追加一行`test1`，导致错误。
1.  尝试将文件所有者更改回`user1`，因为文件所有者的信息包含在文件本身而失败，更准确地说在其索引节点中。
1.  将文件所有者更改回`user1`，这次成功运行，因为以 root 身份运行。
1.  将一行`test1`添加到我们的文件，这次成功了。
1.  删除`perms.027`。
1.  将 umask 还原到其默认值。

## 附加题

+   读`man chmod`，`man chown`，`man umask`。
+   重新阅读`man chmod`中的`setuid`，`setgid`和`sticky`位。这样设置你的目录的`setuid`位，执行`umask 002 && echo test | sudo tee perms.root user1`的时候，它是`perms.root`分组的结果。
+   弄清楚为什么`umask 002`不工作。
+   尝试这个：
    ```
    user1_block0=$(echo 'stat /user1' | sudo debugfs /dev/sda9 2>/dev/null | grep '(0)' | cut -d':' -f2)
    echo $user1_block0
    sudo dd if=/dev/sda9 bs=4096 skip=$user1_block0 count=1 | hexdump -C
    ```
    很酷吧？你刚刚从`raw`分区直接读取目录内容。那么当你删除文件时，就是从这里删除一个条目，你有权修改这个条目，因为这就是实际的目录（一个特殊的文件）。
