]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cmd/lxc_user_nic.c
build: add src/include to build and simplify header inclusions
[mirror_lxc.git] / src / lxc / cmd / lxc_user_nic.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #include <arpa/inet.h>
7 #include <ctype.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <grp.h>
11 #include <linux/netlink.h>
12 #include <linux/rtnetlink.h>
13 #include <linux/sockios.h>
14 #include <net/if.h>
15 #include <net/if_arp.h>
16 #include <netinet/in.h>
17 #include <pwd.h>
18 #include <sched.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/file.h>
24 #include <sys/ioctl.h>
25 #include <sys/mman.h>
26 #include <sys/param.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #include "compiler.h"
33 #include "config.h"
34 #include "file_utils.h"
35 #include "log.h"
36 #include "memory_utils.h"
37 #include "network.h"
38 #include "nl.h"
39 #include "parse.h"
40 #include "process_utils.h"
41 #include "string_utils.h"
42 #include "syscall_wrappers.h"
43 #include "utils.h"
44
45 #ifndef HAVE_STRLCPY
46 #include "strlcpy.h"
47 #endif
48
49 #define usernic_debug_stream(stream, format, ...) \
50 do { \
51 fprintf(stream, "%s: %d: %s: " format, __FILE__, __LINE__, \
52 __func__, __VA_ARGS__); \
53 } while (false)
54
55 #define usernic_error(format, ...) usernic_debug_stream(stderr, format, __VA_ARGS__)
56
57 #define cmd_error_errno(__ret__, __errno__, format, ...) \
58 ({ \
59 typeof(__ret__) __internal_ret__ = (__ret__); \
60 errno = (__errno__); \
61 CMD_SYSERROR(format, ##__VA_ARGS__); \
62 __internal_ret__; \
63 })
64
65 __noreturn static void usage(bool fail)
66 {
67 fprintf(stderr, "Description:\n");
68 fprintf(stderr, " Manage nics in another network namespace\n\n");
69
70 fprintf(stderr, "Usage:\n");
71 fprintf(stderr, " lxc-user-nic [command]\n\n");
72
73 fprintf(stderr, "Available Commands:\n");
74 fprintf(stderr, " create {lxcpath} {name} {pid} {type} {bridge} {container nicname}\n");
75 fprintf(stderr, " delete {lxcpath} {name} {/proc/<pid>/ns/net} {type} {bridge} {container nicname}\n");
76
77 if (fail)
78 _exit(EXIT_FAILURE);
79
80 _exit(EXIT_SUCCESS);
81 }
82
83 static int open_and_lock(const char *path)
84 {
85 __do_close int fd = -EBADF;
86 int ret;
87 struct flock lk;
88
89 fd = open(path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR | O_CLOEXEC);
90 if (fd < 0) {
91 CMD_SYSERROR("Failed to open \"%s\"\n", path);
92 return -1;
93 }
94
95 lk.l_type = F_WRLCK;
96 lk.l_whence = SEEK_SET;
97 lk.l_start = 0;
98 lk.l_len = 0;
99
100 ret = fcntl(fd, F_SETLKW, &lk);
101 if (ret < 0) {
102 CMD_SYSERROR("Failed to lock \"%s\"\n", path);
103 return -1;
104 }
105
106 return move_fd(fd);
107 }
108
109 static char *get_username(void)
110 {
111 __do_free char *buf = NULL;
112 struct passwd pwent;
113 struct passwd *pwentp = NULL;
114 size_t bufsize;
115 int ret;
116
117 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
118 if (bufsize == -1)
119 bufsize = 1024;
120
121 buf = malloc(bufsize);
122 if (!buf)
123 return NULL;
124
125 ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp);
126 if (!pwentp) {
127 if (ret == 0)
128 usernic_error("%s", "Could not find matched password record\n");
129
130 CMD_SYSERROR("Failed to get username: %u\n", getuid());
131 return NULL;
132 }
133
134 return strdup(pwent.pw_name);
135 }
136
137
138 static char **get_groupnames(void)
139 {
140 __do_free char *buf = NULL;
141 __do_free gid_t *group_ids = NULL;
142 __do_free_string_list char **groupnames = NULL;
143 int ngroups;
144 int ret, i;
145 struct group grent;
146 struct group *grentp = NULL;
147 size_t bufsize;
148
149 ngroups = getgroups(0, NULL);
150 if (ngroups < 0) {
151 CMD_SYSERROR("Failed to get number of groups the user belongs to\n");
152 return NULL;
153 }
154
155 if (ngroups == 0)
156 return NULL;
157
158 group_ids = malloc(sizeof(gid_t) * ngroups);
159 if (!group_ids) {
160 CMD_SYSERROR("Failed to allocate memory while getting groups the user belongs to\n");
161 return NULL;
162 }
163
164 ret = getgroups(ngroups, group_ids);
165 if (ret < 0) {
166 CMD_SYSERROR("Failed to get process groups\n");
167 return NULL;
168 }
169
170 groupnames = zalloc(sizeof(char *) * (ngroups + 1));
171 if (!groupnames) {
172 CMD_SYSERROR("Failed to allocate memory while getting group names\n");
173 return NULL;
174 }
175
176 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
177 if (bufsize == -1)
178 bufsize = 1024;
179
180 buf = malloc(bufsize);
181 if (!buf) {
182 CMD_SYSERROR("Failed to allocate memory while getting group names\n");
183 return NULL;
184 }
185
186 for (i = 0; i < ngroups; i++) {
187 while ((ret = getgrgid_r(group_ids[i], &grent, buf, bufsize, &grentp)) == ERANGE) {
188 char *new_buf;
189
190 bufsize <<= 1;
191 if (bufsize > MAX_GRBUF_SIZE) {
192 usernic_error("Failed to get group members: %u\n", group_ids[i]);
193 return NULL;
194 }
195
196 new_buf = realloc(buf, bufsize);
197 if (!new_buf) {
198 usernic_error("Failed to allocate memory while getting group names: %s\n",
199 strerror(errno));
200 return NULL;
201 }
202 buf = new_buf;
203 }
204
205 /* If a group is not found, just ignore it. */
206 if (!grentp)
207 continue;
208
209 groupnames[i] = strdup(grent.gr_name);
210 if (!groupnames[i]) {
211 usernic_error("Failed to copy group name \"%s\"", grent.gr_name);
212 return NULL;
213 }
214 }
215
216 return move_ptr(groupnames);
217 }
218
219 static bool name_is_in_groupnames(char *name, char **groupnames)
220 {
221 while (groupnames) {
222 if (!strcmp(name, *groupnames))
223 return true;
224 groupnames++;
225 }
226
227 return false;
228 }
229
230 struct alloted_s {
231 char *name;
232 int allowed;
233 struct alloted_s *next;
234 };
235
236 static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n)
237 {
238 __do_free struct alloted_s *al = NULL;
239 struct alloted_s *cur;
240
241 if (!head || !name) {
242 /* Sanity check. Parameters should not be null. */
243 usernic_error("%s\n", "Unexpected NULL argument");
244 return NULL;
245 }
246
247 al = zalloc(sizeof(struct alloted_s));
248 if (!al) {
249 CMD_SYSERROR("Failed to allocate memory\n");
250 return NULL;
251 }
252
253 al->name = strdup(name);
254 if (!al->name)
255 return NULL;
256
257 al->allowed = n;
258 al->next = NULL;
259
260 if (*head) {
261 cur = *head;
262 while (cur->next)
263 cur = cur->next;
264 cur->next = al;
265 } else {
266 *head = al;
267 }
268
269 return move_ptr(al);
270 }
271
272 static void free_alloted(struct alloted_s **head)
273 {
274 struct alloted_s *cur;
275
276 if (!head)
277 return;
278
279 cur = *head;
280 while (cur) {
281 cur = cur->next;
282 free((*head)->name);
283 free(*head);
284 *head = cur;
285 }
286 }
287
288 /* The configuration file consists of lines of the form:
289 *
290 * user type bridge count
291 * or
292 * @group type bridge count
293 *
294 * Return the count entry for the calling user if there is one. Else
295 * return -1.
296 */
297 static int get_alloted(char *me, char *intype, char *link,
298 struct alloted_s **alloted)
299 {
300 __do_free char *line = NULL;
301 __do_fclose FILE *fin = NULL;
302 __do_free_string_list char **groups = NULL;
303 int n, ret;
304 char name[100], type[100], br[100];
305 int count = 0;
306 size_t len = 0;
307
308 fin = fopen(LXC_USERNIC_CONF, "re");
309 if (!fin) {
310 CMD_SYSERROR("Failed to open \"%s\"\n", LXC_USERNIC_CONF);
311 return -1;
312 }
313
314 groups = get_groupnames();
315 while ((getline(&line, &len, fin)) != -1) {
316 ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", name,
317 type, br, &n);
318 if (ret != 4)
319 continue;
320
321 if (is_empty_string(name))
322 continue;
323
324 if (!strequal(name, me)) {
325 if (name[0] != '@')
326 continue;
327
328 if (!name_is_in_groupnames(name + 1, groups))
329 continue;
330 }
331
332 if (!strequal(type, intype))
333 continue;
334
335 if (!strequal(link, br))
336 continue;
337
338 /*
339 * Found the user or group with the appropriate settings,
340 * therefore finish the search. What to do if there are is more
341 * than one applicable line? Currently this is not specified in
342 * the docs.
343 *
344 * If append_alloted returns NULL, e.g. due to a malloc error,
345 * we set count to 0 and break the loop, allowing cleanup and
346 * then exiting from main().
347 */
348 if (!append_alloted(alloted, name, n)) {
349 count = 0;
350 break;
351 }
352
353 count += n;
354 }
355
356 /* Now return the total number of nics that this user can create. */
357 return count;
358 }
359
360 static char *get_eol(char *s, char *e)
361 {
362 while ((s < e) && *s && (*s != '\n'))
363 s++;
364
365 return s;
366 }
367
368 static char *get_eow(char *s, char *e)
369 {
370 while ((s < e) && *s && !isblank(*s) && (*s != '\n'))
371 s++;
372
373 return s;
374 }
375
376 static char *find_line(char *buf_start, char *buf_end, char *name,
377 char *net_type, char *net_link, char *net_dev,
378 bool *owner, bool *found, bool *keep)
379 {
380 char *end_of_line, *end_of_word, *line;
381
382 while (buf_start < buf_end) {
383 size_t len;
384 char netdev_name[IFNAMSIZ];
385
386 *found = false;
387 *keep = true;
388 *owner = false;
389
390 end_of_line = get_eol(buf_start, buf_end);
391 if (end_of_line >= buf_end)
392 return NULL;
393
394 line = buf_start;
395 if (*buf_start == '#')
396 goto next;
397
398 while ((buf_start < buf_end) && isblank(*buf_start))
399 buf_start++;
400
401 /* Check whether the line contains the caller's name. */
402 end_of_word = get_eow(buf_start, buf_end);
403 /* corrupt db */
404 if (!end_of_word)
405 return NULL;
406
407 if (strncmp(buf_start, name, strlen(name)))
408 *found = false;
409 else
410 if (strlen(name) == (size_t)(end_of_word - buf_start))
411 *owner = true;
412
413 buf_start = end_of_word + 1;
414 while ((buf_start < buf_end) && isblank(*buf_start))
415 buf_start++;
416
417 /* Check whether line is of the right network type. */
418 end_of_word = get_eow(buf_start, buf_end);
419 /* corrupt db */
420 if (!end_of_word)
421 return NULL;
422
423 if (strncmp(buf_start, net_type, strlen(net_type)))
424 *found = false;
425
426 buf_start = end_of_word + 1;
427 while ((buf_start < buf_end) && isblank(*buf_start))
428 buf_start++;
429
430 /* Check whether line is contains the right link. */
431 end_of_word = get_eow(buf_start, buf_end);
432 /* corrupt db */
433 if (!end_of_word)
434 return NULL;
435
436 if (strncmp(buf_start, net_link, strlen(net_link)))
437 *found = false;
438
439 buf_start = end_of_word + 1;
440 while ((buf_start < buf_end) && isblank(*buf_start))
441 buf_start++;
442
443 /* Check whether line contains the right network device. */
444 end_of_word = get_eow(buf_start, buf_end);
445 /* corrupt db */
446 if (!end_of_word)
447 return NULL;
448
449 len = end_of_word - buf_start;
450 /* corrupt db */
451 if (len >= IFNAMSIZ)
452 return NULL;
453
454 memcpy(netdev_name, buf_start, len);
455 netdev_name[len] = '\0';
456 *keep = lxc_nic_exists(netdev_name);
457
458 if (net_dev && !strcmp(netdev_name, net_dev))
459 *found = true;
460
461 return line;
462
463 next:
464 buf_start = end_of_line + 1;
465 }
466
467 return NULL;
468 }
469
470 static int instantiate_veth(char *veth1, char *veth2, pid_t pid, unsigned int mtu)
471 {
472 int ret;
473
474 ret = lxc_veth_create(veth1, veth2, pid, mtu);
475 if (ret < 0) {
476 CMD_SYSERROR("Failed to create %s-%s\n", veth1, veth2);
477 return ret_errno(-ret);
478 }
479
480 /*
481 * Changing the high byte of the mac address to 0xfe, the bridge
482 * interface will always keep the host's mac address and not take the
483 * mac address of a container.
484 */
485 ret = setup_private_host_hw_addr(veth1);
486 if (ret < 0) {
487 CMD_SYSERROR("Failed to change mac address of host interface %s\n", veth1);
488 return ret_errno(-ret);
489 }
490
491 return netdev_set_flag(veth1, IFF_UP);
492 }
493
494 static int get_mtu(char *name)
495 {
496 int idx;
497
498 idx = if_nametoindex(name);
499 if (idx < 0)
500 return -1;
501
502 return netdev_get_mtu(idx);
503 }
504
505 static int create_nic(char *nic, char *br, int pid, char **cnic)
506 {
507 unsigned int mtu = 1500;
508 int ret;
509 char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ];
510
511 ret = snprintf(veth1buf, IFNAMSIZ, "%s", nic);
512 if (ret < 0 || ret >= IFNAMSIZ) {
513 usernic_error("%s", "Could not create nic name\n");
514 return -1;
515 }
516
517 ret = snprintf(veth2buf, IFNAMSIZ, "%sp", veth1buf);
518 if (ret < 0 || ret >= IFNAMSIZ) {
519 usernic_error("%s\n", "Could not create nic name");
520 return -1;
521 }
522
523 if (strcmp(br, "none"))
524 mtu = get_mtu(br);
525 if (!mtu)
526 mtu = 1500;
527
528 /* create the nics */
529 ret = instantiate_veth(veth1buf, veth2buf, pid, mtu);
530 if (ret < 0) {
531 usernic_error("%s", "Error creating veth tunnel\n");
532 return -1;
533 }
534
535 if (strcmp(br, "none")) {
536 if (mtu > 0) {
537 ret = lxc_netdev_set_mtu(veth1buf, mtu);
538 if (ret < 0) {
539 usernic_error("Failed to set mtu to %d on %s\n",
540 mtu, veth1buf);
541 goto out_del;
542 }
543 }
544
545 /* attach veth1 to bridge */
546 ret = lxc_bridge_attach(br, veth1buf);
547 if (ret < 0) {
548 usernic_error("Error attaching %s to %s\n", veth1buf, br);
549 goto out_del;
550 }
551 }
552
553 *cnic = strdup(veth2buf);
554 if (!*cnic) {
555 usernic_error("Failed to copy string \"%s\"\n", veth2buf);
556 return -1;
557 }
558
559 return 0;
560
561 out_del:
562 lxc_netdev_delete_by_name(veth1buf);
563 return -1;
564 }
565
566 struct entry_line {
567 char *start;
568 int len;
569 bool keep;
570 };
571
572 static bool cull_entries(int fd, char *name, char *net_type, char *net_link,
573 char *net_dev, bool *found_nicname)
574 {
575 __do_free char *buf = NULL;
576 __do_free struct entry_line *entry_lines = NULL;
577 int n = 0;
578 size_t length = 0;
579 int ret;
580 char *buf_end, *buf_start;
581 bool found, keep;
582
583 ret = fd_to_buf(fd, &buf, &length);
584 if (ret < 0) {
585 CMD_SYSERROR("Failed to read database file\n");
586 return false;
587 }
588 if (lseek(fd, 0, SEEK_SET) < 0)
589 return false;
590
591 if (length == 0)
592 return false;
593
594 buf_start = buf;
595 buf_end = buf + length;
596 while ((buf_start = find_line(buf_start, buf_end, name, net_type,
597 net_link, net_dev, &(bool){true}, &found,
598 &keep))) {
599 struct entry_line *newe;
600
601 newe = realloc(entry_lines, sizeof(*entry_lines) * (n + 1));
602 if (!newe)
603 return false;
604
605 if (found)
606 *found_nicname = true;
607
608 entry_lines = newe;
609 entry_lines[n].start = buf_start;
610 entry_lines[n].len = get_eol(buf_start, buf_end) - entry_lines[n].start;
611 entry_lines[n].keep = keep;
612 n++;
613
614 buf_start += entry_lines[n - 1].len + 1;
615 if (buf_start >= buf_end)
616 break;
617 }
618
619 buf_start = buf;
620
621 for (int i = 0; i < n; i++) {
622 if (!entry_lines[i].keep)
623 continue;
624
625 memcpy(buf_start, entry_lines[i].start, entry_lines[i].len);
626 buf_start += entry_lines[i].len;
627 *buf_start = '\n';
628 buf_start++;
629 }
630
631 return ftruncate(fd, buf_start - buf) == 0;
632 }
633
634 static int count_entries(char *buf, off_t len, char *name, char *net_type, char *net_link)
635 {
636 int count = 0;
637 bool owner = false;
638 char *buf_end;
639
640 buf_end = &buf[len];
641 while ((buf = find_line(buf, buf_end, name, net_type, net_link, NULL,
642 &owner, &(bool){true}, &(bool){true}))) {
643 if (owner)
644 count++;
645
646 buf = get_eol(buf, buf_end) + 1;
647 if (buf >= buf_end)
648 break;
649 }
650
651 return count;
652 }
653
654 /* The dbfile has lines of the format: user type bridge nicname. */
655 static char *get_nic_if_avail(int fd, struct alloted_s *names, int pid,
656 char *intype, char *br, int allowed, char **cnic)
657 {
658 __do_free char *buf = NULL, *newline = NULL;
659 size_t length = 0;
660 int ret;
661 size_t slen;
662 char *owner;
663 char nicname[IFNAMSIZ];
664 struct alloted_s *n;
665 uid_t uid;
666
667 for (n = names; n != NULL; n = n->next)
668 cull_entries(fd, n->name, intype, br, NULL, NULL);
669
670 if (allowed == 0)
671 return NULL;
672
673 owner = names->name;
674
675 ret = fd_to_buf(fd, &buf, &length);
676 if (ret < 0) {
677 CMD_SYSERROR("Failed to read database file\n");
678 return false;
679 }
680 if (lseek(fd, 0, SEEK_SET) < 0)
681 return false;
682
683 if (length > 0) {
684 owner = NULL;
685
686 for (n = names; n != NULL; n = n->next) {
687 int count;
688
689 count = count_entries(buf, length, n->name, intype, br);
690 if (count >= n->allowed)
691 continue;
692
693 owner = n->name;
694 break;
695 }
696 }
697
698 if (!owner)
699 return NULL;
700
701 uid = getuid();
702 /*
703 * For POSIX integer uids the network device name schema is
704 * vethUID_XXXX.
705 * With four random characters passed to
706 * lxc_ifname_alnum_case_sensitive() we get 62^4 = 14776336
707 * combinations per uid. That's plenty of network devices for now.
708 */
709 if (uid > 0 && uid <= 65536)
710 ret = snprintf(nicname, sizeof(nicname), "veth%d_XXXX", uid);
711 else
712 ret = snprintf(nicname, sizeof(nicname), "vethXXXXXX");
713 if (ret < 0 || (size_t)ret >= sizeof(nicname))
714 return NULL;
715
716 if (!lxc_ifname_alnum_case_sensitive(nicname))
717 return NULL;
718
719 ret = create_nic(nicname, br, pid, cnic);
720 if (ret < 0) {
721 usernic_error("%s", "Failed to create new nic\n");
722 return NULL;
723 }
724
725 /* strlen(owner)
726 * +
727 * " "
728 * +
729 * strlen(intype)
730 * +
731 * " "
732 * +
733 * strlen(br)
734 * +
735 * " "
736 * +
737 * strlen(nicname)
738 * +
739 * \n
740 * +
741 * \0
742 */
743 slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(nicname) + 4;
744 newline = malloc(slen + 1);
745 if (!newline) {
746 CMD_SYSERROR("Failed allocate memory\n");
747 return NULL;
748 }
749
750 ret = snprintf(newline, slen + 1, "%s %s %s %s\n", owner, intype, br, nicname);
751 if (ret < 0 || (size_t)ret >= (slen + 1)) {
752 if (lxc_netdev_delete_by_name(nicname) != 0)
753 usernic_error("Error unlinking %s\n", nicname);
754
755 return NULL;
756 }
757
758 if (lxc_pwrite_nointr(fd, newline, slen, length) != slen) {
759 CMD_SYSERROR("Failed to append new entry \"%s\" to database file", newline);
760
761 if (lxc_netdev_delete_by_name(nicname) != 0)
762 usernic_error("Error unlinking %s\n", nicname);
763
764 return NULL;
765 }
766
767 ret = ftruncate(fd, length + slen);
768 if (ret < 0) {
769 CMD_SYSERROR("Failed to truncate file\n");
770
771 if (lxc_netdev_delete_by_name(nicname) != 0)
772 usernic_error("Error unlinking %s\n", nicname);
773
774 return NULL;
775 }
776
777 return strdup(nicname);
778 }
779
780 static bool create_db_dir(char *fnam)
781 {
782 __do_free char *copy = NULL;
783 char *p;
784 int ret;
785
786 copy = must_copy_string(fnam);
787 p = copy;
788 fnam = p;
789 p = p + 1;
790
791 again:
792 while (*p && *p != '/')
793 p++;
794
795 if (!*p)
796 return true;
797
798 *p = '\0';
799
800 ret = mkdir(fnam, 0755);
801 if (ret < 0 && errno != EEXIST) {
802 CMD_SYSERROR("Failed to create %s\n", fnam);
803 *p = '/';
804 return false;
805 }
806
807 *(p++) = '/';
808
809 goto again;
810 }
811
812 static char *lxc_secure_rename_in_ns(int pid, char *oldname, char *newname,
813 int *container_veth_ifidx)
814 {
815 __do_close int fd = -EBADF, ofd = -EBADF;
816 int fret = -1;
817 int ifindex, ret;
818 pid_t pid_self;
819 uid_t ruid, suid, euid;
820 char ifname[IFNAMSIZ];
821
822 pid_self = lxc_raw_getpid();
823
824 ofd = lxc_preserve_ns(pid_self, "net");
825 if (ofd < 0)
826 return cmd_error_errno(NULL, errno, "Failed opening network namespace path for %d", pid_self);
827
828 fd = lxc_preserve_ns(pid, "net");
829 if (fd < 0)
830 return cmd_error_errno(NULL, errno, "Failed opening network namespace path for %d", pid);
831
832 ret = getresuid(&ruid, &euid, &suid);
833 if (ret < 0)
834 return cmd_error_errno(NULL, errno, "Failed to retrieve real, effective, and saved user IDs\n");
835
836 ret = setns(fd, CLONE_NEWNET);
837 if (ret < 0)
838 return cmd_error_errno(NULL, errno, "Failed to setns() to the network namespace of the container with PID %d\n", pid);
839
840 ret = setresuid(ruid, ruid, 0);
841 if (ret < 0) {
842 CMD_SYSERROR("Failed to drop privilege by setting effective user id and real user id to %d, and saved user ID to 0\n", ruid);
843 /*
844 * It's ok to jump to do_full_cleanup here since setresuid()
845 * will succeed when trying to set real, effective, and saved
846 * to values they currently have.
847 */
848 goto out_setns;
849 }
850
851 /* Check if old interface exists. */
852 ifindex = if_nametoindex(oldname);
853 if (!ifindex) {
854 CMD_SYSERROR("Failed to get netdev index\n");
855 goto out_setresuid;
856 }
857
858 /*
859 * When the IFLA_IFNAME attribute is passed something like "<prefix>%d"
860 * netlink will replace the format specifier with an appropriate index.
861 * So we pass "eth%d".
862 */
863 ret = lxc_netdev_rename_by_name(oldname, newname ? newname : "eth%d");
864 if (ret < 0) {
865 CMD_SYSERROR("Error %d renaming netdev %s to %s in container\n", ret, oldname, newname ? newname : "eth%d");
866 goto out_setresuid;
867 }
868
869 /* Retrieve new name for interface. */
870 if (!if_indextoname(ifindex, ifname)) {
871 CMD_SYSERROR("Failed to get new netdev name\n");
872 goto out_setresuid;
873 }
874
875 fret = 0;
876
877 out_setresuid:
878 ret = setresuid(ruid, euid, suid);
879 if (ret < 0)
880 return cmd_error_errno(NULL, errno, "Failed to restore privilege by setting effective user id to %d, real user id to %d, and saved user ID to %d\n",
881 ruid, euid, suid);
882
883 out_setns:
884 ret = setns(ofd, CLONE_NEWNET);
885 if (ret < 0)
886 return cmd_error_errno(NULL, errno, "Failed to setns() to original network namespace of PID %d\n", ofd);
887
888 if (fret < 0)
889 return NULL;
890
891 *container_veth_ifidx = ifindex;
892 return strdup(ifname);
893 }
894
895 /* If the caller (real uid, not effective uid) may read the /proc/[pid]/ns/net,
896 * then it is either the caller's netns or one which it created.
897 */
898 static bool may_access_netns(int pid)
899 {
900 int ret;
901 char s[200];
902 uid_t ruid, suid, euid;
903 bool may_access = false;
904
905 ret = getresuid(&ruid, &euid, &suid);
906 if (ret < 0) {
907 CMD_SYSERROR("Failed to retrieve real, effective, and saved user IDs\n");
908 return false;
909 }
910
911 ret = setresuid(ruid, ruid, euid);
912 if (ret < 0) {
913 CMD_SYSERROR("Failed to drop privilege by setting effective user id and real user id to %d, and saved user ID to %d\n",
914 ruid, euid);
915 return false;
916 }
917
918 ret = snprintf(s, 200, "/proc/%d/ns/net", pid);
919 if (ret < 0 || ret >= 200)
920 return false;
921
922 ret = access(s, R_OK);
923 may_access = true;
924 if (ret < 0) {
925 may_access = false;
926 CMD_SYSERROR("Uid %d may not access %s\n", (int)ruid, s);
927 }
928
929 ret = setresuid(ruid, euid, suid);
930 if (ret < 0) {
931 CMD_SYSERROR("Failed to restore user id to %d, real user id to %d, and saved user ID to %d\n",
932 ruid, euid, suid);
933 may_access = false;
934 }
935
936 return may_access;
937 }
938
939 struct user_nic_args {
940 char *cmd;
941 char *lxc_path;
942 char *lxc_name;
943 char *pid;
944 char *type;
945 char *link;
946 char *veth_name;
947 };
948
949 enum lxc_user_nic_command {
950 LXC_USERNIC_CREATE = 0,
951 LXC_USERNIC_DELETE = 1,
952 };
953
954 static bool is_privileged_over_netns(int netns_fd)
955 {
956 int ofd, ret;
957 pid_t pid_self;
958 uid_t euid, ruid, suid;
959 bool bret = false;
960
961 pid_self = lxc_raw_getpid();
962
963 ofd = lxc_preserve_ns(pid_self, "net");
964 if (ofd < 0) {
965 usernic_error("Failed opening network namespace path for %d", pid_self);
966 return false;
967 }
968
969 ret = getresuid(&ruid, &euid, &suid);
970 if (ret < 0) {
971 CMD_SYSERROR("Failed to retrieve real, effective, and saved user IDs\n");
972 goto do_partial_cleanup;
973 }
974
975 ret = setns(netns_fd, CLONE_NEWNET);
976 if (ret < 0) {
977 CMD_SYSERROR("Failed to setns() to network namespace\n");
978 goto do_partial_cleanup;
979 }
980
981 ret = setresuid(ruid, ruid, 0);
982 if (ret < 0) {
983 CMD_SYSERROR("Failed to drop privilege by setting effective user id and real user id to %d, and saved user ID to 0\n",
984 ruid);
985 /* It's ok to jump to do_full_cleanup here since setresuid()
986 * will succeed when trying to set real, effective, and saved to
987 * values they currently have.
988 */
989 goto do_full_cleanup;
990 }
991
992 /* Test whether we are privileged over the network namespace. To do this
993 * we try to delete the loopback interface which is not possible. If we
994 * are privileged over the network namespace we will get ENOTSUP. If we
995 * are not privileged over the network namespace we will get EPERM.
996 */
997 ret = lxc_netdev_delete_by_name("lo");
998 if (ret == -ENOTSUP)
999 bret = true;
1000
1001 do_full_cleanup:
1002 ret = setresuid(ruid, euid, suid);
1003 if (ret < 0) {
1004 CMD_SYSERROR("Failed to restore privilege by setting effective user id to %d, real user id to %d, and saved user ID to %d\n",
1005 ruid, euid, suid);
1006 bret = false;
1007 }
1008
1009 ret = setns(ofd, CLONE_NEWNET);
1010 if (ret < 0) {
1011 CMD_SYSERROR("Failed to setns() to original network namespace of PID %d\n",
1012 ofd);
1013 bret = false;
1014 }
1015
1016 do_partial_cleanup:
1017 close(ofd);
1018 return bret;
1019 }
1020
1021 static inline int validate_args(const struct user_nic_args *args, int argc)
1022 {
1023 int request = -EINVAL;
1024
1025 if (!strcmp(args->cmd, "create"))
1026 request = LXC_USERNIC_CREATE;
1027 else if (!strcmp(args->cmd, "delete"))
1028 request = LXC_USERNIC_DELETE;
1029
1030 return request;
1031 }
1032
1033 int main(int argc, char *argv[])
1034 {
1035 __do_free char *me = NULL, *newname = NULL, *nicname = NULL;
1036 int fd, n, pid, request, ret;
1037 struct user_nic_args args;
1038 int container_veth_ifidx = -1, host_veth_ifidx = -1, netns_fd = -1;
1039 char *cnic = NULL;
1040 struct alloted_s *alloted = NULL;
1041
1042 if (argc < 7 || argc > 8)
1043 usage(true);
1044
1045 memset(&args, 0, sizeof(struct user_nic_args));
1046
1047 args.cmd = argv[1];
1048 args.lxc_path = argv[2];
1049 args.lxc_name = argv[3];
1050 args.pid = argv[4];
1051 args.type = argv[5];
1052 args.link = argv[6];
1053 if (argc == 8)
1054 args.veth_name = argv[7];
1055
1056 request = validate_args(&args, argc);
1057 if (request < 0)
1058 usage(true);
1059
1060 /* Set a sane env, because we are setuid-root. */
1061 ret = clearenv();
1062 if (ret) {
1063 usernic_error("%s", "Failed to clear environment\n");
1064 _exit(EXIT_FAILURE);
1065 }
1066
1067 ret = setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
1068 if (ret < 0) {
1069 usernic_error("%s", "Failed to set PATH, exiting\n");
1070 _exit(EXIT_FAILURE);
1071 }
1072
1073 me = get_username();
1074 if (!me) {
1075 usernic_error("%s", "Failed to get username\n");
1076 _exit(EXIT_FAILURE);
1077 }
1078
1079 if (request == LXC_USERNIC_CREATE) {
1080 ret = lxc_safe_int(args.pid, &pid);
1081 if (ret < 0) {
1082 usernic_error("Could not read pid: %s\n", args.pid);
1083 _exit(EXIT_FAILURE);
1084 }
1085 } else if (request == LXC_USERNIC_DELETE) {
1086 char opath[LXC_PROC_PID_FD_LEN];
1087
1088 /* Open the path with O_PATH which will not trigger an actual
1089 * open(). Don't report an errno to the caller to not leak
1090 * information whether the path exists or not.
1091 * When stracing setuid is stripped so this is not a concern
1092 * either.
1093 */
1094 netns_fd = open(args.pid, O_PATH | O_CLOEXEC);
1095 if (netns_fd < 0) {
1096 usernic_error("Failed to open \"%s\"\n", args.pid);
1097 _exit(EXIT_FAILURE);
1098 }
1099
1100 if (!fhas_fs_type(netns_fd, NSFS_MAGIC)) {
1101 usernic_error("Path \"%s\" does not refer to a network namespace path\n", args.pid);
1102 close(netns_fd);
1103 _exit(EXIT_FAILURE);
1104 }
1105
1106 ret = snprintf(opath, sizeof(opath), "/proc/self/fd/%d", netns_fd);
1107 if (ret < 0 || (size_t)ret >= sizeof(opath)) {
1108 close(netns_fd);
1109 _exit(EXIT_FAILURE);
1110 }
1111
1112 /* Now get an fd that we can use in setns() calls. */
1113 ret = open(opath, O_RDONLY | O_CLOEXEC);
1114 if (ret < 0) {
1115 CMD_SYSERROR("Failed to open \"%s\"\n", args.pid);
1116 close(netns_fd);
1117 _exit(EXIT_FAILURE);
1118 }
1119
1120 close(netns_fd);
1121 netns_fd = ret;
1122 }
1123
1124 if (!create_db_dir(LXC_USERNIC_DB)) {
1125 usernic_error("%s", "Failed to create directory for db file\n");
1126
1127 if (netns_fd >= 0)
1128 close(netns_fd);
1129
1130 _exit(EXIT_FAILURE);
1131 }
1132
1133 fd = open_and_lock(LXC_USERNIC_DB);
1134 if (fd < 0) {
1135 usernic_error("Failed to lock %s\n", LXC_USERNIC_DB);
1136
1137 if (netns_fd >= 0)
1138 close(netns_fd);
1139
1140 _exit(EXIT_FAILURE);
1141 }
1142
1143 if (request == LXC_USERNIC_CREATE) {
1144 if (!may_access_netns(pid)) {
1145 usernic_error("User %s may not modify netns for pid %d\n", me, pid);
1146 _exit(EXIT_FAILURE);
1147 }
1148 } else if (request == LXC_USERNIC_DELETE) {
1149 bool has_priv;
1150
1151 has_priv = is_privileged_over_netns(netns_fd);
1152 close(netns_fd);
1153 if (!has_priv) {
1154 usernic_error("%s", "Process is not privileged over network namespace\n");
1155 _exit(EXIT_FAILURE);
1156 }
1157 }
1158
1159 n = get_alloted(me, args.type, args.link, &alloted);
1160
1161 if (request == LXC_USERNIC_DELETE) {
1162 struct alloted_s *it;
1163 bool found_nicname = false;
1164
1165 if (!is_ovs_bridge(args.link)) {
1166 usernic_error("%s", "Deletion of non ovs type network devices not implemented\n");
1167 close(fd);
1168 free_alloted(&alloted);
1169 _exit(EXIT_FAILURE);
1170 }
1171
1172 /* Check whether the network device we are supposed to delete
1173 * exists in the db. If it doesn't we will not delete it as we
1174 * need to assume the network device is not under our control.
1175 * As a side effect we also clear any invalid entries from the
1176 * database.
1177 */
1178 for (it = alloted; it; it = it->next)
1179 cull_entries(fd, it->name, args.type, args.link,
1180 args.veth_name, &found_nicname);
1181 close(fd);
1182 free_alloted(&alloted);
1183
1184 if (!found_nicname) {
1185 usernic_error("Caller is not allowed to delete network device \"%s\"\n", args.veth_name);
1186 _exit(EXIT_FAILURE);
1187 }
1188
1189 ret = lxc_ovs_delete_port(args.link, args.veth_name);
1190 if (ret < 0) {
1191 usernic_error("Failed to remove port \"%s\" from openvswitch bridge \"%s\"", args.veth_name, args.link);
1192 _exit(EXIT_FAILURE);
1193 }
1194
1195 _exit(EXIT_SUCCESS);
1196 }
1197
1198 if (n > 0)
1199 nicname = get_nic_if_avail(fd, alloted, pid, args.type,
1200 args.link, n, &cnic);
1201
1202 close(fd);
1203 free_alloted(&alloted);
1204
1205 if (!nicname) {
1206 usernic_error("%s", "Quota reached\n");
1207 _exit(EXIT_FAILURE);
1208 }
1209
1210 /* Now rename the link. */
1211 newname = lxc_secure_rename_in_ns(pid, cnic, args.veth_name,
1212 &container_veth_ifidx);
1213 if (!newname) {
1214 usernic_error("%s", "Failed to rename the link\n");
1215
1216 ret = lxc_netdev_delete_by_name(cnic);
1217 if (ret < 0)
1218 usernic_error("Failed to delete \"%s\"\n", cnic);
1219
1220 _exit(EXIT_FAILURE);
1221 }
1222
1223 host_veth_ifidx = if_nametoindex(nicname);
1224 if (!host_veth_ifidx) {
1225 CMD_SYSERROR("Failed to get netdev index\n");
1226 _exit(EXIT_FAILURE);
1227 }
1228
1229 /* Write names of veth pairs and their ifindices to stout:
1230 * (e.g. eth0:731:veth9MT2L4:730)
1231 */
1232 fprintf(stdout, "%s:%d:%s:%d\n", newname, container_veth_ifidx, nicname,
1233 host_veth_ifidx);
1234
1235 fflush(stdout);
1236 _exit(EXIT_SUCCESS);
1237 }