capset系统调用及示例

capget - 获取进程能力

函数介绍

capget系统调用用于获取进程的能力状态信息。能力是一种细粒度的权限控制机制,将传统的超级用户权限分解为独立的权限单元。

函数原型

1
2
3
4
5
6
#include <linux/capability.h>
#include <sys/syscall.h>
#include <unistd.h>

int capget(cap_user_header_t hdrp, cap_user_data_t datap);

功能

获取指定进程的能力集,包括有效能力、允许能力和可继承能力。

参数

  • cap_user_header_t hdrp: 指向头部结构的指针,包含版本和进程ID

  • cap_user_data_t datap: 指向能力数据结构的指针

返回值

  • 成功时返回0

  • 失败时返回-1,并设置errno

相似函数

  • capset(): 设置进程能力

  • prctl(): 进程控制函数

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/capability.h>
#include <errno.h>
#include <string.h>

// 系统调用包装
static int capget_wrapper(cap_user_header_t hdrp, cap_user_data_t datap) {
return syscall(__NR_capget, hdrp, datap);
}

// 打印能力位图
void print_capabilities(const char *label, __u32 caps) {
printf("%s: 0x%08x (", label, caps);

if (caps == 0) {
printf("none");
} else {
int first = 1;
for (int i = 0; i < 32; i++) {
if (caps & (1 << i)) {
if (!first) printf(" ");
first = 0;
switch (i) {
case 0: printf("CAP_CHOWN"); break;
case 1: printf("CAP_DAC_OVERRIDE"); break;
case 2: printf("CAP_DAC_READ_SEARCH"); break;
case 3: printf("CAP_FOWNER"); break;
case 4: printf("CAP_FSETID"); break;
case 5: printf("CAP_KILL"); break;
case 6: printf("CAP_SETGID"); break;
case 7: printf("CAP_SETUID"); break;
case 8: printf("CAP_SETPCAP"); break;
case 9: printf("CAP_LINUX_IMMUTABLE"); break;
case 10: printf("CAP_NET_BIND_SERVICE"); break;
case 11: printf("CAP_NET_BROADCAST"); break;
case 12: printf("CAP_NET_ADMIN"); break;
case 13: printf("CAP_SYS_MODULE"); break;
case 14: printf("CAP_SYS_RAWIO"); break;
case 15: printf("CAP_SYS_CHROOT"); break;
case 16: printf("CAP_SYS_PTRACE"); break;
case 17: printf("CAP_SYS_PACCT"); break;
case 18: printf("CAP_SYS_ADMIN"); break;
case 19: printf("CAP_SYS_BOOT"); break;
case 20: printf("CAP_SYS_NICE"); break;
case 21: printf("CAP_SYS_RESOURCE"); break;
case 22: printf("CAP_SYS_TIME"); break;
case 23: printf("CAP_SYS_TTY_CONFIG"); break;
case 24: printf("CAP_MKNOD"); break;
case 25: printf("CAP_LEASE"); break;
case 26: printf("CAP_AUDIT_WRITE"); break;
case 27: printf("CAP_AUDIT_CONTROL"); break;
case 28: printf("CAP_SETFCAP"); break;
case 29: printf("CAP_MAC_OVERRIDE"); break;
case 30: printf("CAP_MAC_ADMIN"); break;
case 31: printf("CAP_SYSLOG"); break;
default: printf("CAP_%d", i); break;
}
}
}
}
printf(")\n");
}

int main() {
struct __user_cap_header_struct hdr;
struct __user_cap_data_struct data&#91;2];

printf("=== Capget 函数示例 ===\n");
printf("当前进程 PID: %d\n", getpid());
printf("当前用户 UID: %d\n", getuid());
printf("当前有效 UID: %d\n", geteuid());

// 设置头部信息
hdr.version = _LINUX_CAPABILITY_VERSION_3;
hdr.pid = 0; // 当前进程

// 获取能力信息
if (capget_wrapper(&hdr, data) == -1) {
perror("capget 失败");
exit(EXIT_FAILURE);
}

printf("\n进程能力信息:\n");
printf("能力版本: 0x%08x\n", hdr.version);
printf("进程 PID: %d\n", hdr.pid);

// 显示能力集
printf("\n能力集详情:\n");
print_capabilities("有效能力 (Effective)", data&#91;0].effective);
print_capabilities("允许能力 (Permitted)", data&#91;0].permitted);
print_capabilities("可继承能力 (Inheritable)", data&#91;0].inheritable);

// 分析当前能力状态
printf("\n能力状态分析:\n");
if (geteuid() == 0) {
printf("✓ 当前进程是 root 用户\n");
if (data&#91;0].effective == 0 && data&#91;0].permitted == 0) {
printf(" 但所有能力都被丢弃\n");
} else {
printf(" 拥有完整的 root 能力\n");
}
} else {
printf("✓ 当前进程是非特权用户\n");
if (data&#91;0].effective == 0) {
printf(" 没有有效能力\n");
} else {
printf(" 拥有一些特定能力\n");
}
}

// 错误处理演示
printf("\n错误处理演示:\n");

// 无效版本号
struct __user_cap_header_struct bad_hdr;
bad_hdr.version = 0x12345678;
bad_hdr.pid = 0;

if (capget_wrapper(&bad_hdr, data) == -1) {
if (errno == EINVAL) {
printf("无效版本号错误处理正确: %s\n", strerror(errno));
}
}

// 无效指针
hdr.version = _LINUX_CAPABILITY_VERSION_3;
if (capget_wrapper(&hdr, NULL) == -1) {
if (errno == EFAULT) {
printf("无效指针错误处理正确: %s\n", strerror(errno));
}
}

return 0;
}

  1. capset - 设置进程能力

函数介绍

capset系统调用用于设置调用进程的能力状态。它允许进程修改自己的能力集,但只能降低或保持当前能力,不能提升能力。

函数原型

1
2
3
4
5
6
#include <linux/capability.h>
#include <sys/syscall.h>
#include <unistd.h>

int capset(cap_user_header_t hdrp, const cap_user_data_t datap);

功能

设置调用进程的能力状态,包括有效能力、允许能力和可继承能力。

参数

  • cap_user_header_t hdrp: 指向头部结构的指针

  • const cap_user_data_t datap: 指向能力数据结构的指针

返回值

  • 成功时返回0

  • 失败时返回-1,并设置errno

相似函数

  • capget(): 获取进程能力

  • prctl(): 进程控制函数

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/capability.h>
#include <errno.h>
#include <string.h>

// 系统调用包装
static int capget_wrapper(cap_user_header_t hdrp, cap_user_data_t datap) {
return syscall(__NR_capget, hdrp, datap);
}

static int capset_wrapper(cap_user_header_t hdrp, const cap_user_data_t datap) {
return syscall(__NR_capset, hdrp, datap);
}

// 检查是否具有特定能力
int has_capability(__u32 caps, int cap) {
return (caps & (1 << cap)) != 0;
}

int main() {
struct __user_cap_header_struct hdr;
struct __user_cap_data_struct data&#91;2];

printf("=== Capset 函数示例 ===\n");
printf("当前进程 PID: %d\n", getpid());
printf("当前用户 UID: %d\n", getuid());
printf("当前有效 UID: %d\n", geteuid());

// 获取当前能力
hdr.version = _LINUX_CAPABILITY_VERSION_3;
hdr.pid = 0;

if (capget_wrapper(&hdr, data) == -1) {
perror("capget 失败");
exit(EXIT_FAILURE);
}

printf("\n修改前的能力状态:\n");
printf(" 有效能力: 0x%08x\n", data&#91;0].effective);
printf(" 允许能力: 0x%08x\n", data&#91;0].permitted);
printf(" 继承能力: 0x%08x\n", data&#91;0].inheritable);

// 演示能力丢弃(只能丢弃,不能增加)
printf("\n能力修改演示:\n");

// 保存原始状态
__u32 original_effective = data&#91;0].effective;
__u32 original_permitted = data&#91;0].permitted;

// 检查是否具有某些能力
if (has_capability(data&#91;0].effective, CAP_CHOWN)) {
printf(" 当前具有 CAP_CHOWN 能力\n");

// 尝试丢弃该能力
data&#91;0].effective &= ~(1 << CAP_CHOWN);

if (capset_wrapper(&hdr, data) == -1) {
printf(" capset 丢弃能力失败: %s\n", strerror(errno));
} else {
printf(" 成功丢弃 CAP_CHOWN 能力\n");
}
} else {
printf(" 当前不具有 CAP_CHOWN 能力\n");
}

// 再次获取能力状态验证
if (capget_wrapper(&hdr, data) == -1) {
perror("重新获取能力失败");
} else {
printf("\n修改后的有效能力: 0x%08x\n", data&#91;0].effective);

// 恢复原始状态
data&#91;0].effective = original_effective;
data&#91;0].permitted = original_permitted;

if (capset_wrapper(&hdr, data) == -1) {
printf("恢复原始状态失败: %s\n", strerror(errno));
} else {
printf("已恢复原始能力状态\n");
}
}

// 错误处理演示
printf("\n错误处理演示:\n");

// 权限不足错误
hdr.version = _LINUX_CAPABILITY_VERSION_3;
hdr.pid = 0;

if (capget_wrapper(&hdr, data) == 0) {
// 尝试设置不允许的能力(会失败)
__u32 temp_effective = data&#91;0].effective;
data&#91;0].effective |= (1 << CAP_SYS_MODULE); // 尝试增加能力

if (capset_wrapper(&hdr, data) == -1) {
if (errno == EPERM) {
printf("权限不足错误处理正确: %s\n", strerror(errno));
printf("说明: 不能通过 capset 提升能力\n");
}
}

// 恢复状态
data&#91;0].effective = temp_effective;
}

// 无效参数错误
hdr.version = 0x12345678; // 无效版本
if (capset_wrapper(&hdr, data) == -1) {
if (errno == EINVAL) {
printf("无效参数错误处理正确: %s\n", strerror(errno));
}
}

// 实际应用场景演示
printf("\n实际应用场景:\n");
printf("1. 网络服务器安全降权:\n");
printf(" - 启动时绑定特权端口(需要 CAP_NET_BIND_SERVICE)\n");
printf(" - 完成绑定后丢弃该能力\n");
printf(" - 切换到非特权用户运行\n\n");

printf("2. 最小权限原则:\n");
printf(" - 只保留执行任务必需的能力\n");
printf(" - 降低被攻击时的安全风险\n\n");

printf("3. 容器安全:\n");
printf(" - 限制容器内进程的能力\n");
printf(" - 防止容器逃逸攻击\n");

return 0;
}
data-ad-format="auto" data-full-width-responsive="true">