]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nspawn/nspawn-settings.c
nspawn: Fix calculation of capabilities for configuration file (#7087)
[thirdparty/systemd.git] / src / nspawn / nspawn-settings.c
CommitLineData
f757855e
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2015 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
b5efdb8a 20#include "alloc-util.h"
f757855e 21#include "cap-list.h"
7b3e062c 22#include "conf-parser.h"
f6d6bad1 23#include "nspawn-network.h"
f757855e 24#include "nspawn-settings.h"
7732f92b 25#include "parse-util.h"
7b3e062c 26#include "process-util.h"
22b28dfd
LP
27#include "socket-util.h"
28#include "string-util.h"
7b3e062c 29#include "strv.h"
0de7acce 30#include "user-util.h"
7b3e062c 31#include "util.h"
f757855e
LP
32
33int settings_load(FILE *f, const char *path, Settings **ret) {
34 _cleanup_(settings_freep) Settings *s = NULL;
35 int r;
36
37 assert(path);
38 assert(ret);
39
40 s = new0(Settings, 1);
41 if (!s)
42 return -ENOMEM;
43
7732f92b 44 s->start_mode = _START_MODE_INVALID;
f757855e 45 s->personality = PERSONALITY_INVALID;
0de7acce
LP
46 s->userns_mode = _USER_NAMESPACE_MODE_INVALID;
47 s->uid_shift = UID_INVALID;
48 s->uid_range = UID_INVALID;
f757855e
LP
49
50 s->read_only = -1;
51 s->volatile_mode = _VOLATILE_MODE_INVALID;
0de7acce 52 s->userns_chown = -1;
f757855e
LP
53
54 s->private_network = -1;
55 s->network_veth = -1;
56
57 r = config_parse(NULL, path, f,
58 "Exec\0"
59 "Network\0"
60 "Files\0",
61 config_item_perf_lookup, nspawn_gperf_lookup,
62 false,
63 false,
64 true,
65 s);
66 if (r < 0)
67 return r;
68
0de7acce
LP
69 /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
70 * both fields shall be initialized or neither. */
71 if (s->userns_mode == USER_NAMESPACE_PICK)
72 s->userns_chown = true;
73 else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
74 s->userns_chown = false;
75
76 if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
77 s->userns_mode = USER_NAMESPACE_NO;
78
f757855e
LP
79 *ret = s;
80 s = NULL;
81
82 return 0;
83}
84
85Settings* settings_free(Settings *s) {
86
87 if (!s)
88 return NULL;
89
90 strv_free(s->parameters);
91 strv_free(s->environment);
92 free(s->user);
b53ede69
PW
93 free(s->pivot_root_new);
94 free(s->pivot_root_old);
5f932eb9 95 free(s->working_directory);
960e4569
LP
96 strv_free(s->syscall_whitelist);
97 strv_free(s->syscall_blacklist);
f757855e
LP
98
99 strv_free(s->network_interfaces);
100 strv_free(s->network_macvlan);
101 strv_free(s->network_ipvlan);
f6d6bad1 102 strv_free(s->network_veth_extra);
f757855e 103 free(s->network_bridge);
22b28dfd 104 free(s->network_zone);
f757855e
LP
105 expose_port_free_all(s->expose_ports);
106
107 custom_mount_free_all(s->custom_mounts, s->n_custom_mounts);
6b430fdb 108 return mfree(s);
f757855e
LP
109}
110
0e265674
LP
111bool settings_private_network(Settings *s) {
112 assert(s);
113
114 return
115 s->private_network > 0 ||
116 s->network_veth > 0 ||
117 s->network_bridge ||
22b28dfd 118 s->network_zone ||
0e265674
LP
119 s->network_interfaces ||
120 s->network_macvlan ||
f6d6bad1
LP
121 s->network_ipvlan ||
122 s->network_veth_extra;
0e265674
LP
123}
124
125bool settings_network_veth(Settings *s) {
126 assert(s);
127
128 return
129 s->network_veth > 0 ||
22b28dfd
LP
130 s->network_bridge ||
131 s->network_zone;
0e265674
LP
132}
133
f757855e
LP
134DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode");
135
136int config_parse_expose_port(
137 const char *unit,
138 const char *filename,
139 unsigned line,
140 const char *section,
141 unsigned section_line,
142 const char *lvalue,
143 int ltype,
144 const char *rvalue,
145 void *data,
146 void *userdata) {
147
148 Settings *s = data;
149 int r;
150
151 assert(filename);
152 assert(lvalue);
153 assert(rvalue);
154
155 r = expose_port_parse(&s->expose_ports, rvalue);
156 if (r == -EEXIST) {
157 log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue);
158 return 0;
159 }
160 if (r < 0) {
161 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue);
162 return 0;
163 }
164
165 return 0;
166}
167
168int config_parse_capability(
169 const char *unit,
170 const char *filename,
171 unsigned line,
172 const char *section,
173 unsigned section_line,
174 const char *lvalue,
175 int ltype,
176 const char *rvalue,
177 void *data,
178 void *userdata) {
179
180 uint64_t u = 0, *result = data;
181 int r;
182
183 assert(filename);
184 assert(lvalue);
185 assert(rvalue);
186
187 for (;;) {
188 _cleanup_free_ char *word = NULL;
189 int cap;
190
191 r = extract_first_word(&rvalue, &word, NULL, 0);
192 if (r < 0) {
193 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue);
194 return 0;
195 }
196 if (r == 0)
197 break;
198
199 cap = capability_from_name(word);
200 if (cap < 0) {
12ca818f 201 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word);
f757855e
LP
202 continue;
203 }
204
1898e5f9 205 u |= UINT64_C(1) << cap;
f757855e
LP
206 }
207
208 if (u == 0)
209 return 0;
210
211 *result |= u;
212 return 0;
213}
214
215int config_parse_id128(
216 const char *unit,
217 const char *filename,
218 unsigned line,
219 const char *section,
220 unsigned section_line,
221 const char *lvalue,
222 int ltype,
223 const char *rvalue,
224 void *data,
225 void *userdata) {
226
227 sd_id128_t t, *result = data;
228 int r;
229
230 assert(filename);
231 assert(lvalue);
232 assert(rvalue);
233
234 r = sd_id128_from_string(rvalue, &t);
235 if (r < 0) {
236 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
237 return 0;
238 }
239
240 *result = t;
241 return 0;
242}
243
b53ede69
PW
244int config_parse_pivot_root(
245 const char *unit,
246 const char *filename,
247 unsigned line,
248 const char *section,
249 unsigned section_line,
250 const char *lvalue,
251 int ltype,
252 const char *rvalue,
253 void *data,
254 void *userdata) {
255
256 Settings *settings = data;
257 int r;
258
259 assert(filename);
260 assert(lvalue);
261 assert(rvalue);
262
263 r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue);
264 if (r < 0) {
265 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue);
266 return 0;
267 }
268
269 return 0;
270}
271
f757855e
LP
272int config_parse_bind(
273 const char *unit,
274 const char *filename,
275 unsigned line,
276 const char *section,
277 unsigned section_line,
278 const char *lvalue,
279 int ltype,
280 const char *rvalue,
281 void *data,
282 void *userdata) {
283
284 Settings *settings = data;
285 int r;
286
287 assert(filename);
288 assert(lvalue);
289 assert(rvalue);
290
291 r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
292 if (r < 0) {
293 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue);
294 return 0;
295 }
296
297 return 0;
298}
299
300int config_parse_tmpfs(
301 const char *unit,
302 const char *filename,
303 unsigned line,
304 const char *section,
305 unsigned section_line,
306 const char *lvalue,
307 int ltype,
308 const char *rvalue,
309 void *data,
310 void *userdata) {
311
312 Settings *settings = data;
313 int r;
314
315 assert(filename);
316 assert(lvalue);
317 assert(rvalue);
318
319 r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue);
320 if (r < 0) {
321 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue);
322 return 0;
323 }
324
f6d6bad1
LP
325 return 0;
326}
f757855e 327
7b4318b6
LP
328int config_parse_overlay(
329 const char *unit,
330 const char *filename,
331 unsigned line,
332 const char *section,
333 unsigned section_line,
334 const char *lvalue,
335 int ltype,
336 const char *rvalue,
337 void *data,
338 void *userdata) {
339
340 Settings *settings = data;
341 int r;
342
343 assert(filename);
344 assert(lvalue);
345 assert(rvalue);
346
347 r = overlay_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype);
348 if (r < 0)
349 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid overlay file system specification %s, ignoring: %m", rvalue);
350
351 return 0;
352}
353
f6d6bad1
LP
354int config_parse_veth_extra(
355 const char *unit,
356 const char *filename,
357 unsigned line,
358 const char *section,
359 unsigned section_line,
360 const char *lvalue,
361 int ltype,
362 const char *rvalue,
363 void *data,
364 void *userdata) {
365
366 Settings *settings = data;
367 int r;
368
369 assert(filename);
370 assert(lvalue);
371 assert(rvalue);
372
373 r = veth_extra_parse(&settings->network_veth_extra, rvalue);
374 if (r < 0) {
375 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
376 return 0;
377 }
f757855e
LP
378
379 return 0;
380}
7732f92b 381
22b28dfd
LP
382int config_parse_network_zone(
383 const char *unit,
384 const char *filename,
385 unsigned line,
386 const char *section,
387 unsigned section_line,
388 const char *lvalue,
389 int ltype,
390 const char *rvalue,
391 void *data,
392 void *userdata) {
393
394 Settings *settings = data;
395 _cleanup_free_ char *j = NULL;
396
397 assert(filename);
398 assert(lvalue);
399 assert(rvalue);
400
401 j = strappend("vz-", rvalue);
402 if (!ifname_valid(j)) {
403 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue);
404 return 0;
405 }
406
407 free(settings->network_zone);
408 settings->network_zone = j;
409 j = NULL;
410
411 return 0;
412}
413
7732f92b
LP
414int config_parse_boot(
415 const char *unit,
416 const char *filename,
417 unsigned line,
418 const char *section,
419 unsigned section_line,
420 const char *lvalue,
421 int ltype,
422 const char *rvalue,
423 void *data,
424 void *userdata) {
425
426 Settings *settings = data;
427 int r;
428
429 assert(filename);
430 assert(lvalue);
431 assert(rvalue);
432
433 r = parse_boolean(rvalue);
434 if (r < 0) {
435 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
436 return 0;
437 }
438
439 if (r > 0) {
440 if (settings->start_mode == START_PID2)
441 goto conflict;
442
443 settings->start_mode = START_BOOT;
444 } else {
445 if (settings->start_mode == START_BOOT)
446 goto conflict;
447
448 if (settings->start_mode < 0)
449 settings->start_mode = START_PID1;
450 }
451
452 return 0;
453
454conflict:
455 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
456 return 0;
457}
458
459int config_parse_pid2(
460 const char *unit,
461 const char *filename,
462 unsigned line,
463 const char *section,
464 unsigned section_line,
465 const char *lvalue,
466 int ltype,
467 const char *rvalue,
468 void *data,
469 void *userdata) {
470
471 Settings *settings = data;
472 int r;
473
474 assert(filename);
475 assert(lvalue);
476 assert(rvalue);
477
478 r = parse_boolean(rvalue);
479 if (r < 0) {
480 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
481 return 0;
482 }
483
484 if (r > 0) {
485 if (settings->start_mode == START_BOOT)
486 goto conflict;
487
488 settings->start_mode = START_PID2;
489 } else {
490 if (settings->start_mode == START_PID2)
491 goto conflict;
492
493 if (settings->start_mode < 0)
494 settings->start_mode = START_PID1;
495 }
496
497 return 0;
498
499conflict:
500 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
501 return 0;
502}
0de7acce
LP
503
504int config_parse_private_users(
505 const char *unit,
506 const char *filename,
507 unsigned line,
508 const char *section,
509 unsigned section_line,
510 const char *lvalue,
511 int ltype,
512 const char *rvalue,
513 void *data,
514 void *userdata) {
515
516 Settings *settings = data;
517 int r;
518
519 assert(filename);
520 assert(lvalue);
521 assert(rvalue);
522
523 r = parse_boolean(rvalue);
524 if (r == 0) {
525 /* no: User namespacing off */
526 settings->userns_mode = USER_NAMESPACE_NO;
527 settings->uid_shift = UID_INVALID;
528 settings->uid_range = UINT32_C(0x10000);
529 } else if (r > 0) {
530 /* yes: User namespacing on, UID range is read from root dir */
531 settings->userns_mode = USER_NAMESPACE_FIXED;
532 settings->uid_shift = UID_INVALID;
533 settings->uid_range = UINT32_C(0x10000);
534 } else if (streq(rvalue, "pick")) {
535 /* pick: User namespacing on, UID range is picked randomly */
536 settings->userns_mode = USER_NAMESPACE_PICK;
537 settings->uid_shift = UID_INVALID;
538 settings->uid_range = UINT32_C(0x10000);
539 } else {
540 const char *range, *shift;
541 uid_t sh, rn;
542
543 /* anything else: User namespacing on, UID range is explicitly configured */
544
545 range = strchr(rvalue, ':');
546 if (range) {
547 shift = strndupa(rvalue, range - rvalue);
548 range++;
549
550 r = safe_atou32(range, &rn);
551 if (r < 0 || rn <= 0) {
552 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
553 return 0;
554 }
555 } else {
556 shift = rvalue;
557 rn = UINT32_C(0x10000);
558 }
559
560 r = parse_uid(shift, &sh);
561 if (r < 0) {
562 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
563 return 0;
564 }
565
566 settings->userns_mode = USER_NAMESPACE_FIXED;
567 settings->uid_shift = sh;
568 settings->uid_range = rn;
569 }
570
571 return 0;
572}
960e4569
LP
573
574int config_parse_syscall_filter(
575 const char *unit,
576 const char *filename,
577 unsigned line,
578 const char *section,
579 unsigned section_line,
580 const char *lvalue,
581 int ltype,
582 const char *rvalue,
583 void *data,
584 void *userdata) {
585
586 Settings *settings = data;
587 bool negative;
588 const char *items;
589 int r;
590
591 assert(filename);
592 assert(lvalue);
593 assert(rvalue);
594
595 negative = rvalue[0] == '~';
596 items = negative ? rvalue + 1 : rvalue;
597
598 for (;;) {
599 _cleanup_free_ char *word = NULL;
600
601 r = extract_first_word(&items, &word, NULL, 0);
602 if (r == 0)
603 break;
604 if (r == -ENOMEM)
605 return log_oom();
606 if (r < 0) {
607 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
608 return 0;
609 }
610
611 if (negative)
612 r = strv_extend(&settings->syscall_blacklist, word);
613 else
614 r = strv_extend(&settings->syscall_whitelist, word);
615 if (r < 0)
616 return log_oom();
617 }
618
619 return 0;
620}