trace: use standard POSIX header for basename()
[project/procd.git] / jail / netifd.c
1 /*
2 * Copyright (C) 2021 Daniel Golle <daniel@makrotopia.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * launch private ubus and netifd instances for containers with managed
14 * network namespace.
15 */
16
17 #define _GNU_SOURCE /* See feature_test_macros(7) */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <libgen.h>
23 #include <fcntl.h>
24
25 #include <sys/inotify.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28
29 #include <pwd.h>
30
31 #include <linux/limits.h>
32
33 #include <libubox/uloop.h>
34 #include <libubox/utils.h>
35 #include <libubus.h>
36 #include <libubox/blobmsg.h>
37 #include <libubox/blobmsg_json.h>
38 #include <uci.h>
39
40 #include "netifd.h"
41 #include "log.h"
42 #include "jail.h"
43
44 #define INOTIFY_SZ (sizeof(struct inotify_event) + PATH_MAX + 1)
45
46 static const char ubusd_path[] = "/sbin/ubusd";
47 static const char netifd_path[] = "/sbin/netifd";
48 static const char uci_net[] = "network";
49 static const char ubus_sock_name[] = "ubus.sock";
50
51 static char *jail_name, *ubus_sock_path, *ubus_sock_dir, *uci_config_network = NULL;
52
53 static char *inotify_buffer;
54 static struct uloop_fd fd_inotify_read;
55 static struct passwd *ubus_pw;
56 static pid_t ns_pid;
57
58 static struct ubus_context *host_ubus_ctx = NULL;
59 static struct ubus_context *jail_ubus_ctx = NULL;
60
61 static struct ubus_subscriber config_watch_subscribe;
62
63 /* generate /etc/config/network for jail'ed netifd */
64 static int gen_jail_uci_network(void)
65 {
66 struct uci_context *uci_ctx = uci_alloc_context();
67 struct uci_package *pkg = NULL;
68 struct uci_element *e, *t;
69 bool has_loopback = false;
70 int ret = 0;
71 FILE *ucinetf;
72
73 /* if no network configuration is active just return */
74 if (!uci_config_network)
75 goto uci_out;
76
77 /* open output uci network config file */
78 ucinetf = fopen(uci_config_network, "w");
79 if (!ucinetf) {
80 ret = errno;
81 goto uci_out;
82 }
83
84 /* load network uci package */
85 if (uci_load(uci_ctx, uci_net, &pkg) != UCI_OK) {
86 char *err;
87 uci_get_errorstr(uci_ctx, &err, uci_net);
88 fprintf(stderr, "unable to load configuration (%s)\n", err);
89 free(err);
90 ret = EIO;
91 goto ucinetf_out;
92 }
93
94 /* remove all sections which don't match jail */
95 uci_foreach_element_safe(&pkg->sections, t, e) {
96 struct uci_section *s = uci_to_section(e);
97 struct uci_option *o = uci_lookup_option(uci_ctx, s, "jail");
98 struct uci_ptr ptr = { .p = pkg, .s = s };
99
100 /* keep match, but remove 'jail' option and rename 'jail_ifname' */
101 if (o && o->type == UCI_TYPE_STRING && !strcmp(o->v.string, jail_name)) {
102 ptr.o = o;
103 struct uci_option *jio = uci_lookup_option(uci_ctx, s, "jail_device");
104 if (!jio)
105 jio = uci_lookup_option(uci_ctx, s, "jail_ifname");
106
107 if (jio) {
108 struct uci_ptr ren_ptr = { .p = pkg, .s = s, .o = jio, .value = "device" };
109 struct uci_option *host_device = uci_lookup_option(uci_ctx, s, "device");
110 struct uci_option *legacy_ifname = uci_lookup_option(uci_ctx, s, "ifname");
111 if (host_device && legacy_ifname) {
112 struct uci_ptr delif_ptr = { .p = pkg, .s = s, .o = legacy_ifname };
113 uci_delete(uci_ctx, &delif_ptr);
114 }
115
116 struct uci_ptr renif_ptr = { .p = pkg, .s = s, .o = host_device?:legacy_ifname, .value = "host_device" };
117 uci_rename(uci_ctx, &renif_ptr);
118 uci_rename(uci_ctx, &ren_ptr);
119 }
120 }
121
122 uci_delete(uci_ctx, &ptr);
123 }
124
125 /* check if device 'lo' is defined by any remaining interfaces */
126 uci_foreach_element(&pkg->sections, e) {
127 struct uci_section *s = uci_to_section(e);
128 if (strcmp(s->type, "interface"))
129 continue;
130
131 const char *devname = uci_lookup_option_string(uci_ctx, s, "device");
132 if (devname && !strcmp(devname, "lo")) {
133 has_loopback = true;
134 break;
135 }
136 }
137
138 /* create loopback interface section if not defined */
139 if (!has_loopback) {
140 struct uci_ptr ptr = { .p = pkg, .section = "loopback", .value = "interface" };
141 uci_set(uci_ctx, &ptr);
142 uci_reorder_section(uci_ctx, ptr.s, 0);
143 struct uci_ptr ptr1 = { .p = pkg, .s = ptr.s, .option = "device", .value = "lo" };
144 struct uci_ptr ptr2 = { .p = pkg, .s = ptr.s, .option = "proto", .value = "static" };
145 struct uci_ptr ptr3 = { .p = pkg, .s = ptr.s, .option = "ipaddr", .value = "127.0.0.1" };
146 struct uci_ptr ptr4 = { .p = pkg, .s = ptr.s, .option = "netmask", .value = "255.0.0.0" };
147 uci_set(uci_ctx, &ptr1);
148 uci_set(uci_ctx, &ptr2);
149 uci_set(uci_ctx, &ptr3);
150 uci_set(uci_ctx, &ptr4);
151 }
152
153 ret = uci_export(uci_ctx, ucinetf, pkg, false);
154
155 ucinetf_out:
156 fclose(ucinetf);
157
158 uci_out:
159 uci_free_context(uci_ctx);
160
161 return ret;
162 }
163
164 static void run_ubusd(struct uloop_timeout *t)
165 {
166 static struct blob_buf req;
167 void *ins, *in, *cmd;
168 uint32_t id;
169
170 blob_buf_init(&req, 0);
171 blobmsg_add_string(&req, "name", jail_name);
172 ins = blobmsg_open_table(&req, "instances");
173 in = blobmsg_open_table(&req, "ubus");
174 cmd = blobmsg_open_array(&req, "command");
175 blobmsg_add_string(&req, "", ubusd_path);
176 blobmsg_add_string(&req, "", "-s");
177 blobmsg_add_string(&req, "", ubus_sock_path);
178 blobmsg_close_array(&req, cmd);
179
180 if (ubus_pw) {
181 blobmsg_add_string(&req, "user", "ubus");
182 blobmsg_add_string(&req, "group", "ubus");
183 }
184
185 blobmsg_close_table(&req, in);
186 blobmsg_close_table(&req, ins);
187
188 if (!ubus_lookup_id(host_ubus_ctx, "container", &id))
189 ubus_invoke(host_ubus_ctx, id, "add", req.head, NULL, NULL, 3000);
190
191 blob_buf_free(&req);
192 }
193
194 static void run_netifd(struct uloop_timeout *t)
195 {
196 static struct blob_buf req;
197 void *ins, *in, *cmd, *jail, *setns, *setnso, *namespaces, *mount, *pathenv;
198 char *resolvconf_dir, *resolvconf, *ucimount, *ubusmount;
199 char uci_dir[] = "/var/containers/ujail-uci-XXXXXX";
200
201 uint32_t id;
202 bool running = false;
203
204 uloop_fd_delete(&fd_inotify_read);
205 close(fd_inotify_read.fd);
206
207 jail_ubus_ctx = ubus_connect(ubus_sock_path);
208 if (!jail_ubus_ctx)
209 return;
210
211 if (asprintf(&resolvconf_dir, "/tmp/resolv.conf-%s.d", jail_name) == -1)
212 return;
213
214 if (asprintf(&resolvconf, "%s/resolv.conf.auto", resolvconf_dir) == -1)
215 goto netifd_out_resolvconf_dir;
216
217 if (!mkdtemp(uci_dir))
218 goto netifd_out_resolvconf;
219
220 if (asprintf(&uci_config_network, "%s/network", uci_dir) == -1)
221 goto netifd_out_ucidir;
222
223 if (asprintf(&ucimount, "%s:/etc/config", uci_dir) == -1)
224 goto netifd_out_ucinetconf;
225
226 if (asprintf(&ubusmount, "%s:/var/run/ubus", ubus_sock_dir) == -1)
227 goto netifd_out_ucimount;
228
229 if (gen_jail_uci_network())
230 goto netifd_out_ubusmount;
231
232 blob_buf_init(&req, 0);
233 blobmsg_add_string(&req, "name", jail_name);
234 ins = blobmsg_open_table(&req, "instances");
235 in = blobmsg_open_table(&req, "netifd");
236
237 cmd = blobmsg_open_array(&req, "command");
238 blobmsg_add_string(&req, "", netifd_path);
239 blobmsg_add_string(&req, "", "-r");
240 blobmsg_add_string(&req, "", resolvconf);
241 blobmsg_close_array(&req, cmd);
242
243 pathenv = blobmsg_open_table(&req, "env");
244 blobmsg_add_string(&req, "PATH", "/usr/sbin:/usr/bin:/sbin:/bin");
245 blobmsg_close_table(&req, pathenv);
246
247 jail = blobmsg_open_table(&req, "jail");
248
249 setns = blobmsg_open_array(&req, "setns");
250 setnso = blobmsg_open_table(&req, "");
251 blobmsg_add_u32(&req, "pid", ns_pid);
252 namespaces = blobmsg_open_array(&req, "namespaces");
253 blobmsg_add_string(&req, "", "net");
254 blobmsg_add_string(&req, "", "ipc");
255 blobmsg_add_string(&req, "", "uts");
256 blobmsg_close_array(&req, namespaces);
257 blobmsg_close_table(&req, setnso);
258 blobmsg_close_array(&req, setns);
259
260 mount = blobmsg_open_table(&req, "mount");
261 blobmsg_add_string(&req, ubusmount, "1");
262 blobmsg_add_string(&req, resolvconf_dir, "1");
263 blobmsg_add_string(&req, ucimount, "0");
264 blobmsg_add_string(&req, "/bin/cat", "0");
265 blobmsg_add_string(&req, "/bin/ipcalc.sh", "0");
266 blobmsg_add_string(&req, "/bin/kill", "0");
267 blobmsg_add_string(&req, "/bin/ubus", "0");
268 blobmsg_add_string(&req, "/etc/hotplug.d", "0");
269 blobmsg_add_string(&req, "/lib/functions", "0");
270 blobmsg_add_string(&req, "/lib/functions.sh", "0");
271 blobmsg_add_string(&req, "/lib/netifd", "0");
272 blobmsg_add_string(&req, "/lib/network", "0");
273 blobmsg_add_string(&req, "/usr/bin/awk", "0");
274 blobmsg_add_string(&req, "/usr/bin/killall", "0");
275 blobmsg_add_string(&req, "/usr/bin/logger", "0");
276 blobmsg_add_string(&req, "/usr/bin/jshn", "0");
277 blobmsg_add_string(&req, "/usr/share/libubox/jshn.sh", "0");
278 blobmsg_add_string(&req, "/sbin/hotplug-call", "0");
279 blobmsg_add_string(&req, "/sbin/udhcpc", "0");
280 blobmsg_close_table(&req, mount);
281
282 blobmsg_add_u8(&req, "log", 1);
283 blobmsg_add_u8(&req, "procfs", 1);
284 blobmsg_add_u8(&req, "sysfs", 1);
285
286 blobmsg_add_u8(&req, "requirejail", 1);
287
288 blobmsg_close_table(&req, jail);
289
290 blobmsg_add_u8(&req, "stdout", 1);
291 blobmsg_add_u8(&req, "stderr", 1);
292
293 blobmsg_close_table(&req, in);
294 blobmsg_close_table(&req, ins);
295
296 if (!ubus_lookup_id(host_ubus_ctx, "container", &id))
297 running = !ubus_invoke(host_ubus_ctx, id, "add", req.head, NULL, NULL, 3000);
298
299 if (!running)
300 blob_buf_free(&req);
301 netifd_out_ubusmount:
302 free(ubusmount);
303 netifd_out_ucimount:
304 free(ucimount);
305 netifd_out_ucinetconf:
306 if (!running) {
307 unlink(uci_config_network);
308 free(uci_config_network);
309 }
310 netifd_out_ucidir:
311 if (!running)
312 rmdir(uci_dir);
313 netifd_out_resolvconf:
314 free(resolvconf);
315 netifd_out_resolvconf_dir:
316 free(resolvconf_dir);
317
318 uloop_end();
319 }
320
321 static struct uloop_timeout netifd_start_timeout = { .cb = run_netifd, };
322
323 static void inotify_read_handler(struct uloop_fd *u, unsigned int events)
324 {
325 int rc;
326 char *p;
327 struct inotify_event *in;
328
329 /* read inotify events */
330 while ((rc = read(u->fd, inotify_buffer, INOTIFY_SZ)) == -1 && errno == EINTR);
331
332 if (rc <= 0)
333 return;
334
335 /* process events from buffer */
336 for (p = inotify_buffer;
337 rc - (p - inotify_buffer) >= (int)sizeof(struct inotify_event);
338 p += sizeof(struct inotify_event) + in->len) {
339 in = (struct inotify_event*)p;
340
341 if (in->len < 4)
342 continue;
343
344 if (!strncmp(ubus_sock_name, in->name, in->len))
345 uloop_timeout_add(&netifd_start_timeout);
346 }
347 }
348
349 static void netns_updown(struct ubus_context *ubus, const char *name, bool start, int netns_fd)
350 {
351 static struct blob_buf req;
352 uint32_t id;
353
354 if (!ubus)
355 return;
356
357 blob_buf_init(&req, 0);
358 if (name)
359 blobmsg_add_string(&req, "jail", name);
360
361 blobmsg_add_u8(&req, "start", start);
362
363 if (ubus_lookup_id(ubus, "network", &id) ||
364 ubus_invoke_fd(ubus, id, "netns_updown", req.head, NULL, NULL, 3000, netns_fd)) {
365 INFO("ubus request failed\n");
366 }
367
368 blob_buf_free(&req);
369 }
370
371 static void jail_network_reload(struct uloop_timeout *t)
372 {
373 uint32_t id;
374
375 if (!jail_ubus_ctx)
376 return;
377
378 if (gen_jail_uci_network())
379 return;
380
381 if (ubus_lookup_id(jail_ubus_ctx, "network", &id))
382 return;
383
384 ubus_invoke(jail_ubus_ctx, id, "reload", NULL, NULL, NULL, 3000);
385 }
386
387 static const struct blobmsg_policy service_watch_policy = { "config", BLOBMSG_TYPE_STRING };
388 static struct uloop_timeout jail_network_reload_timeout = { .cb = jail_network_reload, };
389
390 static int config_watch_notify_cb(struct ubus_context *ctx, struct ubus_object *obj,
391 struct ubus_request_data *req, const char *method,
392 struct blob_attr *msg)
393 {
394 struct blob_attr *attr;
395 const char *config;
396
397 if (strcmp(method, "config.change"))
398 return 0;
399
400 blobmsg_parse(&service_watch_policy, 1, &attr, blob_data(msg), blob_len(msg));
401 if (!attr)
402 return 1;
403
404 config = blobmsg_get_string(attr);
405 if (strcmp(config, "network"))
406 return 0;
407
408 uloop_timeout_add(&jail_network_reload_timeout);
409
410 return 0;
411 }
412
413 static void watch_ubus_service(void)
414 {
415 uint32_t id;
416
417 config_watch_subscribe.cb = config_watch_notify_cb;
418 if (ubus_register_subscriber(host_ubus_ctx, &config_watch_subscribe)) {
419 ERROR("failed to register ubus subscriber\n");
420 return;
421 }
422
423 if (ubus_lookup_id(host_ubus_ctx, "service", &id))
424 return;
425
426 if (!ubus_subscribe(host_ubus_ctx, &config_watch_subscribe, id))
427 return;
428
429 ERROR("failed to subscribe %d\n", id);
430 }
431
432 static struct uloop_timeout ubus_start_timeout = { .cb = run_ubusd, };
433
434 int jail_network_start(struct ubus_context *new_ctx, char *new_jail_name, pid_t new_ns_pid)
435 {
436 ubus_pw = getpwnam("ubus");
437 int ret = 0;
438 int netns_fd;
439
440 host_ubus_ctx = new_ctx;
441 ns_pid = new_ns_pid;
442 jail_name = new_jail_name;
443
444 if (asprintf(&ubus_sock_dir, "/var/containers/ubus-%s", jail_name) == -1) {
445 ret = ENOMEM;
446 goto errout_dir;
447 }
448
449 if (asprintf(&ubus_sock_path, "%s/%s", ubus_sock_dir, ubus_sock_name) == -1) {
450 ret = ENOMEM;
451 goto errout_path;
452 }
453
454 mkdir_p(ubus_sock_dir, 0755);
455 if (ubus_pw) {
456 ret = chown(ubus_sock_dir, ubus_pw->pw_uid, ubus_pw->pw_gid);
457 if (ret) {
458 ret = errno;
459 goto errout;
460 }
461 }
462
463 fd_inotify_read.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
464 fd_inotify_read.cb = inotify_read_handler;
465 if (fd_inotify_read.fd == -1) {
466 ERROR("failed to initialize inotify handler\n");
467 ret = EIO;
468 goto errout;
469 }
470 uloop_fd_add(&fd_inotify_read, ULOOP_READ);
471
472 inotify_buffer = calloc(1, INOTIFY_SZ);
473 if (!inotify_buffer) {
474 ret = ENOMEM;
475 goto errout_inotify;
476 }
477
478 if (inotify_add_watch(fd_inotify_read.fd, ubus_sock_dir, IN_CREATE) == -1) {
479 ERROR("failed to add inotify watch on %s\n", ubus_sock_dir);
480 free(inotify_buffer);
481 ret = EIO;
482 goto errout_inotify;
483 }
484
485 watch_ubus_service();
486
487 netns_fd = ns_open_pid("net", ns_pid);
488 if (netns_fd < 0) {
489 ret = ESRCH;
490 goto errout_inotify;
491 }
492
493 netns_updown(host_ubus_ctx, jail_name, true, netns_fd);
494
495 close(netns_fd);
496 uloop_timeout_add(&ubus_start_timeout);
497 uloop_run();
498
499 return 0;
500
501 errout_inotify:
502 close(fd_inotify_read.fd);
503 errout:
504 free(ubus_sock_path);
505 errout_path:
506 free(ubus_sock_dir);
507 errout_dir:
508 return ret;
509 }
510
511 static int jail_delete_instance(const char *instance)
512 {
513 static struct blob_buf req;
514 uint32_t id;
515
516 if (ubus_lookup_id(host_ubus_ctx, "container", &id))
517 return -1;
518
519 blob_buf_init(&req, 0);
520 blobmsg_add_string(&req, "name", jail_name);
521 blobmsg_add_string(&req, "instance", instance);
522
523 return ubus_invoke(host_ubus_ctx, id, "delete", req.head, NULL, NULL, 3000);
524 }
525
526 int jail_network_stop(void)
527 {
528 int host_netns = open("/proc/self/ns/net", O_RDONLY);
529
530 if (host_netns < 0)
531 return errno;
532
533 netns_updown(jail_ubus_ctx, NULL, false, host_netns);
534
535 close(host_netns);
536 ubus_free(jail_ubus_ctx);
537
538 jail_delete_instance("netifd");
539 jail_delete_instance("ubus");
540
541 if (uci_config_network) {
542 unlink(uci_config_network);
543 rmdir(dirname(uci_config_network));
544 free(uci_config_network);
545 }
546
547 free(ubus_sock_path);
548 rmdir(ubus_sock_dir);
549 free(ubus_sock_dir);
550
551 return 0;
552 }