]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
f757855e LP |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2015 Lennart Poettering | |
f757855e LP |
6 | ***/ |
7 | ||
b5efdb8a | 8 | #include "alloc-util.h" |
f757855e | 9 | #include "cap-list.h" |
7b3e062c | 10 | #include "conf-parser.h" |
f6d6bad1 | 11 | #include "nspawn-network.h" |
f757855e | 12 | #include "nspawn-settings.h" |
7732f92b | 13 | #include "parse-util.h" |
7b3e062c | 14 | #include "process-util.h" |
22b28dfd LP |
15 | #include "socket-util.h" |
16 | #include "string-util.h" | |
7b3e062c | 17 | #include "strv.h" |
0de7acce | 18 | #include "user-util.h" |
7b3e062c | 19 | #include "util.h" |
f757855e LP |
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 | ||
7732f92b | 32 | s->start_mode = _START_MODE_INVALID; |
f757855e | 33 | s->personality = PERSONALITY_INVALID; |
0de7acce LP |
34 | s->userns_mode = _USER_NAMESPACE_MODE_INVALID; |
35 | s->uid_shift = UID_INVALID; | |
36 | s->uid_range = UID_INVALID; | |
f757855e LP |
37 | |
38 | s->read_only = -1; | |
39 | s->volatile_mode = _VOLATILE_MODE_INVALID; | |
0de7acce | 40 | s->userns_chown = -1; |
f757855e LP |
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, | |
bcde742e | 50 | CONFIG_PARSE_WARN, |
f757855e LP |
51 | s); |
52 | if (r < 0) | |
53 | return r; | |
54 | ||
0de7acce LP |
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 | ||
1cc6c93a | 65 | *ret = TAKE_PTR(s); |
f757855e LP |
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); | |
b53ede69 PW |
78 | free(s->pivot_root_new); |
79 | free(s->pivot_root_old); | |
5f932eb9 | 80 | free(s->working_directory); |
960e4569 LP |
81 | strv_free(s->syscall_whitelist); |
82 | strv_free(s->syscall_blacklist); | |
f757855e LP |
83 | |
84 | strv_free(s->network_interfaces); | |
85 | strv_free(s->network_macvlan); | |
86 | strv_free(s->network_ipvlan); | |
f6d6bad1 | 87 | strv_free(s->network_veth_extra); |
f757855e | 88 | free(s->network_bridge); |
22b28dfd | 89 | free(s->network_zone); |
f757855e LP |
90 | expose_port_free_all(s->expose_ports); |
91 | ||
92 | custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); | |
6b430fdb | 93 | return mfree(s); |
f757855e LP |
94 | } |
95 | ||
0e265674 LP |
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 || | |
22b28dfd | 103 | s->network_zone || |
0e265674 LP |
104 | s->network_interfaces || |
105 | s->network_macvlan || | |
f6d6bad1 LP |
106 | s->network_ipvlan || |
107 | s->network_veth_extra; | |
0e265674 LP |
108 | } |
109 | ||
110 | bool settings_network_veth(Settings *s) { | |
111 | assert(s); | |
112 | ||
113 | return | |
114 | s->network_veth > 0 || | |
22b28dfd LP |
115 | s->network_bridge || |
116 | s->network_zone; | |
0e265674 LP |
117 | } |
118 | ||
f757855e LP |
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) { | |
12ca818f | 186 | log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word); |
f757855e LP |
187 | continue; |
188 | } | |
189 | ||
1898e5f9 | 190 | u |= UINT64_C(1) << cap; |
f757855e LP |
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 | ||
b53ede69 PW |
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 | ||
f757855e LP |
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 | ||
f6d6bad1 LP |
310 | return 0; |
311 | } | |
f757855e | 312 | |
7b4318b6 LP |
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 | ||
f6d6bad1 LP |
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 | } | |
f757855e LP |
363 | |
364 | return 0; | |
365 | } | |
7732f92b | 366 | |
22b28dfd LP |
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 | ||
f9ecfd3b | 392 | free_and_replace(settings->network_zone, j); |
22b28dfd LP |
393 | |
394 | return 0; | |
395 | } | |
396 | ||
7732f92b LP |
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 | } | |
0de7acce LP |
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 | } | |
960e4569 LP |
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 | } |