diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-08-21 19:30:01 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-08-21 19:30:01 +0200 |
commit | 200bcc851acbe1ba30fe90b5cf918f88370a5d15 (patch) | |
tree | 50f4d5ac42869548b78b00470d7b80b50a28157a | |
parent | 44b3f2ffbc01c0a9fcfb5d60af3e292f505ac67c (diff) | |
download | busybox-w32-200bcc851acbe1ba30fe90b5cf918f88370a5d15.tar.gz busybox-w32-200bcc851acbe1ba30fe90b5cf918f88370a5d15.tar.bz2 busybox-w32-200bcc851acbe1ba30fe90b5cf918f88370a5d15.zip |
run-init: new applet
function old new delta
switch_root_main 354 637 +283
drop_usermodehelper - 157 +157
cap_name_to_number - 77 +77
packed_usage 31707 31743 +36
applet_names 2665 2674 +9
applet_main 1544 1548 +4
applet_install_loc 193 194 +1
setpriv_main 933 928 -5
getcaps 131 122 -9
parse_cap 117 29 -88
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 5/3 up/down: 567/-102) Total: 465 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | include/libbb.h | 9 | ||||
-rw-r--r-- | klibc-utils/run-init.c | 27 | ||||
-rw-r--r-- | libbb/capability.c | 47 | ||||
-rw-r--r-- | util-linux/setpriv.c | 55 | ||||
-rw-r--r-- | util-linux/switch_root.c | 126 |
5 files changed, 212 insertions, 52 deletions
diff --git a/include/libbb.h b/include/libbb.h index 9535f5fb3..95a7470a8 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -1475,6 +1475,15 @@ const char *get_shell_name(void) FAST_FUNC; | |||
1475 | 1475 | ||
1476 | unsigned cap_name_to_number(const char *cap) FAST_FUNC; | 1476 | unsigned cap_name_to_number(const char *cap) FAST_FUNC; |
1477 | void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC; | 1477 | void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC; |
1478 | void drop_capability(int cap_ordinal) FAST_FUNC; | ||
1479 | /* Structures inside "struct caps" are Linux-specific and libcap-specific: */ | ||
1480 | #define DEFINE_STRUCT_CAPS \ | ||
1481 | struct caps { \ | ||
1482 | struct __user_cap_header_struct header; \ | ||
1483 | unsigned u32s; \ | ||
1484 | struct __user_cap_data_struct data[2]; \ | ||
1485 | } | ||
1486 | void getcaps(void *caps) FAST_FUNC; | ||
1478 | 1487 | ||
1479 | unsigned cap_name_to_number(const char *name) FAST_FUNC; | 1488 | unsigned cap_name_to_number(const char *name) FAST_FUNC; |
1480 | void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC; | 1489 | void printf_cap(const char *pfx, unsigned cap_no) FAST_FUNC; |
diff --git a/klibc-utils/run-init.c b/klibc-utils/run-init.c new file mode 100644 index 000000000..a70d1bfbf --- /dev/null +++ b/klibc-utils/run-init.c | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * run-init implementation for busybox | ||
3 | * | ||
4 | * Copyright (c) 2017 Denys Vlasenko <vda.linux@gmail.com> | ||
5 | * | ||
6 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
7 | */ | ||
8 | //config:config RUN_INIT | ||
9 | //config: bool "run-init" | ||
10 | //config: default y | ||
11 | //config: select PLATFORM_LINUX | ||
12 | //config: help | ||
13 | //config: The run-init utility is used from initramfs to select a new | ||
14 | //config: root device. Under initramfs, you have to use this instead of | ||
15 | //config: pivot_root. | ||
16 | //config: | ||
17 | //config: Booting with initramfs extracts a gzipped cpio archive into rootfs | ||
18 | //config: (which is a variant of ramfs/tmpfs). Because rootfs can't be moved | ||
19 | //config: or unmounted, pivot_root will not work from initramfs. Instead, | ||
20 | //config: run-init deletes everything out of rootfs (including itself), | ||
21 | //config: does a mount --move that overmounts rootfs with the new root, and | ||
22 | //config: then execs the specified init program. | ||
23 | //config: | ||
24 | //config: util-linux has a similar tool, switch-root. | ||
25 | //config: run-init differs by also having a "-d CAPS_TO_DROP" option. | ||
26 | |||
27 | /* applet and kbuild hooks are in switch_root.c */ | ||
diff --git a/libbb/capability.c b/libbb/capability.c index 692024f2f..f60062bfc 100644 --- a/libbb/capability.c +++ b/libbb/capability.c | |||
@@ -6,6 +6,14 @@ | |||
6 | //kbuild:lib-$(CONFIG_PLATFORM_LINUX) += capability.o | 6 | //kbuild:lib-$(CONFIG_PLATFORM_LINUX) += capability.o |
7 | 7 | ||
8 | #include <linux/capability.h> | 8 | #include <linux/capability.h> |
9 | // #include <sys/capability.h> | ||
10 | // This header is in libcap, but the functions are in libc. | ||
11 | // Comment in the header says this above capset/capget: | ||
12 | /* system calls - look to libc for function to system call mapping */ | ||
13 | extern int capset(cap_user_header_t header, cap_user_data_t data); | ||
14 | extern int capget(cap_user_header_t header, const cap_user_data_t data); | ||
15 | // so for bbox, let's just repeat the declarations. | ||
16 | // This way, libcap needs not be installed in build environment. | ||
9 | #include "libbb.h" | 17 | #include "libbb.h" |
10 | 18 | ||
11 | static const char *const capabilities[] = { | 19 | static const char *const capabilities[] = { |
@@ -77,3 +85,42 @@ void FAST_FUNC printf_cap(const char *pfx, unsigned cap_no) | |||
77 | } | 85 | } |
78 | printf("%scap_%u", pfx, cap_no); | 86 | printf("%scap_%u", pfx, cap_no); |
79 | } | 87 | } |
88 | |||
89 | DEFINE_STRUCT_CAPS; | ||
90 | |||
91 | void FAST_FUNC getcaps(void *arg) | ||
92 | { | ||
93 | static const uint8_t versions[] = { | ||
94 | _LINUX_CAPABILITY_U32S_3, /* = 2 (fits into byte) */ | ||
95 | _LINUX_CAPABILITY_U32S_2, /* = 2 */ | ||
96 | _LINUX_CAPABILITY_U32S_1, /* = 1 */ | ||
97 | }; | ||
98 | int i; | ||
99 | struct caps *caps = arg; | ||
100 | |||
101 | caps->header.pid = 0; | ||
102 | for (i = 0; i < ARRAY_SIZE(versions); i++) { | ||
103 | caps->header.version = versions[i]; | ||
104 | if (capget(&caps->header, NULL) == 0) | ||
105 | goto got_it; | ||
106 | } | ||
107 | bb_simple_perror_msg_and_die("capget"); | ||
108 | got_it: | ||
109 | |||
110 | switch (caps->header.version) { | ||
111 | case _LINUX_CAPABILITY_VERSION_1: | ||
112 | caps->u32s = _LINUX_CAPABILITY_U32S_1; | ||
113 | break; | ||
114 | case _LINUX_CAPABILITY_VERSION_2: | ||
115 | caps->u32s = _LINUX_CAPABILITY_U32S_2; | ||
116 | break; | ||
117 | case _LINUX_CAPABILITY_VERSION_3: | ||
118 | caps->u32s = _LINUX_CAPABILITY_U32S_3; | ||
119 | break; | ||
120 | default: | ||
121 | bb_error_msg_and_die("unsupported capability version"); | ||
122 | } | ||
123 | |||
124 | if (capget(&caps->header, caps->data) != 0) | ||
125 | bb_simple_perror_msg_and_die("capget"); | ||
126 | } | ||
diff --git a/util-linux/setpriv.c b/util-linux/setpriv.c index 9f2793949..12ab1bd66 100644 --- a/util-linux/setpriv.c +++ b/util-linux/setpriv.c | |||
@@ -124,48 +124,7 @@ enum { | |||
124 | }; | 124 | }; |
125 | 125 | ||
126 | #if ENABLE_FEATURE_SETPRIV_CAPABILITIES | 126 | #if ENABLE_FEATURE_SETPRIV_CAPABILITIES |
127 | struct caps { | 127 | DEFINE_STRUCT_CAPS; |
128 | struct __user_cap_header_struct header; | ||
129 | cap_user_data_t data; | ||
130 | int u32s; | ||
131 | }; | ||
132 | |||
133 | static void getcaps(struct caps *caps) | ||
134 | { | ||
135 | static const uint8_t versions[] = { | ||
136 | _LINUX_CAPABILITY_U32S_3, /* = 2 (fits into byte) */ | ||
137 | _LINUX_CAPABILITY_U32S_2, /* = 2 */ | ||
138 | _LINUX_CAPABILITY_U32S_1, /* = 1 */ | ||
139 | }; | ||
140 | int i; | ||
141 | |||
142 | caps->header.pid = 0; | ||
143 | for (i = 0; i < ARRAY_SIZE(versions); i++) { | ||
144 | caps->header.version = versions[i]; | ||
145 | if (capget(&caps->header, NULL) == 0) | ||
146 | goto got_it; | ||
147 | } | ||
148 | bb_simple_perror_msg_and_die("capget"); | ||
149 | got_it: | ||
150 | |||
151 | switch (caps->header.version) { | ||
152 | case _LINUX_CAPABILITY_VERSION_1: | ||
153 | caps->u32s = _LINUX_CAPABILITY_U32S_1; | ||
154 | break; | ||
155 | case _LINUX_CAPABILITY_VERSION_2: | ||
156 | caps->u32s = _LINUX_CAPABILITY_U32S_2; | ||
157 | break; | ||
158 | case _LINUX_CAPABILITY_VERSION_3: | ||
159 | caps->u32s = _LINUX_CAPABILITY_U32S_3; | ||
160 | break; | ||
161 | default: | ||
162 | bb_error_msg_and_die("unsupported capability version"); | ||
163 | } | ||
164 | |||
165 | caps->data = xmalloc(sizeof(caps->data[0]) * caps->u32s); | ||
166 | if (capget(&caps->header, caps->data) < 0) | ||
167 | bb_simple_perror_msg_and_die("capget"); | ||
168 | } | ||
169 | 128 | ||
170 | static unsigned parse_cap(const char *cap) | 129 | static unsigned parse_cap(const char *cap) |
171 | { | 130 | { |
@@ -195,7 +154,7 @@ static void set_inh_caps(char *capstring) | |||
195 | 154 | ||
196 | cap = parse_cap(capstring); | 155 | cap = parse_cap(capstring); |
197 | if (CAP_TO_INDEX(cap) >= caps.u32s) | 156 | if (CAP_TO_INDEX(cap) >= caps.u32s) |
198 | bb_error_msg_and_die("invalid capability cap"); | 157 | bb_error_msg_and_die("invalid capability '%s'", capstring); |
199 | 158 | ||
200 | if (capstring[0] == '+') | 159 | if (capstring[0] == '+') |
201 | caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap); | 160 | caps.data[CAP_TO_INDEX(cap)].inheritable |= CAP_TO_MASK(cap); |
@@ -204,11 +163,8 @@ static void set_inh_caps(char *capstring) | |||
204 | capstring = strtok(NULL, ","); | 163 | capstring = strtok(NULL, ","); |
205 | } | 164 | } |
206 | 165 | ||
207 | if ((capset(&caps.header, caps.data)) < 0) | 166 | if (capset(&caps.header, caps.data) != 0) |
208 | bb_perror_msg_and_die("capset"); | 167 | bb_perror_msg_and_die("capset"); |
209 | |||
210 | if (ENABLE_FEATURE_CLEAN_UP) | ||
211 | free(caps.data); | ||
212 | } | 168 | } |
213 | 169 | ||
214 | static void set_ambient_caps(char *string) | 170 | static void set_ambient_caps(char *string) |
@@ -322,10 +278,9 @@ static int dump(void) | |||
322 | bb_putchar('\n'); | 278 | bb_putchar('\n'); |
323 | # endif | 279 | # endif |
324 | 280 | ||
325 | if (ENABLE_FEATURE_CLEAN_UP) { | 281 | if (ENABLE_FEATURE_CLEAN_UP) |
326 | IF_FEATURE_SETPRIV_CAPABILITIES(free(caps.data);) | ||
327 | free(gids); | 282 | free(gids); |
328 | } | 283 | |
329 | return EXIT_SUCCESS; | 284 | return EXIT_SUCCESS; |
330 | } | 285 | } |
331 | #endif /* FEATURE_SETPRIV_DUMP */ | 286 | #endif /* FEATURE_SETPRIV_DUMP */ |
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c index fb6057a02..fe9ab68d0 100644 --- a/util-linux/switch_root.c +++ b/util-linux/switch_root.c | |||
@@ -26,20 +26,46 @@ | |||
26 | //config: list of active mount points. That's why. | 26 | //config: list of active mount points. That's why. |
27 | 27 | ||
28 | //applet:IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP)) | 28 | //applet:IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP)) |
29 | // APPLET_ODDNAME:name main location suid_type help | ||
30 | //applet:IF_RUN_INIT( APPLET_ODDNAME(run-init, switch_root, BB_DIR_SBIN, BB_SUID_DROP, run_init)) | ||
29 | 31 | ||
30 | //kbuild:lib-$(CONFIG_SWITCH_ROOT) += switch_root.o | 32 | //kbuild:lib-$(CONFIG_SWITCH_ROOT) += switch_root.o |
33 | //kbuild:lib-$(CONFIG_RUN_INIT) += switch_root.o | ||
31 | 34 | ||
32 | //usage:#define switch_root_trivial_usage | 35 | //usage:#define switch_root_trivial_usage |
33 | //usage: "[-c /dev/console] NEW_ROOT NEW_INIT [ARGS]" | 36 | //usage: "[-c CONSOLE_DEV] NEW_ROOT NEW_INIT [ARGS]" |
34 | //usage:#define switch_root_full_usage "\n\n" | 37 | //usage:#define switch_root_full_usage "\n\n" |
35 | //usage: "Free initramfs and switch to another root fs:\n" | 38 | //usage: "Free initramfs and switch to another root fs:\n" |
36 | //usage: "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n" | 39 | //usage: "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n" |
37 | //usage: "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n" | 40 | //usage: "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n" |
38 | //usage: "\n -c DEV Reopen stdio to DEV after switch" | 41 | //usage: "\n -c DEV Reopen stdio to DEV after switch" |
39 | 42 | ||
43 | //usage:#define run_init_trivial_usage | ||
44 | //usage: "[-d CAP,CAP...] [-c CONSOLE_DEV] NEW_ROOT NEW_INIT [ARGS]" | ||
45 | //usage:#define run_init_full_usage "\n\n" | ||
46 | //usage: "Free initramfs and switch to another root fs:\n" | ||
47 | //usage: "chroot to NEW_ROOT, delete all in /, move NEW_ROOT to /,\n" | ||
48 | //usage: "execute NEW_INIT. PID must be 1. NEW_ROOT must be a mountpoint.\n" | ||
49 | //usage: "\n -c DEV Reopen stdio to DEV after switch" | ||
50 | //usage: "\n -d CAPS Drop capabilities" | ||
51 | |||
40 | #include <sys/vfs.h> | 52 | #include <sys/vfs.h> |
41 | #include <sys/mount.h> | 53 | #include <sys/mount.h> |
54 | #if ENABLE_RUN_INIT | ||
55 | # include <sys/prctl.h> | ||
56 | # include <linux/capability.h> | ||
57 | // #include <sys/capability.h> | ||
58 | // This header is in libcap, but the functions are in libc. | ||
59 | // Comment in the header says this above capset/capget: | ||
60 | /* system calls - look to libc for function to system call mapping */ | ||
61 | extern int capset(cap_user_header_t header, cap_user_data_t data); | ||
62 | extern int capget(cap_user_header_t header, const cap_user_data_t data); | ||
63 | // so for bbox, let's just repeat the declarations. | ||
64 | // This way, libcap needs not be installed in build environment. | ||
65 | #endif | ||
66 | |||
42 | #include "libbb.h" | 67 | #include "libbb.h" |
68 | |||
43 | // Make up for header deficiencies | 69 | // Make up for header deficiencies |
44 | #ifndef RAMFS_MAGIC | 70 | #ifndef RAMFS_MAGIC |
45 | # define RAMFS_MAGIC ((unsigned)0x858458f6) | 71 | # define RAMFS_MAGIC ((unsigned)0x858458f6) |
@@ -89,6 +115,84 @@ static void delete_contents(const char *directory, dev_t rootdev) | |||
89 | } | 115 | } |
90 | } | 116 | } |
91 | 117 | ||
118 | #if ENABLE_RUN_INIT | ||
119 | DEFINE_STRUCT_CAPS; | ||
120 | |||
121 | static void drop_capset(int cap_idx) | ||
122 | { | ||
123 | struct caps caps; | ||
124 | |||
125 | /* Get the current capability mask */ | ||
126 | getcaps(&caps); | ||
127 | |||
128 | /* Drop the bit */ | ||
129 | caps.data[CAP_TO_INDEX(cap_idx)].inheritable &= ~CAP_TO_MASK(cap_idx); | ||
130 | |||
131 | /* And drop the capability. */ | ||
132 | if (capset(&caps.header, caps.data) != 0) | ||
133 | bb_perror_msg_and_die("capset"); | ||
134 | } | ||
135 | |||
136 | static void drop_bounding_set(int cap_idx) | ||
137 | { | ||
138 | int ret; | ||
139 | |||
140 | ret = prctl(PR_CAPBSET_READ, cap_idx, 0, 0, 0); | ||
141 | if (ret < 0) | ||
142 | bb_perror_msg_and_die("prctl: %s", "PR_CAPBSET_READ"); | ||
143 | |||
144 | if (ret == 1) { | ||
145 | ret = prctl(PR_CAPBSET_DROP, cap_idx, 0, 0, 0); | ||
146 | if (ret != 0) | ||
147 | bb_perror_msg_and_die("prctl: %s", "PR_CAPBSET_DROP"); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | static void drop_usermodehelper(const char *filename, int cap_idx) | ||
152 | { | ||
153 | unsigned lo, hi; | ||
154 | char buf[sizeof(int)*3 * 2 + 8]; | ||
155 | int fd; | ||
156 | int ret; | ||
157 | |||
158 | ret = open_read_close(filename, buf, sizeof(buf) - 1); | ||
159 | if (ret < 0) | ||
160 | return; /* assuming files do not exist */ | ||
161 | |||
162 | buf[ret] = '\0'; | ||
163 | ret = sscanf(buf, "%u %u", &lo, &hi); | ||
164 | if (ret != 2) | ||
165 | bb_perror_msg_and_die("can't parse file '%s'", filename); | ||
166 | |||
167 | if (cap_idx < 32) | ||
168 | lo &= ~(1 << cap_idx); | ||
169 | else | ||
170 | hi &= ~(1 << (cap_idx - 32)); | ||
171 | |||
172 | fd = xopen(filename, O_WRONLY); | ||
173 | fdprintf(fd, "%u %u", lo, hi); | ||
174 | close(fd); | ||
175 | } | ||
176 | |||
177 | static void drop_capabilities(char *string) | ||
178 | { | ||
179 | char *cap; | ||
180 | |||
181 | cap = strtok(string, ","); | ||
182 | while (cap) { | ||
183 | unsigned cap_idx; | ||
184 | |||
185 | cap_idx = cap_name_to_number(cap); | ||
186 | drop_usermodehelper("/proc/sys/kernel/usermodehelper/bset", cap_idx); | ||
187 | drop_usermodehelper("/proc/sys/kernel/usermodehelper/inheritable", cap_idx); | ||
188 | drop_bounding_set(cap_idx); | ||
189 | drop_capset(cap_idx); | ||
190 | bb_error_msg("dropped capability: %s", cap); | ||
191 | cap = strtok(NULL, ","); | ||
192 | } | ||
193 | } | ||
194 | #endif | ||
195 | |||
92 | int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 196 | int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
93 | int switch_root_main(int argc UNUSED_PARAM, char **argv) | 197 | int switch_root_main(int argc UNUSED_PARAM, char **argv) |
94 | { | 198 | { |
@@ -98,7 +202,25 @@ int switch_root_main(int argc UNUSED_PARAM, char **argv) | |||
98 | dev_t rootdev; | 202 | dev_t rootdev; |
99 | 203 | ||
100 | // Parse args (-c console). '+': stop at first non-option | 204 | // Parse args (-c console). '+': stop at first non-option |
101 | getopt32(argv, "^+" "c:" "\0" "-2" /* minimum 2 args */, &console); | 205 | if (ENABLE_SWITCH_ROOT && (!ENABLE_RUN_INIT || applet_name[0] == 's')) { |
206 | getopt32(argv, "^+" | ||
207 | "c:" | ||
208 | "\0" "-2" /* minimum 2 args */, | ||
209 | &console | ||
210 | ); | ||
211 | } else { | ||
212 | #if ENABLE_RUN_INIT | ||
213 | char *cap_list = NULL; | ||
214 | getopt32(argv, "^+" | ||
215 | "c:d:" | ||
216 | "\0" "-2" /* minimum 2 args */, | ||
217 | &console, | ||
218 | &cap_list | ||
219 | ); | ||
220 | if (cap_list) | ||
221 | drop_capabilities(cap_list); | ||
222 | #endif | ||
223 | } | ||
102 | argv += optind; | 224 | argv += optind; |
103 | newroot = *argv++; | 225 | newroot = *argv++; |
104 | 226 | ||