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