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