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