]>
Commit | Line | Data |
---|---|---|
b3357a6f DL |
1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
9afe19d6 | 7 | * Daniel Lezcano <daniel.lezcano at free.fr> |
b3357a6f DL |
8 | * |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
250b1eec | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
b3357a6f DL |
22 | */ |
23 | ||
24 | #define _GNU_SOURCE | |
ca364dc0 CB |
25 | #include "config.h" |
26 | ||
27 | #include <errno.h> | |
28 | #include <limits.h> | |
20d81659 CS |
29 | #include <fcntl.h> |
30 | #include <stdlib.h> | |
ca364dc0 | 31 | #include <unistd.h> |
b3357a6f | 32 | #include <sys/prctl.h> |
b3357a6f | 33 | |
ca364dc0 | 34 | #include "caps.h" |
b3357a6f DL |
35 | #include "log.h" |
36 | ||
37 | lxc_log_define(lxc_caps, lxc); | |
38 | ||
e37dda71 | 39 | #if HAVE_LIBCAP |
495d2046 | 40 | |
cbec0030 SG |
41 | #ifndef PR_CAPBSET_READ |
42 | #define PR_CAPBSET_READ 23 | |
43 | #endif | |
44 | ||
b3357a6f DL |
45 | int lxc_caps_down(void) |
46 | { | |
47 | cap_t caps; | |
48 | int ret; | |
49 | ||
7ee895e4 DL |
50 | /* when we are run as root, we don't want to play |
51 | * with the capabilities */ | |
52 | if (!getuid()) | |
53 | return 0; | |
54 | ||
b3357a6f DL |
55 | caps = cap_get_proc(); |
56 | if (!caps) { | |
13277ec4 | 57 | ERROR("failed to cap_get_proc: %s", strerror(errno)); |
b3357a6f DL |
58 | return -1; |
59 | } | |
60 | ||
61 | ret = cap_clear_flag(caps, CAP_EFFECTIVE); | |
62 | if (ret) { | |
13277ec4 | 63 | ERROR("failed to cap_clear_flag: %s", strerror(errno)); |
b3357a6f DL |
64 | goto out; |
65 | } | |
66 | ||
67 | ret = cap_set_proc(caps); | |
68 | if (ret) { | |
13277ec4 | 69 | ERROR("failed to cap_set_proc: %s", strerror(errno)); |
b3357a6f DL |
70 | goto out; |
71 | } | |
72 | ||
73 | out: | |
74 | cap_free(caps); | |
d028235d | 75 | return 0; |
b3357a6f DL |
76 | } |
77 | ||
78 | int lxc_caps_up(void) | |
79 | { | |
80 | cap_t caps; | |
81 | cap_value_t cap; | |
82 | int ret; | |
83 | ||
7ee895e4 DL |
84 | /* when we are run as root, we don't want to play |
85 | * with the capabilities */ | |
86 | if (!getuid()) | |
87 | return 0; | |
88 | ||
b3357a6f DL |
89 | caps = cap_get_proc(); |
90 | if (!caps) { | |
13277ec4 | 91 | ERROR("failed to cap_get_proc: %s", strerror(errno)); |
b3357a6f DL |
92 | return -1; |
93 | } | |
94 | ||
95 | for (cap = 0; cap <= CAP_LAST_CAP; cap++) { | |
96 | ||
97 | cap_flag_value_t flag; | |
98 | ||
99 | ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag); | |
100 | if (ret) { | |
2b657f10 | 101 | if (errno == EINVAL) { |
959aee9c | 102 | INFO("Last supported cap was %d", cap-1); |
2b657f10 SH |
103 | break; |
104 | } else { | |
13277ec4 | 105 | ERROR("failed to cap_get_flag: %s", |
106 | strerror(errno)); | |
2b657f10 SH |
107 | goto out; |
108 | } | |
b3357a6f DL |
109 | } |
110 | ||
111 | ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); | |
112 | if (ret) { | |
13277ec4 | 113 | ERROR("failed to cap_set_flag: %s", strerror(errno)); |
b3357a6f DL |
114 | goto out; |
115 | } | |
116 | } | |
117 | ||
118 | ret = cap_set_proc(caps); | |
119 | if (ret) { | |
13277ec4 | 120 | ERROR("failed to cap_set_proc: %s", strerror(errno)); |
b3357a6f DL |
121 | goto out; |
122 | } | |
123 | ||
124 | out: | |
125 | cap_free(caps); | |
d028235d | 126 | return 0; |
b3357a6f DL |
127 | } |
128 | ||
129 | int lxc_caps_init(void) | |
130 | { | |
131 | uid_t uid = getuid(); | |
132 | gid_t gid = getgid(); | |
133 | uid_t euid = geteuid(); | |
134 | ||
135 | if (!uid) { | |
136 | INFO("command is run as 'root'"); | |
137 | return 0; | |
138 | } | |
139 | ||
140 | if (uid && !euid) { | |
141 | INFO("command is run as setuid root (uid : %d)", uid); | |
142 | ||
143 | if (prctl(PR_SET_KEEPCAPS, 1)) { | |
13277ec4 | 144 | ERROR("failed to 'PR_SET_KEEPCAPS': %s", |
145 | strerror(errno)); | |
b3357a6f DL |
146 | return -1; |
147 | } | |
148 | ||
149 | if (setresgid(gid, gid, gid)) { | |
13277ec4 | 150 | ERROR("failed to change gid to '%d': %s", gid, |
151 | strerror(errno)); | |
b3357a6f DL |
152 | return -1; |
153 | } | |
154 | ||
155 | if (setresuid(uid, uid, uid)) { | |
13277ec4 | 156 | ERROR("failed to change uid to '%d': %s", uid, |
157 | strerror(errno)); | |
b3357a6f DL |
158 | return -1; |
159 | } | |
160 | ||
161 | if (lxc_caps_up()) { | |
13277ec4 | 162 | ERROR("failed to restore capabilities: %s", |
163 | strerror(errno)); | |
b3357a6f DL |
164 | return -1; |
165 | } | |
166 | } | |
167 | ||
168 | if (uid == euid) | |
169 | INFO("command is run as user '%d'", uid); | |
170 | ||
171 | return 0; | |
172 | } | |
20d81659 CS |
173 | |
174 | static int _real_caps_last_cap(void) | |
175 | { | |
176 | int fd; | |
177 | int result = -1; | |
178 | ||
179 | /* try to get the maximum capability over the kernel | |
180 | * interface introduced in v3.2 */ | |
181 | fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY); | |
182 | if (fd >= 0) { | |
183 | char buf[32]; | |
184 | char *ptr; | |
185 | int n; | |
186 | ||
187 | if ((n = read(fd, buf, 31)) >= 0) { | |
188 | buf[n] = '\0'; | |
09bbd745 | 189 | errno = 0; |
20d81659 | 190 | result = strtol(buf, &ptr, 10); |
09bbd745 | 191 | if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0) |
20d81659 CS |
192 | result = -1; |
193 | } | |
194 | ||
195 | close(fd); | |
196 | } | |
197 | ||
198 | /* try to get it manually by trying to get the status of | |
199 | * each capability indiviually from the kernel */ | |
200 | if (result < 0) { | |
201 | int cap = 0; | |
202 | while (prctl(PR_CAPBSET_READ, cap) >= 0) cap++; | |
203 | result = cap - 1; | |
204 | } | |
205 | ||
206 | return result; | |
207 | } | |
208 | ||
209 | int lxc_caps_last_cap(void) | |
210 | { | |
211 | static int last_cap = -1; | |
212 | if (last_cap < 0) last_cap = _real_caps_last_cap(); | |
213 | ||
214 | return last_cap; | |
215 | } | |
4a2ca8b2 | 216 | |
207c4c71 | 217 | static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag) |
ca364dc0 CB |
218 | { |
219 | int ret; | |
ca364dc0 CB |
220 | cap_flag_value_t flagval; |
221 | ||
207c4c71 CB |
222 | ret = cap_get_flag(caps, cap, flag, &flagval); |
223 | if (ret < 0) { | |
224 | ERROR("Failed to perform cap_get_flag(): %s.", strerror(errno)); | |
225 | return false; | |
226 | } | |
227 | ||
228 | return flagval == CAP_SET; | |
229 | } | |
230 | ||
231 | bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) | |
232 | { | |
69924fff | 233 | #if LIBCAP_SUPPORTS_FILE_CAPABILITIES |
207c4c71 CB |
234 | bool cap_is_set; |
235 | cap_t caps; | |
236 | ||
237 | caps = cap_get_file(path); | |
ca364dc0 | 238 | if (!caps) { |
207c4c71 CB |
239 | /* This is undocumented in the manpage but the source code show |
240 | * that cap_get_file() may return NULL when successful for the | |
241 | * case where it didn't detect any file capabilities. In this | |
242 | * case errno will be set to ENODATA. | |
243 | */ | |
244 | if (errno != ENODATA) | |
245 | ERROR("Failed to perform cap_get_file(): %s.\n", strerror(errno)); | |
ca364dc0 CB |
246 | return false; |
247 | } | |
248 | ||
207c4c71 CB |
249 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
250 | cap_free(caps); | |
251 | return cap_is_set; | |
69924fff CB |
252 | #else |
253 | errno = ENODATA; | |
254 | return false; | |
d6018f88 | 255 | #endif |
207c4c71 CB |
256 | } |
257 | ||
258 | bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) | |
259 | { | |
260 | bool cap_is_set; | |
261 | cap_t caps; | |
262 | ||
263 | caps = cap_get_proc(); | |
264 | if (!caps) { | |
265 | ERROR("Failed to perform cap_get_proc(): %s.\n", strerror(errno)); | |
ca364dc0 CB |
266 | return false; |
267 | } | |
268 | ||
207c4c71 | 269 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
ca364dc0 | 270 | cap_free(caps); |
207c4c71 | 271 | return cap_is_set; |
ca364dc0 CB |
272 | } |
273 | ||
495d2046 | 274 | #endif |