为什么在使用 capset 后 setuid 失败?

why setuid fails after capset is used?

试图找出 linux 功能界面,我遇到了一个意想不到的问题(至少对我而言)。当使用 capset 系统调用设置进程的功能时,内核拒绝使用 setuid 系统调用更改用户标识。有人知道为什么 setuid 失败吗?

这是我为测试此行为而编写的代码:

#undef _POSIX_SOURCE  
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/capability.h>
#include <sys/capability.h>
#include <string.h>

int main(int argc, char** argv){
    struct __user_cap_header_struct cap_header;
    struct __user_cap_data_struct cap_data;
    int cap_res;
    FILE *file;
    int sockfd;
    
    cap_header.pid = getpid();
    cap_header.version = _LINUX_CAPABILITY_VERSION_1;
    
    __u32 cap_mask = 0;
    cap_mask |= (1 << CAP_DAC_OVERRIDE);
    cap_mask |= (1 << CAP_SETUID);

    printf("You selected mask: %x\n", cap_mask);
    
    cap_data.effective = cap_mask;    
    cap_data.permitted = cap_mask;
    cap_data.inheritable = cap_mask;

    cap_res = capset(&cap_header, &cap_data);
    if(cap_res < 0){
        printf("Trying to apply mask: FAIL\n", cap_mask);
    } else {
        printf("Capability set correctly\n");
    }

    int uid = atol(argv[1]);
    int setuid_res = setuid(uid);
    if (setuid_res == -1){
        printf("7w7\n");
    } else {
        printf("UID set correctly\n");
    }
}

编译:

$ gcc -g test1.c -o test1

输出为(用户 ID:1000)

$ # ./test1 1000
You selected mask: 2
Capability set correctly
7w7

我认为您的问题中可能遗漏了几个步骤:

  1. 如何给二进制文件一些权限?
  2. 您似乎在尝试使用 cap_dac_override 来实现 cap_setuid 的目的。

重写程序如下:

#undef _POSIX_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/capability.h>
#include <sys/capability.h>
#include <string.h>

int main(int argc, char** argv){
    struct __user_cap_header_struct cap_header;
    struct __user_cap_data_struct cap_data;
    int cap_res;

    // need to start from known data. C does not guarantee these are
    // zero filled by default. You could declare them static to get
    // that.
    memset(&cap_header, 0, sizeof(cap_header));
    memset(&cap_data, 0, sizeof(cap_data));

    cap_header.pid = getpid();
    cap_header.version = _LINUX_CAPABILITY_VERSION_1;

    __u32 cap_mask = 0;
    cap_mask |= (1 << CAP_SETUID);

    printf("You selected mask: %x\n", cap_mask);

    cap_data.effective = cap_mask;
    cap_data.permitted = cap_mask;
    // not needed: cap_data.inheritable = cap_mask;

    cap_res = capset(&cap_header, &cap_data);
    if(cap_res < 0){
        printf("Trying to apply mask: FAIL\n", cap_mask);
        exit(1);
    } else {
        printf("Capability set correctly\n");
    }

    if (argc != 2) {
        printf("usage: %s <uid>\n", argv[0]);
        exit(1);
    }
    int uid = atol(argv[1]);
    int setuid_res = setuid(uid);
    if (setuid_res == -1){
        printf("7w7\n");
    } else {
        printf("UID set correctly to %d\n", uid);
    }
}

你可以运行这样的程序:

$ sudo ./test1 1000
You selected mask: 80
Capability set correctly
UID set correctly to 1000

或者,使用文件功能:

$ sudo setcap cap_setuid=p ./test1
$ ./test1 1000
You selected mask: 80
Capability set correctly
UID set correctly to 1000

如果您想使用前 32 项功能,这将起作用。但是,目前在 Linux 下有大约 40 个,所以我建议您考虑使用 libcap API 代替,它会为您计算出所有内核 ABI 详细信息。