]>
Commit | Line | Data |
---|---|---|
f757855e LP |
1 | /*** |
2 | This file is part of systemd. | |
3 | ||
4 | Copyright 2015 Lennart Poettering | |
5 | ||
6 | systemd is free software; you can redistribute it and/or modify it | |
7 | under the terms of the GNU Lesser General Public License as published by | |
8 | the Free Software Foundation; either version 2.1 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | systemd is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | Lesser General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU Lesser General Public License | |
17 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
18 | ***/ | |
19 | ||
b5efdb8a | 20 | #include "alloc-util.h" |
f757855e | 21 | #include "cap-list.h" |
7b3e062c | 22 | #include "conf-parser.h" |
f6d6bad1 | 23 | #include "nspawn-network.h" |
f757855e | 24 | #include "nspawn-settings.h" |
7732f92b | 25 | #include "parse-util.h" |
7b3e062c | 26 | #include "process-util.h" |
22b28dfd LP |
27 | #include "socket-util.h" |
28 | #include "string-util.h" | |
7b3e062c | 29 | #include "strv.h" |
0de7acce | 30 | #include "user-util.h" |
7b3e062c | 31 | #include "util.h" |
f757855e LP |
32 | |
33 | int settings_load(FILE *f, const char *path, Settings **ret) { | |
34 | _cleanup_(settings_freep) Settings *s = NULL; | |
35 | int r; | |
36 | ||
37 | assert(path); | |
38 | assert(ret); | |
39 | ||
40 | s = new0(Settings, 1); | |
41 | if (!s) | |
42 | return -ENOMEM; | |
43 | ||
7732f92b | 44 | s->start_mode = _START_MODE_INVALID; |
f757855e | 45 | s->personality = PERSONALITY_INVALID; |
0de7acce LP |
46 | s->userns_mode = _USER_NAMESPACE_MODE_INVALID; |
47 | s->uid_shift = UID_INVALID; | |
48 | s->uid_range = UID_INVALID; | |
f757855e LP |
49 | |
50 | s->read_only = -1; | |
51 | s->volatile_mode = _VOLATILE_MODE_INVALID; | |
0de7acce | 52 | s->userns_chown = -1; |
f757855e LP |
53 | |
54 | s->private_network = -1; | |
55 | s->network_veth = -1; | |
56 | ||
57 | r = config_parse(NULL, path, f, | |
58 | "Exec\0" | |
59 | "Network\0" | |
60 | "Files\0", | |
61 | config_item_perf_lookup, nspawn_gperf_lookup, | |
bcde742e | 62 | CONFIG_PARSE_WARN, |
f757855e LP |
63 | s); |
64 | if (r < 0) | |
65 | return r; | |
66 | ||
0de7acce LP |
67 | /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either |
68 | * both fields shall be initialized or neither. */ | |
69 | if (s->userns_mode == USER_NAMESPACE_PICK) | |
70 | s->userns_chown = true; | |
71 | else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0) | |
72 | s->userns_chown = false; | |
73 | ||
74 | if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID) | |
75 | s->userns_mode = USER_NAMESPACE_NO; | |
76 | ||
f757855e LP |
77 | *ret = s; |
78 | s = NULL; | |
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); | |
b53ede69 PW |
91 | free(s->pivot_root_new); |
92 | free(s->pivot_root_old); | |
5f932eb9 | 93 | free(s->working_directory); |
960e4569 LP |
94 | strv_free(s->syscall_whitelist); |
95 | strv_free(s->syscall_blacklist); | |
f757855e LP |
96 | |
97 | strv_free(s->network_interfaces); | |
98 | strv_free(s->network_macvlan); | |
99 | strv_free(s->network_ipvlan); | |
f6d6bad1 | 100 | strv_free(s->network_veth_extra); |
f757855e | 101 | free(s->network_bridge); |
22b28dfd | 102 | free(s->network_zone); |
f757855e LP |
103 | expose_port_free_all(s->expose_ports); |
104 | ||
105 | custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); | |
6b430fdb | 106 | return mfree(s); |
f757855e LP |
107 | } |
108 | ||
0e265674 LP |
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 || | |
22b28dfd | 116 | s->network_zone || |
0e265674 LP |
117 | s->network_interfaces || |
118 | s->network_macvlan || | |
f6d6bad1 LP |
119 | s->network_ipvlan || |
120 | s->network_veth_extra; | |
0e265674 LP |
121 | } |
122 | ||
123 | bool settings_network_veth(Settings *s) { | |
124 | assert(s); | |
125 | ||
126 | return | |
127 | s->network_veth > 0 || | |
22b28dfd LP |
128 | s->network_bridge || |
129 | s->network_zone; | |
0e265674 LP |
130 | } |
131 | ||
f757855e LP |
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) { | |
12ca818f | 199 | log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability, ignoring: %s", word); |
f757855e LP |
200 | continue; |
201 | } | |
202 | ||
1898e5f9 | 203 | u |= UINT64_C(1) << cap; |
f757855e LP |
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 | ||
b53ede69 PW |
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 | ||
f757855e LP |
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 | ||
f6d6bad1 LP |
323 | return 0; |
324 | } | |
f757855e | 325 | |
7b4318b6 LP |
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 | ||
f6d6bad1 LP |
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 | } | |
f757855e LP |
376 | |
377 | return 0; | |
378 | } | |
7732f92b | 379 | |
22b28dfd LP |
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(settings->network_zone); | |
406 | settings->network_zone = j; | |
407 | j = NULL; | |
408 | ||
409 | return 0; | |
410 | } | |
411 | ||
7732f92b LP |
412 | int config_parse_boot( |
413 | const char *unit, | |
414 | const char *filename, | |
415 | unsigned line, | |
416 | const char *section, | |
417 | unsigned section_line, | |
418 | const char *lvalue, | |
419 | int ltype, | |
420 | const char *rvalue, | |
421 | void *data, | |
422 | void *userdata) { | |
423 | ||
424 | Settings *settings = data; | |
425 | int r; | |
426 | ||
427 | assert(filename); | |
428 | assert(lvalue); | |
429 | assert(rvalue); | |
430 | ||
431 | r = parse_boolean(rvalue); | |
432 | if (r < 0) { | |
433 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue); | |
434 | return 0; | |
435 | } | |
436 | ||
437 | if (r > 0) { | |
438 | if (settings->start_mode == START_PID2) | |
439 | goto conflict; | |
440 | ||
441 | settings->start_mode = START_BOOT; | |
442 | } else { | |
443 | if (settings->start_mode == START_BOOT) | |
444 | goto conflict; | |
445 | ||
446 | if (settings->start_mode < 0) | |
447 | settings->start_mode = START_PID1; | |
448 | } | |
449 | ||
450 | return 0; | |
451 | ||
452 | conflict: | |
453 | log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); | |
454 | return 0; | |
455 | } | |
456 | ||
457 | int config_parse_pid2( | |
458 | const char *unit, | |
459 | const char *filename, | |
460 | unsigned line, | |
461 | const char *section, | |
462 | unsigned section_line, | |
463 | const char *lvalue, | |
464 | int ltype, | |
465 | const char *rvalue, | |
466 | void *data, | |
467 | void *userdata) { | |
468 | ||
469 | Settings *settings = data; | |
470 | int r; | |
471 | ||
472 | assert(filename); | |
473 | assert(lvalue); | |
474 | assert(rvalue); | |
475 | ||
476 | r = parse_boolean(rvalue); | |
477 | if (r < 0) { | |
478 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue); | |
479 | return 0; | |
480 | } | |
481 | ||
482 | if (r > 0) { | |
483 | if (settings->start_mode == START_BOOT) | |
484 | goto conflict; | |
485 | ||
486 | settings->start_mode = START_PID2; | |
487 | } else { | |
488 | if (settings->start_mode == START_PID2) | |
489 | goto conflict; | |
490 | ||
491 | if (settings->start_mode < 0) | |
492 | settings->start_mode = START_PID1; | |
493 | } | |
494 | ||
495 | return 0; | |
496 | ||
497 | conflict: | |
498 | log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); | |
499 | return 0; | |
500 | } | |
0de7acce LP |
501 | |
502 | int config_parse_private_users( | |
503 | const char *unit, | |
504 | const char *filename, | |
505 | unsigned line, | |
506 | const char *section, | |
507 | unsigned section_line, | |
508 | const char *lvalue, | |
509 | int ltype, | |
510 | const char *rvalue, | |
511 | void *data, | |
512 | void *userdata) { | |
513 | ||
514 | Settings *settings = data; | |
515 | int r; | |
516 | ||
517 | assert(filename); | |
518 | assert(lvalue); | |
519 | assert(rvalue); | |
520 | ||
521 | r = parse_boolean(rvalue); | |
522 | if (r == 0) { | |
523 | /* no: User namespacing off */ | |
524 | settings->userns_mode = USER_NAMESPACE_NO; | |
525 | settings->uid_shift = UID_INVALID; | |
526 | settings->uid_range = UINT32_C(0x10000); | |
527 | } else if (r > 0) { | |
528 | /* yes: User namespacing on, UID range is read from root dir */ | |
529 | settings->userns_mode = USER_NAMESPACE_FIXED; | |
530 | settings->uid_shift = UID_INVALID; | |
531 | settings->uid_range = UINT32_C(0x10000); | |
532 | } else if (streq(rvalue, "pick")) { | |
533 | /* pick: User namespacing on, UID range is picked randomly */ | |
534 | settings->userns_mode = USER_NAMESPACE_PICK; | |
535 | settings->uid_shift = UID_INVALID; | |
536 | settings->uid_range = UINT32_C(0x10000); | |
537 | } else { | |
538 | const char *range, *shift; | |
539 | uid_t sh, rn; | |
540 | ||
541 | /* anything else: User namespacing on, UID range is explicitly configured */ | |
542 | ||
543 | range = strchr(rvalue, ':'); | |
544 | if (range) { | |
545 | shift = strndupa(rvalue, range - rvalue); | |
546 | range++; | |
547 | ||
548 | r = safe_atou32(range, &rn); | |
549 | if (r < 0 || rn <= 0) { | |
550 | log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range); | |
551 | return 0; | |
552 | } | |
553 | } else { | |
554 | shift = rvalue; | |
555 | rn = UINT32_C(0x10000); | |
556 | } | |
557 | ||
558 | r = parse_uid(shift, &sh); | |
559 | if (r < 0) { | |
560 | log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range); | |
561 | return 0; | |
562 | } | |
563 | ||
564 | settings->userns_mode = USER_NAMESPACE_FIXED; | |
565 | settings->uid_shift = sh; | |
566 | settings->uid_range = rn; | |
567 | } | |
568 | ||
569 | return 0; | |
570 | } | |
960e4569 LP |
571 | |
572 | int config_parse_syscall_filter( | |
573 | const char *unit, | |
574 | const char *filename, | |
575 | unsigned line, | |
576 | const char *section, | |
577 | unsigned section_line, | |
578 | const char *lvalue, | |
579 | int ltype, | |
580 | const char *rvalue, | |
581 | void *data, | |
582 | void *userdata) { | |
583 | ||
584 | Settings *settings = data; | |
585 | bool negative; | |
586 | const char *items; | |
587 | int r; | |
588 | ||
589 | assert(filename); | |
590 | assert(lvalue); | |
591 | assert(rvalue); | |
592 | ||
593 | negative = rvalue[0] == '~'; | |
594 | items = negative ? rvalue + 1 : rvalue; | |
595 | ||
596 | for (;;) { | |
597 | _cleanup_free_ char *word = NULL; | |
598 | ||
599 | r = extract_first_word(&items, &word, NULL, 0); | |
600 | if (r == 0) | |
601 | break; | |
602 | if (r == -ENOMEM) | |
603 | return log_oom(); | |
604 | if (r < 0) { | |
605 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse SystemCallFilter= parameter %s, ignoring: %m", rvalue); | |
606 | return 0; | |
607 | } | |
608 | ||
609 | if (negative) | |
610 | r = strv_extend(&settings->syscall_blacklist, word); | |
611 | else | |
612 | r = strv_extend(&settings->syscall_whitelist, word); | |
613 | if (r < 0) | |
614 | return log_oom(); | |
615 | } | |
616 | ||
617 | return 0; | |
618 | } |