一、进程的文件访问权限。
一个程序在其执行过程可能要访问其他文件。当其进程要访问相关文件,例如调用open、read、write等函数时,系统内核会检查用户对文件的访问权。
每个进程都有一个进程属性数据结构记录着进程的相关信息。下表中的进程信息记录项是内核检查进程对文件访问权限的主要依据。
进程信息 | 信息意义 |
实际用户ID | 标识是此用户在执行本进程 |
实际组ID | 标识是此用户组在执行本进程 |
有效用户ID | 当进程要访问文件时,以此用户ID作为要访问文件的用户 |
有效组ID | 当进程要访问文件时,以此用户组ID作为要访问文件的用户组 |
当进程要调用相关函数访问文件时,内核会以上表相关ID与要访问的文件(不是此进程的可执行文件)相关访问权限进行对比检查,以决定文件访问是否能够进行。
一般情况下,程序执行时,会将实际用户ID设置为有效用户ID,将实际组ID设置为有效组ID。但也有例外的情况。这样例外就是下文要叙述的S_ISUID位和S_IGID位的相关操作。
二、设置用户ID位和设置组ID位。
在文件模式字(st_mode)中,有两个位(bit10和bit11)分别称为设置用户组ID位和设置用户ID位,分别有两个测试常量S_ISGID和S_ISUID与其对应。若此文件为可执行文件:
(1)当设置用户组ID位为1,则文件执行时,内核将其进程的有效用户组ID设置为文件的所有组ID。
(2)当设置用户ID位为1,则文件执行时,内核将其进程的有效用户ID设置为文件所有者的用户ID。
为简化表述,下文用S_ISGID位表示设置用户组ID位,用S_ISUID位表示设置用户ID位。
三、常量S_ISGID和S_ISUID的定义
常量S_ISGID和S_ISUID定义在文件<bits/stat.h>或者<sys/stat.h>中。
1、<bits/stat.h>
/* Protection bits. */
#define __S_ISUID 04000 /* Set user ID on execution. */
#define __S_ISGID 02000 /* Set group ID on execution. */
#define __S_ISVTX 01000 /* Save swapped text after use (sticky). */
2、 <sys/stat.h>
/* Protection bits. */
#define S_ISUID __S_ISUID /* Set user ID on execution. */
#define S_ISGID __S_ISGID /* Set group ID on execution. */
#if defined __USE_BSD || defined __USE_MISC || defined __USE_XOPEN
/* Save swapped text after use (sticky bit). This is pretty well obsolete. */
# define S_ISVTX __S_ISVTX
#endif
四、S_ISGID位和S_ISUID位的操作
1、S_ISGID位和S_ISUID位的判断:
通过文件模式字st_mode与S_ISGID或者S_ISUID进行“按位与”运算可以判断对应位的状态,即:
st_mode & S_ISGID:取得S_ISGID位的状态。
st_mode & S_ISUID:取得S_ISUID位的状态。
2、S_ISGID位和S_ISUID位的置位:
通过文件模式字st_mode与S_ISGID或者S_ISUID进行“按位或”运算可以置位对应位,即:
st_mode | S_ISGID:置位S_ISGID位。
st_mode | S_ISUID:置位S_ISUID位。
3、S_ISGID位和S_ISUID位的清除:
通过文件模式字st_mode与S_ISGID或者S_ISUID反码进行“按位与”运算可以清除对应位,即:
st_mode & (~S_ISGID):清除S_ISGID位。
st_mode & (~S_ISUID):清除S_ISUID位。
以上对S_ISGID位和S_ISUID位的操作只是更改了内存中st_mode这个数据结构的值,它并不会更改文件在磁盘中的文件属性信息,要保存到磁盘,还必须调用chmod或者fchmod函数。
实例 x.4.4.1.c
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void set_suid(struct stat *buf);
void clr_suid(struct stat *buf);
void set_sgid(struct stat *buf);
void clr_sgid(struct stat *buf);
void shw_suid(struct stat *buf);
void shw_sgid(struct stat *buf);
int main(int argc, char *argv[])
{
struct stat buf;
/*argv[1] 为命令参数:
*-su:S_ISUID位置位
*-cu:S_ISUID位清除
*-sg:S_ISGID位置位
*-cg:S_ISUID位清除
*
* argv[2]为文件路径
*/
if (argc < 3) { /*判断命令是否合法*/
printf("usage:./a.out -su filepath\n");
exit(1);
}
if (lstat(argv[2], &buf) == -1) { /*获取文件属性结构*/
printf("lstat error for %s\n",argv[2]);
exit(1);
}
/*根据命令参数执行命令*/
if (strcmp(argv[1], "-su") == 0)
set_suid(&buf);
else if (strcmp(argv[1], "-cu") == 0)
clr_suid(&buf);
else if (strcmp(argv[1], "-sg") == 0)
set_sgid(&buf);
else if (strcmp(argv[1], "-cg") == 0)
clr_sgid(&buf);
else
printf("unknown arg for %s\n", argv[2]);
printf("\n");
exit(0);
}
/*S_ISUID位置位*/
void set_suid(struct stat *buf)
{
buf->st_mode = buf->st_mode | S_ISUID;
shw_suid(buf);
}
/*S_ISGID位置位*/
void set_sgid(struct stat *buf)
{
buf->st_mode = buf->st_mode | S_ISGID;
shw_sgid(buf);
}
/*S_ISUID位清除*/
void clr_suid(struct stat *buf)
{
buf->st_mode = buf->st_mode | (~S_ISUID);
shw_suid(buf);
}
/*S_ISGID位清除*/
void clr_sgid(struct stat *buf)
{
buf->st_mode = buf->st_mode | (~S_ISGID);
shw_sgid(buf);
}
/*打印S_ISUID位状态*/
void shw_suid(struct stat *buf)
{
if (buf->st_mode & S_ISUID)
printf("user id is 1");
else
printf("user id is 0");
}
/*打印S_ISGID位状态*/
void shw_sgid(struct stat *buf)
{
if (buf->st_mode & S_ISGID)
printf("group id is 1");
else
printf("group id is 0");
}
编译与执行:
[root@localhost unixc]# echo 'This is my tempfile!' > /tmp/myfile
[root@localhost unixc]# cc x.4.4.1.c
[root@localhost unixc]# ./a.out -su /tmp/myfile #设置用户ID位置位
user id is 1
[root@localhost unixc]# ./a.out -cu /tmp/myfile #设置用户ID位清除
user id is 0
[root@localhost unixc]# ./a.out -sg /tmp/myfile #设置用户组ID位置位
group id is 1
[root@localhost unixc]# ./a.out -cg /tmp/myfile #设置用户组ID位清除
group id is 1
[root@localhost unixc]#