]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-settings.c
tools/hwdb-update: allow downloads to fail
[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_and_replace(settings->network_zone, j);
407
408 return 0;
409 }
410
411 int config_parse_boot(
412 const char *unit,
413 const char *filename,
414 unsigned line,
415 const char *section,
416 unsigned section_line,
417 const char *lvalue,
418 int ltype,
419 const char *rvalue,
420 void *data,
421 void *userdata) {
422
423 Settings *settings = data;
424 int r;
425
426 assert(filename);
427 assert(lvalue);
428 assert(rvalue);
429
430 r = parse_boolean(rvalue);
431 if (r < 0) {
432 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue);
433 return 0;
434 }
435
436 if (r > 0) {
437 if (settings->start_mode == START_PID2)
438 goto conflict;
439
440 settings->start_mode = START_BOOT;
441 } else {
442 if (settings->start_mode == START_BOOT)
443 goto conflict;
444
445 if (settings->start_mode < 0)
446 settings->start_mode = START_PID1;
447 }
448
449 return 0;
450
451 conflict:
452 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
453 return 0;
454 }
455
456 int config_parse_pid2(
457 const char *unit,
458 const char *filename,
459 unsigned line,
460 const char *section,
461 unsigned section_line,
462 const char *lvalue,
463 int ltype,
464 const char *rvalue,
465 void *data,
466 void *userdata) {
467
468 Settings *settings = data;
469 int r;
470
471 assert(filename);
472 assert(lvalue);
473 assert(rvalue);
474
475 r = parse_boolean(rvalue);
476 if (r < 0) {
477 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue);
478 return 0;
479 }
480
481 if (r > 0) {
482 if (settings->start_mode == START_BOOT)
483 goto conflict;
484
485 settings->start_mode = START_PID2;
486 } else {
487 if (settings->start_mode == START_PID2)
488 goto conflict;
489
490 if (settings->start_mode < 0)
491 settings->start_mode = START_PID1;
492 }
493
494 return 0;
495
496 conflict:
497 log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring.");
498 return 0;
499 }
500
501 int config_parse_private_users(
502 const char *unit,
503 const char *filename,
504 unsigned line,
505 const char *section,
506 unsigned section_line,
507 const char *lvalue,
508 int ltype,
509 const char *rvalue,
510 void *data,
511 void *userdata) {
512
513 Settings *settings = data;
514 int r;
515
516 assert(filename);
517 assert(lvalue);
518 assert(rvalue);
519
520 r = parse_boolean(rvalue);
521 if (r == 0) {
522 /* no: User namespacing off */
523 settings->userns_mode = USER_NAMESPACE_NO;
524 settings->uid_shift = UID_INVALID;
525 settings->uid_range = UINT32_C(0x10000);
526 } else if (r > 0) {
527 /* yes: User namespacing on, UID range is read from root dir */
528 settings->userns_mode = USER_NAMESPACE_FIXED;
529 settings->uid_shift = UID_INVALID;
530 settings->uid_range = UINT32_C(0x10000);
531 } else if (streq(rvalue, "pick")) {
532 /* pick: User namespacing on, UID range is picked randomly */
533 settings->userns_mode = USER_NAMESPACE_PICK;
534 settings->uid_shift = UID_INVALID;
535 settings->uid_range = UINT32_C(0x10000);
536 } else {
537 const char *range, *shift;
538 uid_t sh, rn;
539
540 /* anything else: User namespacing on, UID range is explicitly configured */
541
542 range = strchr(rvalue, ':');
543 if (range) {
544 shift = strndupa(rvalue, range - rvalue);
545 range++;
546
547 r = safe_atou32(range, &rn);
548 if (r < 0 || rn <= 0) {
549 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range);
550 return 0;
551 }
552 } else {
553 shift = rvalue;
554 rn = UINT32_C(0x10000);
555 }
556
557 r = parse_uid(shift, &sh);
558 if (r < 0) {
559 log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range);
560 return 0;
561 }
562
563 settings->userns_mode = USER_NAMESPACE_FIXED;
564 settings->uid_shift = sh;
565 settings->uid_range = rn;
566 }
567
568 return 0;
569 }
570
571 int config_parse_syscall_filter(
572 const char *unit,
573 const char *filename,
574 unsigned line,
575 const char *section,
576 unsigned section_line,
577 const char *lvalue,
578 int ltype,
579 const char *rvalue,
580 void *data,
581 void *userdata) {
582
583 Settings *settings = data;
584 bool negative;
585 const char *items;
586 int r;
587
588 assert(filename);
589 assert(lvalue);
590 assert(rvalue);
591
592 negative = rvalue[0] == '~';
593 items = negative ? rvalue + 1 : rvalue;
594
595 for (;;) {
596 _cleanup_free_ char *word = NULL;
597
598 r = extract_first_word(&items, &word, NULL, 0);
599 if (r == 0)
600 break;
601 if (r == -ENOMEM)
602 return log_oom();
603 if (r < 0) {
604 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue);
605 return 0;
606 }
607
608 if (negative)
609 r = strv_extend(&settings->syscall_blacklist, word);
610 else
611 r = strv_extend(&settings->syscall_whitelist, word);
612 if (r < 0)
613 return log_oom();
614 }
615
616 return 0;
617 }