]>
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) { | |
57 | ERROR("failed to cap_get_proc: %m"); | |
58 | return -1; | |
59 | } | |
60 | ||
61 | ret = cap_clear_flag(caps, CAP_EFFECTIVE); | |
62 | if (ret) { | |
63 | ERROR("failed to cap_clear_flag: %m"); | |
64 | goto out; | |
65 | } | |
66 | ||
67 | ret = cap_set_proc(caps); | |
68 | if (ret) { | |
69 | ERROR("failed to cap_set_proc: %m"); | |
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) { | |
91 | ERROR("failed to cap_get_proc: %m"); | |
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 { | |
105 | ERROR("failed to cap_get_flag: %m"); | |
106 | goto out; | |
107 | } | |
b3357a6f DL |
108 | } |
109 | ||
110 | ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag); | |
111 | if (ret) { | |
112 | ERROR("failed to cap_set_flag: %m"); | |
113 | goto out; | |
114 | } | |
115 | } | |
116 | ||
117 | ret = cap_set_proc(caps); | |
118 | if (ret) { | |
119 | ERROR("failed to cap_set_proc: %m"); | |
120 | goto out; | |
121 | } | |
122 | ||
123 | out: | |
124 | cap_free(caps); | |
d028235d | 125 | return 0; |
b3357a6f DL |
126 | } |
127 | ||
128 | int lxc_caps_init(void) | |
129 | { | |
130 | uid_t uid = getuid(); | |
131 | gid_t gid = getgid(); | |
132 | uid_t euid = geteuid(); | |
133 | ||
134 | if (!uid) { | |
135 | INFO("command is run as 'root'"); | |
136 | return 0; | |
137 | } | |
138 | ||
139 | if (uid && !euid) { | |
140 | INFO("command is run as setuid root (uid : %d)", uid); | |
141 | ||
142 | if (prctl(PR_SET_KEEPCAPS, 1)) { | |
143 | ERROR("failed to 'PR_SET_KEEPCAPS': %m"); | |
144 | return -1; | |
145 | } | |
146 | ||
147 | if (setresgid(gid, gid, gid)) { | |
148 | ERROR("failed to change gid to '%d': %m", gid); | |
149 | return -1; | |
150 | } | |
151 | ||
152 | if (setresuid(uid, uid, uid)) { | |
153 | ERROR("failed to change uid to '%d': %m", uid); | |
154 | return -1; | |
155 | } | |
156 | ||
157 | if (lxc_caps_up()) { | |
158 | ERROR("failed to restore capabilities: %m"); | |
159 | return -1; | |
160 | } | |
161 | } | |
162 | ||
163 | if (uid == euid) | |
164 | INFO("command is run as user '%d'", uid); | |
165 | ||
166 | return 0; | |
167 | } | |
20d81659 CS |
168 | |
169 | static int _real_caps_last_cap(void) | |
170 | { | |
171 | int fd; | |
172 | int result = -1; | |
173 | ||
174 | /* try to get the maximum capability over the kernel | |
175 | * interface introduced in v3.2 */ | |
176 | fd = open("/proc/sys/kernel/cap_last_cap", O_RDONLY); | |
177 | if (fd >= 0) { | |
178 | char buf[32]; | |
179 | char *ptr; | |
180 | int n; | |
181 | ||
182 | if ((n = read(fd, buf, 31)) >= 0) { | |
183 | buf[n] = '\0'; | |
09bbd745 | 184 | errno = 0; |
20d81659 | 185 | result = strtol(buf, &ptr, 10); |
09bbd745 | 186 | if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0) |
20d81659 CS |
187 | result = -1; |
188 | } | |
189 | ||
190 | close(fd); | |
191 | } | |
192 | ||
193 | /* try to get it manually by trying to get the status of | |
194 | * each capability indiviually from the kernel */ | |
195 | if (result < 0) { | |
196 | int cap = 0; | |
197 | while (prctl(PR_CAPBSET_READ, cap) >= 0) cap++; | |
198 | result = cap - 1; | |
199 | } | |
200 | ||
201 | return result; | |
202 | } | |
203 | ||
204 | int lxc_caps_last_cap(void) | |
205 | { | |
206 | static int last_cap = -1; | |
207 | if (last_cap < 0) last_cap = _real_caps_last_cap(); | |
208 | ||
209 | return last_cap; | |
210 | } | |
4a2ca8b2 | 211 | |
207c4c71 | 212 | static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag) |
ca364dc0 CB |
213 | { |
214 | int ret; | |
ca364dc0 CB |
215 | cap_flag_value_t flagval; |
216 | ||
207c4c71 CB |
217 | ret = cap_get_flag(caps, cap, flag, &flagval); |
218 | if (ret < 0) { | |
219 | ERROR("Failed to perform cap_get_flag(): %s.", strerror(errno)); | |
220 | return false; | |
221 | } | |
222 | ||
223 | return flagval == CAP_SET; | |
224 | } | |
225 | ||
226 | bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) | |
227 | { | |
69924fff | 228 | #if LIBCAP_SUPPORTS_FILE_CAPABILITIES |
207c4c71 CB |
229 | bool cap_is_set; |
230 | cap_t caps; | |
231 | ||
232 | caps = cap_get_file(path); | |
ca364dc0 | 233 | if (!caps) { |
207c4c71 CB |
234 | /* This is undocumented in the manpage but the source code show |
235 | * that cap_get_file() may return NULL when successful for the | |
236 | * case where it didn't detect any file capabilities. In this | |
237 | * case errno will be set to ENODATA. | |
238 | */ | |
239 | if (errno != ENODATA) | |
240 | ERROR("Failed to perform cap_get_file(): %s.\n", strerror(errno)); | |
ca364dc0 CB |
241 | return false; |
242 | } | |
243 | ||
207c4c71 CB |
244 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
245 | cap_free(caps); | |
246 | return cap_is_set; | |
69924fff CB |
247 | #else |
248 | errno = ENODATA; | |
249 | return false; | |
d6018f88 | 250 | #endif |
207c4c71 CB |
251 | } |
252 | ||
253 | bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) | |
254 | { | |
255 | bool cap_is_set; | |
256 | cap_t caps; | |
257 | ||
258 | caps = cap_get_proc(); | |
259 | if (!caps) { | |
260 | ERROR("Failed to perform cap_get_proc(): %s.\n", strerror(errno)); | |
ca364dc0 CB |
261 | return false; |
262 | } | |
263 | ||
207c4c71 | 264 | cap_is_set = lxc_cap_is_set(caps, cap, flag); |
ca364dc0 | 265 | cap_free(caps); |
207c4c71 | 266 | return cap_is_set; |
ca364dc0 CB |
267 | } |
268 | ||
495d2046 | 269 | #endif |