]>
Commit | Line | Data |
---|---|---|
3c2b20b3 WB |
1 | From 8095074e1aa2b308d8134638999a0ffe25e12347 Mon Sep 17 00:00:00 2001 |
2 | From: Christian Brauner <christian.brauner@ubuntu.com> | |
3 | Date: Sat, 28 Jan 2017 13:02:34 +0100 | |
4 | Subject: [PATCH 9/9] CVE-2017-5985: Ensure target netns is caller-owned | |
5 | ||
6 | Before this commit, lxc-user-nic could potentially have been tricked into | |
7 | operating on a network namespace over which the caller did not hold privilege. | |
8 | ||
9 | This commit ensures that the caller is privileged over the network namespace by | |
10 | temporarily dropping privilege. | |
11 | ||
12 | Launchpad: https://bugs.launchpad.net/ubuntu/+source/lxc/+bug/1654676 | |
13 | Reported-by: Jann Horn <jannh@google.com> | |
14 | Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com> | |
15 | --- | |
16 | src/lxc/lxc_user_nic.c | 119 ++++++++++++++++++++++++++++++++++++------------- | |
17 | 1 file changed, 87 insertions(+), 32 deletions(-) | |
18 | ||
19 | diff --git a/src/lxc/lxc_user_nic.c b/src/lxc/lxc_user_nic.c | |
20 | index 409a53a..96dc398 100644 | |
21 | --- a/src/lxc/lxc_user_nic.c | |
22 | +++ b/src/lxc/lxc_user_nic.c | |
23 | @@ -50,6 +50,14 @@ | |
24 | #include "utils.h" | |
25 | #include "network.h" | |
26 | ||
27 | +#define usernic_debug_stream(stream, format, ...) \ | |
28 | + do { \ | |
29 | + fprintf(stream, "%s: %d: %s: " format, __FILE__, __LINE__, \ | |
30 | + __func__, __VA_ARGS__); \ | |
31 | + } while (false) | |
32 | + | |
33 | +#define usernic_error(format, ...) usernic_debug_stream(stderr, format, __VA_ARGS__) | |
34 | + | |
35 | static void usage(char *me, bool fail) | |
36 | { | |
37 | fprintf(stderr, "Usage: %s lxcpath name pid type bridge nicname\n", me); | |
38 | @@ -670,68 +678,115 @@ again: | |
39 | } | |
40 | ||
41 | #define VETH_DEF_NAME "eth%d" | |
42 | - | |
43 | static int rename_in_ns(int pid, char *oldname, char **newnamep) | |
44 | { | |
45 | - int fd = -1, ofd = -1, ret, ifindex = -1; | |
46 | + uid_t ruid, suid, euid; | |
47 | + int fret = -1; | |
48 | + int fd = -1, ifindex = -1, ofd = -1, ret; | |
49 | bool grab_newname = false; | |
50 | ||
51 | ofd = lxc_preserve_ns(getpid(), "net"); | |
52 | if (ofd < 0) { | |
53 | - fprintf(stderr, "Failed opening network namespace path for '%d'.", getpid()); | |
54 | - return -1; | |
55 | + usernic_error("Failed opening network namespace path for '%d'.", getpid()); | |
56 | + return fret; | |
57 | } | |
58 | ||
59 | fd = lxc_preserve_ns(pid, "net"); | |
60 | if (fd < 0) { | |
61 | - fprintf(stderr, "Failed opening network namespace path for '%d'.", pid); | |
62 | - return -1; | |
63 | + usernic_error("Failed opening network namespace path for '%d'.", pid); | |
64 | + goto do_partial_cleanup; | |
65 | + } | |
66 | + | |
67 | + ret = getresuid(&ruid, &euid, &suid); | |
68 | + if (ret < 0) { | |
69 | + usernic_error("Failed to retrieve real, effective, and saved " | |
70 | + "user IDs: %s\n", | |
71 | + strerror(errno)); | |
72 | + goto do_partial_cleanup; | |
73 | + } | |
74 | + | |
75 | + ret = setns(fd, CLONE_NEWNET); | |
76 | + close(fd); | |
77 | + fd = -1; | |
78 | + if (ret < 0) { | |
79 | + usernic_error("Failed to setns() to the network namespace of " | |
80 | + "the container with PID %d: %s.\n", | |
81 | + pid, strerror(errno)); | |
82 | + goto do_partial_cleanup; | |
83 | } | |
84 | ||
85 | - if (setns(fd, 0) < 0) { | |
86 | - fprintf(stderr, "setns to container network namespace\n"); | |
87 | - goto out_err; | |
88 | + ret = setresuid(ruid, ruid, 0); | |
89 | + if (ret < 0) { | |
90 | + usernic_error("Failed to drop privilege by setting effective " | |
91 | + "user id and real user id to %d, and saved user " | |
92 | + "ID to 0: %s.\n", | |
93 | + ruid, strerror(errno)); | |
94 | + // COMMENT(brauner): It's ok to jump to do_full_cleanup here | |
95 | + // since setresuid() will succeed when trying to set real, | |
96 | + // effective, and saved to values they currently have. | |
97 | + goto do_full_cleanup; | |
98 | } | |
99 | - close(fd); fd = -1; | |
100 | + | |
101 | if (!*newnamep) { | |
102 | grab_newname = true; | |
103 | *newnamep = VETH_DEF_NAME; | |
104 | - if (!(ifindex = if_nametoindex(oldname))) { | |
105 | - fprintf(stderr, "failed to get netdev index\n"); | |
106 | - goto out_err; | |
107 | + | |
108 | + ifindex = if_nametoindex(oldname); | |
109 | + if (!ifindex) { | |
110 | + usernic_error("Failed to get netdev index: %s.\n", strerror(errno)); | |
111 | + goto do_full_cleanup; | |
112 | } | |
113 | } | |
114 | - if ((ret = lxc_netdev_rename_by_name(oldname, *newnamep)) < 0) { | |
115 | - fprintf(stderr, "Error %d renaming netdev %s to %s in container\n", ret, oldname, *newnamep); | |
116 | - goto out_err; | |
117 | + | |
118 | + ret = lxc_netdev_rename_by_name(oldname, *newnamep); | |
119 | + if (ret < 0) { | |
120 | + usernic_error("Error %d renaming netdev %s to %s in container.\n", ret, oldname, *newnamep); | |
121 | + goto do_full_cleanup; | |
122 | } | |
123 | + | |
124 | if (grab_newname) { | |
125 | - char ifname[IFNAMSIZ], *namep = ifname; | |
126 | + char ifname[IFNAMSIZ]; | |
127 | + char *namep = ifname; | |
128 | + | |
129 | if (!if_indextoname(ifindex, namep)) { | |
130 | - fprintf(stderr, "Failed to get new netdev name\n"); | |
131 | - goto out_err; | |
132 | + usernic_error("Failed to get new netdev name: %s.\n", strerror(errno)); | |
133 | + goto do_full_cleanup; | |
134 | } | |
135 | + | |
136 | *newnamep = strdup(namep); | |
137 | if (!*newnamep) | |
138 | - goto out_err; | |
139 | + goto do_full_cleanup; | |
140 | } | |
141 | - if (setns(ofd, 0) < 0) { | |
142 | - fprintf(stderr, "Error returning to original netns\n"); | |
143 | - close(ofd); | |
144 | - return -1; | |
145 | + | |
146 | + fret = 0; | |
147 | + | |
148 | +do_full_cleanup: | |
149 | + ret = setresuid(ruid, euid, suid); | |
150 | + if (ret < 0) { | |
151 | + usernic_error("Failed to restore privilege by setting effective " | |
152 | + "user id to %d, real user id to %d, and saved user " | |
153 | + "ID to %d: %s.\n", | |
154 | + ruid, euid, suid, strerror(errno)); | |
155 | + fret = -1; | |
156 | + // COMMENT(brauner): setns() should fail if setresuid() doesn't | |
157 | + // succeed but there's no harm in falling through; keeps the | |
158 | + // code cleaner. | |
159 | } | |
160 | - close(ofd); | |
161 | ||
162 | - return 0; | |
163 | + ret = setns(ofd, CLONE_NEWNET); | |
164 | + if (ret < 0) { | |
165 | + usernic_error("Failed to setns() to original network namespace " | |
166 | + "of PID %d: %s.\n", | |
167 | + ofd, strerror(errno)); | |
168 | + fret = -1; | |
169 | + } | |
170 | ||
171 | -out_err: | |
172 | - if (ofd >= 0) | |
173 | - close(ofd); | |
174 | - if (setns(ofd, 0) < 0) | |
175 | - fprintf(stderr, "Error returning to original network namespace\n"); | |
176 | +do_partial_cleanup: | |
177 | if (fd >= 0) | |
178 | close(fd); | |
179 | - return -1; | |
180 | + close(ofd); | |
181 | + | |
182 | + return fret; | |
183 | } | |
184 | ||
185 | /* | |
186 | -- | |
187 | 2.1.4 | |
188 |