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