]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/caps.c
autotools: check for cap_get_file
[mirror_lxc.git] / src / lxc / caps.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
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
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #define _GNU_SOURCE
25 #include "config.h"
26
27 #include <errno.h>
28 #include <limits.h>
29 #include <fcntl.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/prctl.h>
33
34 #include "caps.h"
35 #include "log.h"
36
37 lxc_log_define(lxc_caps, lxc);
38
39 #if HAVE_LIBCAP
40
41 #ifndef PR_CAPBSET_READ
42 #define PR_CAPBSET_READ 23
43 #endif
44
45 int lxc_caps_down(void)
46 {
47 cap_t caps;
48 int ret;
49
50 /* when we are run as root, we don't want to play
51 * with the capabilities */
52 if (!getuid())
53 return 0;
54
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);
75 return 0;
76 }
77
78 int lxc_caps_up(void)
79 {
80 cap_t caps;
81 cap_value_t cap;
82 int ret;
83
84 /* when we are run as root, we don't want to play
85 * with the capabilities */
86 if (!getuid())
87 return 0;
88
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) {
101 if (errno == EINVAL) {
102 INFO("Last supported cap was %d", cap-1);
103 break;
104 } else {
105 ERROR("failed to cap_get_flag: %m");
106 goto out;
107 }
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);
125 return 0;
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 }
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';
184 errno = 0;
185 result = strtol(buf, &ptr, 10);
186 if (!ptr || (*ptr != '\0' && *ptr != '\n') || errno != 0)
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 }
211
212 static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag)
213 {
214 int ret;
215 cap_flag_value_t flagval;
216
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 {
228 #if LIBCAP_SUPPORTS_FILE_CAPABILITIES
229 bool cap_is_set;
230 cap_t caps;
231
232 caps = cap_get_file(path);
233 if (!caps) {
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));
241 return false;
242 }
243
244 cap_is_set = lxc_cap_is_set(caps, cap, flag);
245 cap_free(caps);
246 return cap_is_set;
247 #else
248 errno = ENODATA;
249 return false;
250 #endif
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));
261 return false;
262 }
263
264 cap_is_set = lxc_cap_is_set(caps, cap, flag);
265 cap_free(caps);
266 return cap_is_set;
267 }
268
269 #endif