Linux文件权限:setuid、setgid和sticky bit

Search for a command to run...

No comments yet. Be the first to comment.
双向链表是一种比单向链表更为灵活的数据结构,与单向链表相比可以有更多的应用场景,本文讨论双向链表的基本概念及实现方法,并着重介绍使用GLib的GList实现单向链表的方法及步骤,本文给出了多个实际范例源代码,旨在帮助学习基于GLib编程的读者较快地掌握GList的使用方法,本文程序在 ubuntu 20.04 下编译测试完成,gcc 版本号 9.4.0;本文适合初学者阅读。 1 双向链表及其实现 在文章《单向链表以及如何使用GLib中的GSList实现单向链表》中,介绍了单向链表以及基于 G...

C 编程框架是开发人员必不可少的工具,编程框架可以为构建强大且性能优异的应用程序提供结构化的基础,本文将对 8 个最佳 C 编程框架和库做出简要的介绍,如果您正在寻找适合初学者的 C 编程框架或旨在进行 C 编程框架比较,相信本文可以给您一定的帮助。 顶级 C 编程框架 – 概述 本文将介绍以下 8 个 C 语言编程框架: 序号框架名称主要特点易于集成下载链接 1GTK全面的小部件集,跨平台支持中等的下载 2Qt跨平台支持,集成开发环境中等的下载 3CMocka轻量级,模...

单向链表是一种基础的数据结构,也是一种简单而灵活的数据结构,本文讨论单向链表的基本概念及实现方法,并着重介绍使用GLib的GSList实现单向链表的方法及步骤,本文给出了多个实际范例源代码,旨在帮助学习基于GLib编程的读者较快地掌握GSList的使用方法,本文程序在 ubuntu 20.04 下编译测试完成,gcc 版本号 9.4.0;本文适合初学者阅读。 1 单向链表及其实现 在文章《使用GLib进行C语言编程的实例》中,简单介绍了 GLib,建议阅读本文前先阅读这篇文章; 单向链表是一...

本文将讨论使用GLib进行编程的基本步骤,GLib是一个跨平台的,用C语言编写的3个底层库(以前是5个)的集合,GLib提供了多种高级的数据结构,如内存块、双向和单向链表、哈希表等,GLib还实现了线程相关的函数、多线程编程以及相关的工具,例如原始变量访问、互斥锁、异步队列等,GLib主要由GNOME开发;本文是使用GLib编程的入门文章,旨在通过实例帮助希望学习GLib编程的读者较快地入门,本文将给出多个使用GLib库编程范例的源代码,本文程序在 ubuntu 20.04 下编译测试完成,gc...

打开电脑连接wifi是一件很平常的事情,但这些事情通常都是操作系统下的wifi管理程序替我们完成的,如何在程序中扫描wifi信号其实资料并不多,前面已经有两篇文章介绍了如何使用ioctl()扫描wifi信号,但其实在Linux下有一个简单的库对这些ioctl()的操作进行了封装,这个库就是libiw,使用libiw可以简化编程,本文介绍了如果使用libiw对wifi信号进行扫描的基本方法,本文将给出完整的源代码,本文程序在 ubuntu 20.04 下编译测试完成,gcc 版本号 9.4.0;尽...

Linux 下的文件权限管理分为三组:拥有者、组、其它用户,文件权限分为读、写、执行,但其实并不仅仅如此,还有 setuid、setgid、sticky bit 这一组标志,本文通过一个可执行文件的权限 4755 展开介绍 setuid、setgid 和 sticky bit 的概念,希望本文对读者理解 Linux 文件权限管理能有所帮助。
前不久在折腾 openwrt 时,需要给 openwrt 装上 sudo(openwrt 默认是不安装 sudo 的),安装很成功,用起来也没什么问题,但是重启了以后就不能用了,后来发现原因之一是这个 openwrt 在启动的时候将 /usr/bin/ 目录下的所有文件的权限(permission)都改为了 0755,这是一个正常的可执行文件的权限(permission),但是对 sudo 这个可执行文件却是不行的,sudo 的文件权限(permission)必须是 4755;
解决这个问题倒是不难,在 openwrt 启动时,执行一下 chmod 4755 /usr/bin/sudo 就行了;
这件事让我想写这篇文章,因为我们看惯了诸如 0755、0750等可执行文件属性,这个 4755 的文件属性并不多见。
4755 的文件属性在文件列表时的样子
ls -al /usr/bin/sudo

sudo 这个文件有什么与众不同的特点
有哪些文件的属性是 4755
ls -al /usr/bin|grep "^-rws"
在我的 ubuntu 下会显示出这些文件


sudo find / -perm 4755 -type f
我们可以从 /etc/passwd 文件中看到一个用户的 uid 和 gid,比如我当前的用户是 whowin,我可以这样看到这个用户的 uid 和 gid

root 用户的 uid = 0,gid = 0,这个可是不能乱改的;

当一个用户执行一个程序时,需要创建一个进程,Linux 会赋予这个进程一定的权限,当然是赋予执行这个程序的用户所拥有的权限,比如我用 whowin 这个用户运行 touch file.tmp,则 touch 进程获得的是 whowin 的权限,whowin 有在当前目录下创建文件的权限,这个命令创建了一个新文件 file.tmp 该文件的所有人(owner)是 whowin;

同样是 whowin 用户,当执行 cat /etc/shadow 时,权限就不够了,前面说过 /etc/shadow 的权限是 640,只有 root 才能读写该文件,Linux 把用户 whowin 的权限赋予 cat 进程,whowin 没有权限读取 /etc/shadow,所以 cat 进程无法继续执行;

以上例子只是要说明,正常情况下 Linux 会把当前用户的权限赋予其创建的进程,如果这个用户没有足够的权限,则其创建的进程也没有足够的权限;
文件权限的确是用八进制数表示的,权限分为三组,分别是:拥有者、组和其它用户,每组的读、写、执行权限用一个八进制数表示(三位二进制数);

下面我们用一个实例来说明文件权限为 4755 时的不同
首先我们编一个打开文件的小程序 open_file,并建立一个只有 root 用户可以读取的文件 rootfile.txt
程序 open_file.c 的源代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv) {
int fd = 0;
if (argc != 2) {
printf("Usage: %s [filename]\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
if (fd == -1) {
fprintf(stderr, "%s\n", strerror(errno));
return -1;
}
close(fd);
printf("Open file %s successfully.\n", argv[1]);
return 0;
}
gcc open_file.c -o open_file
建立一个只有 root 可以读的文件 rootfile.txt
echo "This file's owner is root.">rootfile.txt
chmod 600 rootfile.txt
sudo chown root:root rootfile.txt
ls -l rootfile.txt
cat rootfile.txt

用程序 open_file 打开 rootfile.txt
./open_file rootfile.txt

将程序 open_file 的所有者改为 root 并再次尝试打开文件 rootfile.txt
sudo chown root:root open_file
ls -l open_file
./open_file rootfile.txt

将 open_file 的权限改为 4755 并再次尝试打开文件 rootfile.txt
sudo chmod 4755 open_file
ls -l open_file
./open_file rootfile.txt

setuid
setuid 是文件权限的一个标志位,当这个标志位置为 1 时,Linux 在执行这个文件时将会把这个文件的所有者的权限赋予这个程序的进程,而不是像普通可执行文件那样将执行该文件的用户的权限赋予这个程序的进程;
换句话说,当一个文件设置了 setuid 标志后,用户执行这个文件时,在这个程序的进程中 uid 为用户的 uid,euid 为这个文件拥有者的 uid;
chmod u+s [文件名]
chmod 4755 [文件名]
用 ls -l 显示这个文件时,当该文件的拥有者有可执行权限时,x 将被改为 s;否则,- 将被改为 S;我们用文件 rootfile.txt 看一下
ls -l rootfile.txt
sudo chmod u+s rootfile.txt
ls -l rootfile.txt
sudo chmod u-s rootfile.txt
ls -l rootfile.txt

前面提到过 sudo 的例子,正是 setuid 的作用使得普通用户可以通过执行 sudo 进行提权;另一个例子是 passwd 命令,普通用户可以通过这个命令修改自己的密码,但这个文件是要读写 /etc/shadow 文件的,而只有 root 才有这个权限,所以执行 passwd 是需要 root 权限的,普通用户可以正常使用它也是由于这个文件设置了 setuid 标志位;
setgid
理解了 setuid 之后 setgid 就比较容易理解了,setgid 也是文件权限的一个标志位,当设置该标志位后,用户执行该文件时,将把该文件所在的组的权限赋予这个程序进程的组权限,而不是使用当前用户的组权限去执行该程序;
换句话说,当一个文件设置了 setgid 标志后,用户执行这个文件时,在这个程序的进程中 gid 为用户的 gid,egid 为这个文件拥有者的 gid;
chmod g+s [文件名]
chmod 2755 [文件名]
sticky bit
sticky bit 是一个比较特殊的东西,这个标志只有用于目录时才有效,当一个目录被设置 sticky bit 后,在这个目录下的所有文件只有文件的拥有者和 root 可以删除,不管这个文件的权限是什么(哪怕是 0777 的权限),所以 sticky bit 又可以被称为 “限制删除标志”
chmod +t [目录名]
chmod 1777 [目录名]
当一个目录被设置 sticky bit 后,在其他用户权限中的可执行位将被改为 t

setuid、setgid 和 sticky bit 组成了文件权限的另一个八进制数

这三个标志位又凑够了一个八进制数,和文件权限的 3 个八进制合在一起组成了 4 个八进制数,文章开头提到的 sudo 文件权限 4755 中的 4 就是这几个标志位组成的八进制数;
理论上,这三个标志是可以同时设置的,但这样设置未必有什么意义,我们可以用上面曾用到的 sticky 目录试一下

尽管我们设置成功了,但是可以想一下 setuid 和 setgid 在这个目录上的意义何在;
清除标志的命令,前面的例子中已经出现过
chmod u-s [文件名]
chmod g-s [文件名]
chmod -t [目录名]
chmod 0755 并不一定能清除标志,我们还以 sticky 目录为例

chmod 0755 仅清除了 sticky bit;chmod 00755 清除了所有三个标志
程序 get_euid.c 的源代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
uid_t uid;
uid_t euid;
uid = getuid();
euid = geteuid();
printf("uid = %d\neuid = %d\n", uid, euid);
return 0;
}
gcc get_euid.c -o get_euid
将该程序的拥有者变成 root(不设置 setuid),运行一下看看 uid 和 euid 都是什么

将该程序设置 setuid,再运行一下看看 uid 和 euid 都是什么

欢迎访问我的博客:https://whowin.cn
email: hengch@163.com
