UNIX再学习 -- 用户 ID 和组 ID
用户 ID和组 ID 的内容已经在好几章中出现过了。之前都没有讲到,现在放到一起总结。
一、用户 ID 和 组 ID 回顾
1、我们在APUE 第 4、6、8 章,都有涉及到。
其中我们用到的地方:
(1)修改文件权限
参看:UNIX再学习 -- 文件和目录
chgrp命令 功能:改变文件或目录的属组。
chown 命令 功能:更改某个文件或目录的属主和属组。
# ls -l
总用量 16
-rwxr-xr-x 1 root root 7308 Apr 27 11:28 a.out
-rw-r--r-- 1 root root 422 Apr 27 11:28 test.c
第一个 root 为属主,第二个 root 为属组。当然这里面没有用户 ID,和 组 ID
参看:unix实际用户ID和有效用户ID解析
参看:维基百科 -- chmod 命令
下面解析一下格式所表示的意思。这种表示方法一共有十位:
9 8 7 6 5 4 3 2 1 0
- r w x r - x r - x
第9位表示文件类型,可以为p、d、l、s、c、b和-:
p表示命名管道文件
d表示目录文件
l表示符号连接文件
-表示普通文件
s表示socket文件
c表示字符设备文件
b表示块设备文件
第8-6位、5-3位、2-0位分别表示文件所有者的权限,同组用户的权限,其他用户的权限,其形式为rwx:
r表示可读,可以读出文件的内容
w表示可写,可以修改文件的内容
x表示可执行,可运行这个程序
没有权限的位置用-表示
如果一个文件被设置了 SUID 或 SGID 位,会分别表现在所有者或同组用户的权限的可执行位上。例如:
-rwsr-xr-x 表示SUID和所有者权限中可执行位被设置
-rwSr--r-- 表示SUID被设置,但所有者权限中可执行位没有被设置
-rwxr-sr-x 表示SGID和同组用户权限中可执行位被设置
-rw-r-Sr-- 表示SGID被设置,但同组用户权限中可执行位没有被设置
其实在UNIX的实现中,文件权限用12个二进制位表示,如果该位置上的值是
表示有相应的权限:
11 10 9 8 7 6 5 4 3 2 1 0
S G T r w x r w x r w x
第11位为SUID位,第10位为SGID位,第9位为sticky位,第8-0位对应于上面的三组rwx位。
上面的-rwsr-xr-x的值为: 1 0 0 1 1 1 1 0 1 1 0 1
-rw-r-Sr--的值为: 0 1 0 1 1 0 1 0 0 1 0 0
(2)/etc/passwd
# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
tarena:x:1000:1000:tarena,,,:/home/tarena:/bin/bash
其中 超级用户 root 的用户 ID 为 0,组 ID 也为 0 。而我所用的 普通用户 tarena 用户 ID 为 1000,组 ID 为 1000 。
(3)函数 stat
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
(4)口令文件相关函数
参看:UNIX再学习 -- 系统数据文件和信息
函数 getpwent、setpwent 和 endpwent
(5)ps 指令部分
USER 用户名
# ps lax
F UID PID PPID PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND
4 0 1 0 20 0 3612 2064 poll_s Ss ? 0:01 /sbin/init
1 0 2 0 20 0 0 0 kthrea S ? 0:00 [kthreadd]
1 0 3 2 20 0 0 0 run_ks S ? 0:01 [ksoftirqd/0]
5 0 5 2 20 0 0 0 worker S ? 0:01 [kworker/u:0]
二、获取调用进程用户 ID 和 组 ID
1、获取调用进程的实际用户 ID 和实际组 ID
#include <unistd.h>
#include <sys/types.h>
uid_t getuid(void);
gid_t getgid(void);
分别返回调用进程的实际用户ID 和实际组 ID
(1)示例说明
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main (void)
{printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());return 0;
}
输出结果:
uid = 0
gid = 0我用的是超级用户 root 用户ID为 0,组 ID 为 0
一个进程的用户和组身份决定了它可以访问哪些资源,比如读、写或者执行某个文件。但真正被用于权限验证的并不是进程的实际用户 ID和实际组 ID,而是其有效用户ID 和有效组 ID。一般情况下,进程的有效用户 ID和有效组 ID 就取自其实际用户 ID 和实际组 ID,二者是等价的。
2、获取调用进程的有效用户 ID 和有效组 ID
#include <unistd.h>
#include <sys/types.h>
uid_t geteuid(void);
gid_t getegid(void);
分别返回调用进程的有效用户ID 和有效组 ID
(1)示例说明
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main (void)
{printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0;
}
输出结果:
euid = 0
egid = 0
三、设置用户 ID 位和设置组 ID 位
1、什么是设置用户 ID 位 和设置组 ID 位
# ls -la test
-rwsrwsr-x 1 root root 7314 Apr 27 16:10 test
sudo chmod u+s test 设置SUID位
sudo chmod g+s test 设置SGID位
sudo chmod u-s test 去掉SUID设置
sudo chmod g-s test 去掉SGID设置
如果该位为4, 则表示设置 setuid
如果该位为2, 则表示设置 setgid
所以,假设文件或目录的原来权限位设置是777,要加上SUID/SGID,如下设置:
chmod 4777 test 设置 SUID 位
chmod 2777 test 设置 SGID 位
chmod 0777 test 去掉 SUID 设置
chmod 0777 test 去掉 SGID 设置
S_ISUID 0004000 set UID bit
S_ISGID 0002000 set-group-ID bit (see below)
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h> int main (void)
{ int res = chmod ("a.txt", S_ISUID | S_ISGID | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (-1 == res) perror ("fail to chmod"), exit (1); execlp ("stat", "stat", "a.txt", NULL);return 0;
}
输出结果:文件:"a.txt"大小:0 块:0 IO 块:4096 普通空文件
设备:801h/2049d Inode:2107549 硬链接:1
权限:(6644/-rwSr-Sr--) Uid:( 0/ root) Gid:( 0/ root)
最近访问:2017-04-27 18:35:15.952010539 +0800
最近更改:2017-04-27 18:35:15.952010539 +0800
最近改动:2017-04-28 09:26:11.755554531 +0800
创建时间:-
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h> int main (void)
{ int res = chmod ("a.txt", ~S_ISUID & ~S_ISGID & S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (-1 == res) perror ("fail to chmod"), exit (1); execlp ("stat", "stat", "a.txt", NULL);return 0;
}
输出结果:文件:"a.txt"大小:0 块:0 IO 块:4096 普通空文件
设备:801h/2049d Inode:2107549 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root)
最近访问:2017-04-27 18:35:15.952010539 +0800
最近更改:2017-04-27 18:35:15.952010539 +0800
最近改动:2017-04-28 09:27:11.747554284 +0800
创建时间:-
# find . -perm -4000 -type f
./a.txt
# stat /etc/passwd文件:"/etc/passwd"大小:1918 块:8 IO 块:4096 普通文件
设备:801h/2049d Inode:1076219 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root)
最近访问:2017-04-28 09:36:00.775552859 +0800
最近更改:2016-12-01 10:28:54.801942085 +0800
最近改动:2016-12-01 10:28:54.809942031 +0800
创建时间:-
我们可以看到 passwd 文件只有对于 root 用户是可写的,而对于所有的他用户来说都是没有写权限的。 那么一个普通的用户如何能够通过运行 passwd 命令修改这个 passwd 文件呢?为了解决这个问题,SUID/SGID 便应运而生。
# ls -la test
-rwxr-xr-x 1 root root 7238 Apr 27 15:35 test
而此时我注销root用户,使用隶属于 tarena 组 (GID = 1000)和tarena 用户(UID = 1000)登录系统,运行 test 程序。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main (void)
{printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0;
}
输出结果:
uid = 1000
gid = 1000
euid = 1000
egid = 1000
可以看到进程的有效用户 ID 和实际用户 ID 一样都是 1000,而有效组 ID 也和实际组 ID一样都是 1000。
设置权限
sudo chmod u+s test
sudo chmod g+s test查看 test 权限
$ ls -la test
-rwsrwsr-x 1 root root 7314 4月 27 16:10 test
使用隶属于 tarena 组和 tarena用户再次运行 test 程序
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main (void)
{printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0;
}
输出结果:
uid = 1000
gid = 1000
euid = 0
egid = 0
不难发现,进程的实际用户 ID 和实际组 ID 并没有发生变化,仍然是 1000,但它的有效用户 ID 和有效组 ID 却变成了 0,显然这是 test 文件的拥有者 root 用户 ID 和 组 ID,而参与权限判断,决定该进程能做什么不能做什么的恰恰是它的有效用户 ID 和有效组 ID,tarena 用户扮演 root 用户行使权限。
四、实际用户 ID和有效用户 ID 区别
(2)有效用户ID和有效用户组ID
-g或--group 显示用户所属群组的ID。
-G或--groups 显示用户所属附加群组的ID。
-n或--name 显示用户,所属群组或附加群组的名称。
-r或--real 显示实际ID。
-u或--user 显示用户ID。
-help 显示帮助。
-version 显示版本信息。
示例:
//超级用户 root 登录
# id
uid=0(root) gid=0(root) 组=0(root)
//普通用户 tarena 登录
$ id
uid=1000(tarena) gid=1000(tarena) 组=1000(tarena),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare)
五、更改用户 ID 和更改组 ID
1、我们可以用 setuid 函数设置实际用户 ID 和有效用户 ID。可以用 setgid 函数设置实际组 ID 和有效组 ID。
#include <sys/types.h>
#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
两个函数返回值,若成功,返回 0,;若出错,返回-1
(1)函数解析
关于谁能更改 ID 有若干规则。现在先考虑更改用户 ID 的规则(关于用户 ID 我们所说明的一切都适用于组 ID)。
(2)示例说明
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>int main (void)
{printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());if(!setgid(1234))printf("setgid successfully!\n");elseperror ("setgid"), exit (1);if(!setuid(1234))printf("setuid successfully!\n");elseperror ("setuid"), exit (1);printf ("--------------------\n");printf ("uid = %d\n", getuid ());printf ("gid = %d\n", getgid ());printf ("euid = %d\n", geteuid ());printf ("egid = %d\n", getegid ());return 0;
}
输出结果:
uid = 0
gid = 0
euid = 0
egid = 0
setgid successfully!
setuid successfully!
--------------------
uid = 1234
gid = 1234
euid = 1234
egid = 1234
(3)示例解析
2、函数 seteuid 和 setegit
#include <unistd.h>
int seteuid(uid_t euid);
int setegid(gid_t egid);
两个函数返回值,若成功,返回 0,失败返回 -1
(1)函数解析
(2)示例说明
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h> int main (void)
{ printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); if(!setegid(1234)) printf("setegid successfully!\n"); else perror ("setegid"), exit (1); if(!seteuid(1234)) printf("seteuid successfully!\n"); else perror ("seteuid"), exit (1); printf ("--------------------\n"); printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); return 0;
}
输出结果:
uid = 0
gid = 0
euid = 0
egid = 0
setegid successfully!
seteuid successfully!
--------------------
uid = 0
gid = 0
euid = 1234
egid = 1234
3、函数 setreuid 和 setregid
#include <sys/types.h>
#include <unistd.h>
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
两个函数返回值,若成功,返回 0;若出错,返回 -1
(1)函数解析
(2)示例说明
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h> int main (void)
{ printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); if(!setregid(0, 1234)) printf("setregid successfully!\n"); else perror ("setregid"), exit (1); if(!setreuid(0, 1234)) printf("setreuid successfully!\n"); else perror ("setreuid"), exit (1); printf ("--------------------\n"); printf ("uid = %d\n", getuid ()); printf ("gid = %d\n", getgid ()); printf ("euid = %d\n", geteuid ()); printf ("egid = %d\n", getegid ()); return 0;
}
输出结果:
uid = 0
gid = 0
euid = 0
egid = 0
setregid successfully!
setreuid successfully!
--------------------
uid = 0
gid = 0
euid = 1234
egid = 1234
4、设置不同用户 ID 的各个函数关系
六、APUE 第 8 章未讲部分
第八章是我总结 APUE 以来讲的时间最长的一章,各种小知识点,各种不熟悉,未讲部分一大串,甚是无语。
UNIX再学习 -- 用户 ID 和组 ID相关推荐
- 8.11 更改用户ID和组ID
8.11 更改用户ID和组ID 在UNIX系统中,特权是基于用户和组ID的,当程序需要增加特权,或需要访问当前并不允许访问的资源时,我们就需要更换自己的用户ID或组ID. 一般而言,在设计应用程序的时 ...
- UNIX再学习 -- 守护进程(转)
参看:守护进程 一.什么是守护进程 守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程.它是一个生存期较长的进程,通常独立于控制 ...
- UNIX再学习 -- 线程
终于要讲到线程部分,线程和进程让人够头痛的内容. 一.线程概念 老样子,我们还是按我们讲进程时的方式说起,参看:UNIX再学习 -- 进程环境 首先需要了解下,什么是线程. Linux 下的线程,可能 ...
- UNIX再学习 -- 进程关系
APUE 第 10 章信号讲完,回过头来看一下第 9 章的进程关系.终端登录和网络登录部分,我们只讲 Linux 系统的. 一.终端登录 我记得我们讲 root 登录设置时有提到,参看:C语言再学习 ...
- UNIX再学习 -- 信号
终于讲到信号部分,很多比较重要的应用程序都需处理信号.第 9 章需要先了解信号机制再看,所以先跳过不讲.现在开始详解信号. 一.信号概念 信号是提供异步事件处理机制的软件中断. 这些异步事件可能来自硬 ...
- UNIX再学习 -- exit 和 wait 系列函数
我们一开始讲进程环境时,就有提到了.进程有 8 种方式使进程终止. 其中 5 种为正常终止,它们是: (1)在 main 函数中执行 return (2)调用 exit 函数,并不处理文件描述符,多进 ...
- UNIX再学习 -- 系统数据文件和信息
UNIX 系统的正常运作需要使用大量与系统有关的数据文件,例如,口令文件 /etc/passwd 和组文件 /ect/group 就是经常被多个程序频繁使用的两个文件.用户每次登陆 UNIX 系统,以 ...
- UNIX再学习 -- 文件和目录
文件I/O部分断断续续写了三天,最后总结发现还有好多内容是略过没讲的,我的内心是崩溃的.UNIX环境高级编程这本书,虽然我只看了四章我就发现了书里面的内容讲的太跳,如果是刚接触UNIX或者没有一点C语 ...
- UNIX再学习 -- shell编程
UNIX环境高级编程看了三章,遇到不少重定向等shell命令.本想到Linux时再讲,看来有必要提前了.之前有看过一本<嵌入式Linux软硬件开发详解>这本书里有简单介绍了一部分shell ...
最新文章
- 谈谈Linux下的数据流重定向和管道命令
- 打印折痕方向(二叉树应用)
- 数据结构中缀表达式转后缀表达式与后缀表达式的求值实训报告_动图+源码,演示 Java 中常用数据结构执行过程及原理...
- 复制出来的文本都是大写_vi或vim怎么复制粘贴
- Redis的编译安装
- 计算机故障检修课过时,第三场公开课|电脑故障维修以及笔记本知识科普
- graphics | R语言的基础绘图系统(二)—— 绘图参数及par函数
- @程序员,如何用最少的字节编写 C64 可执行文件?
- 整理收藏-mysqldump导出数据库
- 中琛物联‘连接+云+数据’服务助阵
- 23种设计模式(八)对象创建之抽象工厂
- Instgram和color,谁会更成功?
- PASCAL-VOC2012数据集介绍
- MATLAB-数据插值
- 苹果服务器文件夹共享权限设置,苹果设备如何访问 Windows 文件共享?
- Express框架、Webstorm中创建Express项目
- 各种浏览器下载tampermonkey网址
- 中国医师节丨华为IdeaHub用远程诊疗护佑人民健康,为医生减负
- 【Echarts】设置主题、扇形格式化
- 华为p30怎么升级鸿蒙系统
热门文章
- php 将查询出的数组数据存入redis
- RedHat/CentOS发行版本号及内核版本号对照表
- [leetcode] Restore IP Addresses
- hdu 1757 矩阵连乘
- Win10:tensorflow 学习笔记(1)
- 优先队列(priority_queue)的原理及用法
- copyof java_死磕 java集合之CopyOnWriteArrayList源码分析
- Gcc编译链接及常用选项总结
- 一场惊心动魄的国际黑客入侵保卫战
- [scala-spark]7. list 与 map