]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/tools/tool_utils.c
29adc67763f70ecad69021e7a0e11ea9c4831470
[mirror_lxc.git] / src / lxc / tools / tool_utils.c
1 /* liblxcapi
2 *
3 * Copyright © 2018 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2018 Canonical Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #define _GNU_SOURCE
21 #define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <unistd.h>
30 #include <linux/sched.h>
31 #include <sys/prctl.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35
36 #if HAVE_SYS_PERSONALITY_H
37 #include <sys/personality.h>
38 #endif
39
40 #include <lxc/lxccontainer.h>
41
42 #include "tool_utils.h"
43
44 int lxc_fill_elevated_privileges(char *flaglist, int *flags)
45 {
46 char *token, *saveptr = NULL;
47 int i, aflag;
48 struct {
49 const char *token;
50 int flag;
51 } all_privs[] = {
52 { "CGROUP", LXC_ATTACH_MOVE_TO_CGROUP },
53 { "CAP", LXC_ATTACH_DROP_CAPABILITIES },
54 { "LSM", LXC_ATTACH_LSM_EXEC },
55 { NULL, 0 }
56 };
57
58 if (!flaglist) {
59 /* For the sake of backward compatibility, drop all privileges
60 * if none is specified.
61 */
62 for (i = 0; all_privs[i].token; i++)
63 *flags |= all_privs[i].flag;
64
65 return 0;
66 }
67
68 token = strtok_r(flaglist, "|", &saveptr);
69 while (token) {
70 aflag = -1;
71 for (i = 0; all_privs[i].token; i++)
72 if (!strcmp(all_privs[i].token, token))
73 aflag = all_privs[i].flag;
74 if (aflag < 0)
75 return -1;
76
77 *flags |= aflag;
78
79 token = strtok_r(NULL, "|", &saveptr);
80 }
81
82 return 0;
83 }
84
85 signed long lxc_config_parse_arch(const char *arch)
86 {
87 #if HAVE_SYS_PERSONALITY_H
88 size_t i;
89 struct per_name {
90 char *name;
91 unsigned long per;
92 } pername[] = {
93 { "x86", PER_LINUX32 },
94 { "linux32", PER_LINUX32 },
95 { "i386", PER_LINUX32 },
96 { "i486", PER_LINUX32 },
97 { "i586", PER_LINUX32 },
98 { "i686", PER_LINUX32 },
99 { "athlon", PER_LINUX32 },
100 { "mips", PER_LINUX32 },
101 { "mipsel", PER_LINUX32 },
102 { "ppc", PER_LINUX32 },
103 { "arm", PER_LINUX32 },
104 { "armv7l", PER_LINUX32 },
105 { "armhf", PER_LINUX32 },
106 { "armel", PER_LINUX32 },
107 { "powerpc", PER_LINUX32 },
108 { "linux64", PER_LINUX },
109 { "x86_64", PER_LINUX },
110 { "amd64", PER_LINUX },
111 { "mips64", PER_LINUX },
112 { "mips64el", PER_LINUX },
113 { "ppc64", PER_LINUX },
114 { "ppc64le", PER_LINUX },
115 { "ppc64el", PER_LINUX },
116 { "powerpc64", PER_LINUX },
117 { "s390x", PER_LINUX },
118 { "aarch64", PER_LINUX },
119 { "arm64", PER_LINUX },
120 };
121 size_t len = sizeof(pername) / sizeof(pername[0]);
122
123 for (i = 0; i < len; i++) {
124 if (!strcmp(pername[i].name, arch))
125 return pername[i].per;
126 }
127 #endif
128
129 return -1;
130 }
131
132 enum {
133 LXC_NS_USER,
134 LXC_NS_MNT,
135 LXC_NS_PID,
136 LXC_NS_UTS,
137 LXC_NS_IPC,
138 LXC_NS_NET,
139 LXC_NS_CGROUP,
140 LXC_NS_MAX
141 };
142
143 const static struct ns_info {
144 const char *proc_name;
145 int clone_flag;
146 } ns_info[LXC_NS_MAX] = {
147 [LXC_NS_USER] = { "user", CLONE_NEWUSER },
148 [LXC_NS_MNT] = { "mnt", CLONE_NEWNS },
149 [LXC_NS_PID] = { "pid", CLONE_NEWPID },
150 [LXC_NS_UTS] = { "uts", CLONE_NEWUTS },
151 [LXC_NS_IPC] = { "ipc", CLONE_NEWIPC },
152 [LXC_NS_NET] = { "net", CLONE_NEWNET },
153 [LXC_NS_CGROUP] = { "cgroup", CLONE_NEWCGROUP }
154 };
155
156 int lxc_namespace_2_cloneflag(const char *namespace)
157 {
158 int i;
159 for (i = 0; i < LXC_NS_MAX; i++)
160 if (!strcasecmp(ns_info[i].proc_name, namespace))
161 return ns_info[i].clone_flag;
162
163 fprintf(stderr, "Invalid namespace name \"%s\"", namespace);
164 return -EINVAL;
165 }
166
167 int lxc_fill_namespace_flags(char *flaglist, int *flags)
168 {
169 char *token, *saveptr = NULL;
170 int aflag;
171
172 if (!flaglist) {
173 fprintf(stderr, "At least one namespace is needed\n");
174 return -1;
175 }
176
177 token = strtok_r(flaglist, "|", &saveptr);
178 while (token) {
179
180 aflag = lxc_namespace_2_cloneflag(token);
181 if (aflag < 0)
182 return -1;
183
184 *flags |= aflag;
185
186 token = strtok_r(NULL, "|", &saveptr);
187 }
188
189 return 0;
190 }
191
192 #if HAVE_LIBCAP
193
194 #ifndef PR_CAPBSET_READ
195 #define PR_CAPBSET_READ 23
196 #endif
197
198 int lxc_caps_init(void)
199 {
200 uid_t uid = getuid();
201 gid_t gid = getgid();
202 uid_t euid = geteuid();
203
204 if (!uid)
205 return 0;
206
207 if (uid && !euid) {
208 if (prctl(PR_SET_KEEPCAPS, 1)) {
209 fprintf(stderr, "%s - Failed to set PR_SET_KEEPCAPS\n", strerror(errno));
210 return -1;
211 }
212
213 if (setresgid(gid, gid, gid)) {
214 fprintf(stderr, "%s - Failed to change gid to %d\n", strerror(errno), gid);
215 return -1;
216 }
217
218 if (setresuid(uid, uid, uid)) {
219 fprintf(stderr, "%s - Failed to change uid to %d\n", strerror(errno), uid);
220 return -1;
221 }
222
223 if (lxc_caps_up()) {
224 fprintf(stderr, "%s - Failed to restore capabilities\n", strerror(errno));
225 return -1;
226 }
227 }
228
229 return 0;
230 }
231
232 int lxc_caps_up(void)
233 {
234 cap_t caps;
235 cap_value_t cap;
236 int ret;
237
238 /* when we are run as root, we don't want to play
239 * with the capabilities */
240 if (!getuid())
241 return 0;
242
243 caps = cap_get_proc();
244 if (!caps) {
245 fprintf(stderr, "%s - Failed to cap_get_proc\n", strerror(errno));
246 return -1;
247 }
248
249 for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
250 cap_flag_value_t flag;
251
252 ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag);
253 if (ret) {
254 if (errno == EINVAL) {
255 break;
256 } else {
257 fprintf(stderr, "%s- Failed to call cap_get_flag\n", strerror(errno));
258 goto out;
259 }
260 }
261
262 ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
263 if (ret) {
264 fprintf(stderr, "%s - Failed to call cap_set_flag", strerror(errno));
265 goto out;
266 }
267 }
268
269 ret = cap_set_proc(caps);
270 if (ret) {
271 fprintf(stderr, "%s - Failed to cap_set_proc", strerror(errno));
272 goto out;
273 }
274
275 out:
276 cap_free(caps);
277 return 0;
278 }
279
280 #endif
281
282 int wait_for_pid(pid_t pid)
283 {
284 int status, ret;
285
286 again:
287 ret = waitpid(pid, &status, 0);
288 if (ret == -1) {
289 if (errno == EINTR)
290 goto again;
291 return -1;
292 }
293 if (ret != pid)
294 goto again;
295 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
296 return -1;
297 return 0;
298 }
299
300 int lxc_wait_for_pid_status(pid_t pid)
301 {
302 int status, ret;
303
304 again:
305 ret = waitpid(pid, &status, 0);
306 if (ret == -1) {
307 if (errno == EINTR)
308 goto again;
309 return -1;
310 }
311 if (ret != pid)
312 goto again;
313 return status;
314 }
315
316 int lxc_safe_uint(const char *numstr, unsigned int *converted)
317 {
318 char *err = NULL;
319 unsigned long int uli;
320
321 while (isspace(*numstr))
322 numstr++;
323
324 if (*numstr == '-')
325 return -EINVAL;
326
327 errno = 0;
328 uli = strtoul(numstr, &err, 0);
329 if (errno == ERANGE && uli == ULONG_MAX)
330 return -ERANGE;
331
332 if (err == numstr || *err != '\0')
333 return -EINVAL;
334
335 if (uli > UINT_MAX)
336 return -ERANGE;
337
338 *converted = (unsigned int)uli;
339 return 0;
340 }
341
342 int lxc_safe_int(const char *numstr, int *converted)
343 {
344 char *err = NULL;
345 signed long int sli;
346
347 errno = 0;
348 sli = strtol(numstr, &err, 0);
349 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
350 return -ERANGE;
351
352 if (errno != 0 && sli == 0)
353 return -EINVAL;
354
355 if (err == numstr || *err != '\0')
356 return -EINVAL;
357
358 if (sli > INT_MAX || sli < INT_MIN)
359 return -ERANGE;
360
361 *converted = (int)sli;
362 return 0;
363 }
364
365 int lxc_safe_long(const char *numstr, long int *converted)
366 {
367 char *err = NULL;
368 signed long int sli;
369
370 errno = 0;
371 sli = strtol(numstr, &err, 0);
372 if (errno == ERANGE && (sli == LONG_MAX || sli == LONG_MIN))
373 return -ERANGE;
374
375 if (errno != 0 && sli == 0)
376 return -EINVAL;
377
378 if (err == numstr || *err != '\0')
379 return -EINVAL;
380
381 *converted = sli;
382 return 0;
383 }
384
385 void lxc_free_array(void **array, lxc_free_fn element_free_fn)
386 {
387 void **p;
388 for (p = array; p && *p; p++)
389 element_free_fn(*p);
390 free((void*)array);
391 }
392
393 int lxc_grow_array(void ***array, size_t* capacity, size_t new_size, size_t capacity_increment)
394 {
395 size_t new_capacity;
396 void **new_array;
397
398 /* first time around, catch some trivial mistakes of the user
399 * only initializing one of these */
400 if (!*array || !*capacity) {
401 *array = NULL;
402 *capacity = 0;
403 }
404
405 new_capacity = *capacity;
406 while (new_size + 1 > new_capacity)
407 new_capacity += capacity_increment;
408 if (new_capacity != *capacity) {
409 /* we have to reallocate */
410 new_array = realloc(*array, new_capacity * sizeof(void *));
411 if (!new_array)
412 return -1;
413 memset(&new_array[*capacity], 0, (new_capacity - (*capacity)) * sizeof(void *));
414 *array = new_array;
415 *capacity = new_capacity;
416 }
417
418 /* array has sufficient elements */
419 return 0;
420 }
421
422 char **lxc_string_split(const char *string, char _sep)
423 {
424 char *token, *str, *saveptr = NULL;
425 char sep[2] = {_sep, '\0'};
426 char **tmp = NULL, **result = NULL;
427 size_t result_capacity = 0;
428 size_t result_count = 0;
429 int r, saved_errno;
430
431 if (!string)
432 return calloc(1, sizeof(char *));
433
434 str = alloca(strlen(string) + 1);
435 strcpy(str, string);
436 for (; (token = strtok_r(str, sep, &saveptr)); str = NULL) {
437 r = lxc_grow_array((void ***)&result, &result_capacity, result_count + 1, 16);
438 if (r < 0)
439 goto error_out;
440 result[result_count] = strdup(token);
441 if (!result[result_count])
442 goto error_out;
443 result_count++;
444 }
445
446 /* if we allocated too much, reduce it */
447 tmp = realloc(result, (result_count + 1) * sizeof(char *));
448 if (!tmp)
449 goto error_out;
450 result = tmp;
451 /* Make sure we don't return uninitialized memory. */
452 if (result_count == 0)
453 *result = NULL;
454 return result;
455 error_out:
456 saved_errno = errno;
457 lxc_free_array((void **)result, free);
458 errno = saved_errno;
459 return NULL;
460 }
461
462 char **lxc_normalize_path(const char *path)
463 {
464 char **components;
465 char **p;
466 size_t components_len = 0;
467 size_t pos = 0;
468
469 components = lxc_string_split(path, '/');
470 if (!components)
471 return NULL;
472 for (p = components; *p; p++)
473 components_len++;
474
475 /* resolve '.' and '..' */
476 for (pos = 0; pos < components_len; ) {
477 if (!strcmp(components[pos], ".") || (!strcmp(components[pos], "..") && pos == 0)) {
478 /* eat this element */
479 free(components[pos]);
480 memmove(&components[pos], &components[pos+1], sizeof(char *) * (components_len - pos));
481 components_len--;
482 } else if (!strcmp(components[pos], "..")) {
483 /* eat this and the previous element */
484 free(components[pos - 1]);
485 free(components[pos]);
486 memmove(&components[pos-1], &components[pos+1], sizeof(char *) * (components_len - pos));
487 components_len -= 2;
488 pos--;
489 } else {
490 pos++;
491 }
492 }
493
494 return components;
495 }
496
497 char *lxc_string_join(const char *sep, const char **parts, bool use_as_prefix)
498 {
499 char *result;
500 char **p;
501 size_t sep_len = strlen(sep);
502 size_t result_len = use_as_prefix * sep_len;
503
504 /* calculate new string length */
505 for (p = (char **)parts; *p; p++)
506 result_len += (p > (char **)parts) * sep_len + strlen(*p);
507
508 result = calloc(result_len + 1, 1);
509 if (!result)
510 return NULL;
511
512 if (use_as_prefix)
513 strcpy(result, sep);
514 for (p = (char **)parts; *p; p++) {
515 if (p > (char **)parts)
516 strcat(result, sep);
517 strcat(result, *p);
518 }
519
520 return result;
521 }
522
523 int is_dir(const char *path)
524 {
525 struct stat statbuf;
526 int ret = stat(path, &statbuf);
527 if (ret == 0 && S_ISDIR(statbuf.st_mode))
528 return 1;
529 return 0;
530 }
531
532 size_t lxc_array_len(void **array)
533 {
534 void **p;
535 size_t result = 0;
536
537 for (p = array; p && *p; p++)
538 result++;
539
540 return result;
541 }