]> git.proxmox.com Git - mirror_lxc.git/blob - src/tests/mount_injection.c
spelling: timeout
[mirror_lxc.git] / src / tests / mount_injection.c
1 /* mount_injection
2 *
3 * Copyright © 2018 Elizaveta Tretiakova <elizabet.tretyakova@gmail.com>.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2, as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <lxc/lxccontainer.h>
25 #include <lxc/list.h>
26 #include <string.h>
27 #include <sys/mount.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #include "config.h"
33 #include "lxctest.h"
34 #include "utils.h"
35
36 #define NAME "mount_injection_test-"
37 #define TEMPLATE P_tmpdir"/mount_injection_XXXXXX"
38
39 struct mountinfo_data {
40 const char *mount_root;
41 const char *mount_point;
42 const char *fstype;
43 const char *mount_source;
44 const char *message;
45 bool should_be_present;
46 };
47
48 static int comp_field(char *line, const char *str, int nfields)
49 {
50 char *p, *p2;
51 int i, ret;
52
53 if(!line)
54 return -1;
55
56 if (!str)
57 return 0;
58
59 for (p = line, i = 0; p && i < nfields; i++)
60 p = strchr(p + 1, ' ');
61 if (!p)
62 return -1;
63 p2 = strchr(p + 1, ' ');
64 if (p2)
65 *p2 = '\0';
66 ret = strcmp(p + 1, str);
67 if (p2)
68 *p2 = ' ';
69 return ret;
70 }
71
72 static int find_in_proc_mounts(void *data)
73 {
74 char buf[LXC_LINELEN];
75 FILE *f;
76 struct mountinfo_data *mdata = (struct mountinfo_data *)data;
77
78 fprintf(stderr, "%s", mdata->message);
79
80 f = fopen("/proc/self/mountinfo", "r");
81 if (!f)
82 return 0;
83
84 while (fgets(buf, LXC_LINELEN, f)) {
85 char *buf2;
86
87 /* checking mount_root is tricky since it will be prefixed with
88 * whatever path was the source of the mount in the original
89 * mount namespace. So only verify it when we know that root is
90 * in fact "/".
91 */
92 if (mdata->mount_root && comp_field(buf, mdata->mount_root, 3) != 0)
93 continue;
94
95 if (comp_field(buf, mdata->mount_point, 4) != 0)
96 continue;
97
98 if (!mdata->fstype || !mdata->mount_source)
99 goto on_success;
100
101 buf2 = strchr(buf, '-');
102 if (comp_field(buf2, mdata->fstype, 1) != 0 ||
103 comp_field(buf2, mdata->mount_source, 2) != 0)
104 continue;
105
106 on_success:
107 fclose(f);
108 fprintf(stderr, "PRESENT\n");
109 if (mdata->should_be_present)
110 _exit(EXIT_SUCCESS);
111
112 _exit(EXIT_FAILURE);
113 }
114
115 fclose(f);
116 fprintf(stderr, "MISSING\n");
117 if (!mdata->should_be_present)
118 _exit(EXIT_SUCCESS);
119
120 _exit(EXIT_FAILURE);
121 }
122
123 static int check_containers_mountinfo(struct lxc_container *c, struct mountinfo_data *d)
124 {
125 pid_t pid;
126 int ret = -1;
127 lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
128
129 ret = c->attach(c, find_in_proc_mounts, d, &attach_options, &pid);
130 if (ret < 0) {
131 fprintf(stderr, "Check of the container's mountinfo failed\n");
132 return ret;
133 }
134
135 ret = wait_for_pid(pid);
136 if (ret < 0)
137 fprintf(stderr, "Attached function failed\n");
138
139 return ret;
140 }
141
142 /* config_items: NULL-terminated array of config pairs */
143 static int perform_container_test(const char *name, const char *config_items[])
144 {
145 int i;
146 char *sret;
147 char template_log[sizeof(TEMPLATE)], template_dir[sizeof(TEMPLATE)],
148 device_message[4096],
149 dir_message[4096],
150 fs_message[4096];
151 struct lxc_container *c;
152 struct lxc_mount mnt;
153 struct lxc_log log;
154 int ret = -1, dev_msg_size = sizeof("Check urandom device injected into "" - ") - 1 + strlen(name) + 1,
155 dir_msg_size = sizeof("Check dir "" injected into "" - ") - 1 + sizeof(TEMPLATE) - 1 + strlen(name) + 1,
156 fs_msg_size = sizeof("Check devtmpfs injected into "" - ") - 1 + strlen(name) + 1;
157 struct mountinfo_data device = {
158 .mount_root = "/urandom",
159 .mount_point = "/mnt/mount_injection_test_urandom",
160 .fstype = NULL,
161 .mount_source = "/dev/urandom",
162 .message = "",
163 .should_be_present = true
164 }, dir = {
165 .mount_root = NULL,
166 .mount_point = template_dir,
167 .fstype = NULL,
168 .mount_source = NULL,
169 .message = "",
170 .should_be_present = true
171 }, fs = {
172 .mount_root = "/",
173 .mount_point = "/mnt/mount_injection_test_devtmpfs",
174 .fstype = "devtmpfs",
175 .mount_source = NULL,
176 .message = "",
177 .should_be_present = true
178 };
179
180 /* Temp paths and messages setup */
181 strcpy(template_dir, TEMPLATE);
182 sret = mkdtemp(template_dir);
183 if (!sret) {
184 lxc_error("Failed to create temporary src file for container %s\n", name);
185 exit(EXIT_FAILURE);
186 }
187
188 ret = snprintf(device_message, dev_msg_size, "Check urandom device injected into %s - ", name);
189 if (ret < 0 || ret >= dev_msg_size) {
190 fprintf(stderr, "Failed to create message for dev\n");
191 exit(EXIT_FAILURE);
192 }
193 device.message = &device_message[0];
194
195 ret = snprintf(dir_message, dir_msg_size, "Check dir %s injected into %s - ", template_dir, name);
196 if (ret < 0 || ret >= dir_msg_size) {
197 fprintf(stderr, "Failed to create message for dir\n");
198 exit(EXIT_FAILURE);
199 }
200 dir.message = &dir_message[0];
201
202 ret = snprintf(fs_message, fs_msg_size, "Check devtmpfs injected into %s - ", name);
203 if (ret < 0 || ret >= fs_msg_size) {
204 fprintf(stderr, "Failed to create message for fs\n");
205 exit(EXIT_FAILURE);
206 }
207 fs.message = &fs_message[0];
208
209 /* Setup logging*/
210 strcpy(template_log, TEMPLATE);
211 i = lxc_make_tmpfile(template_log, false);
212 if (i < 0) {
213 lxc_error("Failed to create temporary log file for container %s\n", name);
214 exit(EXIT_FAILURE);
215 } else {
216 lxc_debug("Using \"%s\" as temporary log file for container %s\n", template_log, name);
217 close(i);
218 }
219
220 log.name = name;
221 log.file = template_log;
222 log.level = "TRACE";
223 log.prefix = "mount-injection";
224 log.quiet = false;
225 log.lxcpath = NULL;
226 if (lxc_log_init(&log))
227 exit(EXIT_FAILURE);
228
229 /* Container setup */
230 c = lxc_container_new(name, NULL);
231 if (!c) {
232 fprintf(stderr, "Unable to instantiate container (%s)...\n", name);
233 goto out;
234 }
235
236 if (c->is_defined(c)) {
237 fprintf(stderr, "Container (%s) already exists\n", name);
238 goto out;
239 }
240
241 for (i = 0; config_items[i]; i += 2) {
242 if (!c->set_config_item(c, config_items[i], config_items[i + 1])) {
243 fprintf(stderr, "Failed to set \"%s\" config option to \"%s\"\n", config_items[i], config_items[i + 1]);
244 goto out;
245 }
246 }
247
248 if (!c->create(c, "busybox", NULL, NULL, 1, NULL)) {
249 fprintf(stderr, "Creating the container (%s) failed...\n", name);
250 goto out;
251 }
252
253 c->want_daemonize(c, true);
254
255 if (!c->start(c, false, NULL)) {
256 fprintf(stderr, "Starting the container (%s) failed...\n", name);
257 goto out;
258 }
259
260 mnt.version = LXC_MOUNT_API_V1;
261
262 /* Check device mounted */
263 ret = c->mount(c, "/dev/urandom", "/mnt/mount_injection_test_urandom", NULL, MS_BIND, NULL, &mnt);
264 if (ret < 0) {
265 fprintf(stderr, "Failed to mount \"/dev/urandom\"\n");
266 goto out;
267 }
268
269 ret = check_containers_mountinfo(c, &device);
270 if (ret < 0)
271 goto out;
272
273 /* Check device unmounted */
274 /* TODO: what about other umount flags? */
275 ret = c->umount(c, "/mnt/mount_injection_test_urandom", MNT_DETACH, &mnt);
276 if (ret < 0) {
277 fprintf(stderr, "Failed to umount2 \"/dev/urandom\"\n");
278 goto out;
279 }
280
281 device.message = "Unmounted \"/mnt/mount_injection_test_urandom\" -- should be missing now: ";
282 device.should_be_present = false;
283 ret = check_containers_mountinfo(c, &device);
284 if (ret < 0)
285 goto out;
286
287 /* Check dir mounted */
288 ret = c->mount(c, template_dir, template_dir, NULL, MS_BIND, NULL, &mnt);
289 if (ret < 0) {
290 fprintf(stderr, "Failed to mount \"%s\"\n", template_dir);
291 goto out;
292 }
293
294 ret = check_containers_mountinfo(c, &dir);
295 if (ret < 0)
296 goto out;
297
298 /* Check dir unmounted */
299 /* TODO: what about other umount flags? */
300 ret = c->umount(c, template_dir, MNT_DETACH, &mnt);
301 if (ret < 0) {
302 fprintf(stderr, "Failed to umount2 \"%s\"\n", template_dir);
303 goto out;
304 }
305
306 dir.message = "Unmounted dir -- should be missing now: ";
307 dir.should_be_present = false;
308 ret = check_containers_mountinfo(c, &dir);
309 if (ret < 0)
310 goto out;
311
312 /* Check fs mounted */
313 ret = c->mount(c, NULL, "/mnt/mount_injection_test_devtmpfs", "devtmpfs", 0, NULL, &mnt);
314 if (ret < 0) {
315 fprintf(stderr, "Failed to mount devtmpfs\n");
316 goto out;
317 }
318
319 ret = check_containers_mountinfo(c, &fs);
320 if (ret < 0)
321 goto out;
322
323 /* Check fs unmounted */
324 /* TODO: what about other umount flags? */
325 ret = c->umount(c, "/mnt/mount_injection_test_devtmpfs", MNT_DETACH, &mnt);
326 if (ret < 0) {
327 fprintf(stderr, "Failed to umount2 devtmpfs\n");
328 goto out;
329 }
330
331 fs.message = "Unmounted \"/mnt/mount_injection_test_devtmpfs\" -- should be missing now: ";
332 fs.should_be_present = false;
333 ret = check_containers_mountinfo(c, &fs);
334 if (ret < 0)
335 goto out;
336
337 /* Finalize the container */
338 if (!c->stop(c)) {
339 fprintf(stderr, "Stopping the container (%s) failed...\n", name);
340 goto out;
341 }
342
343 if (!c->destroy(c)) {
344 fprintf(stderr, "Destroying the container (%s) failed...\n", name);
345 goto out;
346 }
347
348 ret = 0;
349 out:
350 lxc_container_put(c);
351
352 if (ret != 0) {
353 int fd;
354
355 fd = open(template_log, O_RDONLY);
356 if (fd >= 0) {
357 char buf[4096];
358 ssize_t buflen;
359 while ((buflen = read(fd, buf, 1024)) > 0) {
360 buflen = write(STDERR_FILENO, buf, buflen);
361 if (buflen <= 0)
362 break;
363 }
364 close(fd);
365 }
366 }
367
368 unlink(template_log);
369 unlink(template_dir);
370
371 return ret;
372 }
373
374 static int do_priv_container_test()
375 {
376 const char *config_items[] = {"lxc.mount.auto", "shmounts:/tmp/mount_injection_test", NULL};
377 return perform_container_test(NAME"privileged", config_items);
378 }
379
380 static int do_unpriv_container_test()
381 {
382 const char *config_items[] = {
383 "lxc.mount.auto", "shmounts:/tmp/mount_injection_test",
384 NULL
385 };
386 return perform_container_test(NAME"unprivileged", config_items);
387 }
388
389 int main(int argc, char *argv[])
390 {
391 if (do_priv_container_test()) {
392 fprintf(stderr, "Privileged mount injection test failed\n");
393 return -1;
394 }
395
396 if(do_unpriv_container_test()) {
397 fprintf(stderr, "Unprivileged mount injection test failed\n");
398 return -1;
399 }
400 return 0;
401 }