3 * Copyright © 2018 Elizaveta Tretiakova <elizabet.tretyakova@gmail.com>.
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.
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.
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.
24 #include <lxc/lxccontainer.h>
27 #include <sys/mount.h>
29 #include <sys/types.h>
36 #define NAME "mount_injection_test-"
37 #define TEMPLATE P_tmpdir"/mount_injection_XXXXXX"
39 struct mountinfo_data
{
40 const char *mount_root
;
41 const char *mount_point
;
43 const char *mount_source
;
45 bool should_be_present
;
48 static int comp_field(char *line
, const char *str
, int nfields
)
59 for (p
= line
, i
= 0; p
&& i
< nfields
; i
++)
60 p
= strchr(p
+ 1, ' ');
63 p2
= strchr(p
+ 1, ' ');
66 ret
= strcmp(p
+ 1, str
);
72 static int find_in_proc_mounts(void *data
)
74 char buf
[LXC_LINELEN
];
76 struct mountinfo_data
*mdata
= (struct mountinfo_data
*)data
;
78 fprintf(stderr
, "%s", mdata
->message
);
80 f
= fopen("/proc/self/mountinfo", "r");
84 while (fgets(buf
, LXC_LINELEN
, f
)) {
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
92 if (mdata
->mount_root
&& comp_field(buf
, mdata
->mount_root
, 3) != 0)
95 if (comp_field(buf
, mdata
->mount_point
, 4) != 0)
98 if (!mdata
->fstype
|| !mdata
->mount_source
)
101 buf2
= strchr(buf
, '-');
102 if (comp_field(buf2
, mdata
->fstype
, 1) != 0 ||
103 comp_field(buf2
, mdata
->mount_source
, 2) != 0)
108 fprintf(stderr
, "PRESENT\n");
109 if (mdata
->should_be_present
)
116 fprintf(stderr
, "MISSING\n");
117 if (!mdata
->should_be_present
)
123 static int check_containers_mountinfo(struct lxc_container
*c
, struct mountinfo_data
*d
)
127 lxc_attach_options_t attach_options
= LXC_ATTACH_OPTIONS_DEFAULT
;
129 ret
= c
->attach(c
, find_in_proc_mounts
, d
, &attach_options
, &pid
);
131 fprintf(stderr
, "Check of the container's mountinfo failed\n");
135 ret
= wait_for_pid(pid
);
137 fprintf(stderr
, "Attached function failed\n");
142 /* config_items: NULL-terminated array of config pairs */
143 static int perform_container_test(const char *name
, const char *config_items
[])
147 char template_log
[sizeof(TEMPLATE
)], template_dir
[sizeof(TEMPLATE
)],
148 device_message
[4096],
151 struct lxc_container
*c
;
152 struct lxc_mount mnt
;
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",
161 .mount_source
= "/dev/urandom",
163 .should_be_present
= true
166 .mount_point
= template_dir
,
168 .mount_source
= NULL
,
170 .should_be_present
= true
173 .mount_point
= "/mnt/mount_injection_test_devtmpfs",
174 .fstype
= "devtmpfs",
175 .mount_source
= NULL
,
177 .should_be_present
= true
180 /* Temp paths and messages setup */
181 strcpy(template_dir
, TEMPLATE
);
182 sret
= mkdtemp(template_dir
);
184 lxc_error("Failed to create temporary src file for container %s\n", name
);
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");
193 device
.message
= &device_message
[0];
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");
200 dir
.message
= &dir_message
[0];
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");
207 fs
.message
= &fs_message
[0];
210 strcpy(template_log
, TEMPLATE
);
211 i
= lxc_make_tmpfile(template_log
, false);
213 lxc_error("Failed to create temporary log file for container %s\n", name
);
216 lxc_debug("Using \"%s\" as temporary log file for container %s\n", template_log
, name
);
221 log
.file
= template_log
;
223 log
.prefix
= "mount-injection";
226 if (lxc_log_init(&log
))
229 /* Container setup */
230 c
= lxc_container_new(name
, NULL
);
232 fprintf(stderr
, "Unable to instantiate container (%s)...\n", name
);
236 if (c
->is_defined(c
)) {
237 fprintf(stderr
, "Container (%s) already exists\n", name
);
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]);
248 if (!c
->create(c
, "busybox", NULL
, NULL
, 1, NULL
)) {
249 fprintf(stderr
, "Creating the container (%s) failed...\n", name
);
253 c
->want_daemonize(c
, true);
255 if (!c
->start(c
, false, NULL
)) {
256 fprintf(stderr
, "Starting the container (%s) failed...\n", name
);
260 mnt
.version
= LXC_MOUNT_API_V1
;
262 /* Check device mounted */
263 ret
= c
->mount(c
, "/dev/urandom", "/mnt/mount_injection_test_urandom", NULL
, MS_BIND
, NULL
, &mnt
);
265 fprintf(stderr
, "Failed to mount \"/dev/urandom\"\n");
269 ret
= check_containers_mountinfo(c
, &device
);
273 /* Check device unmounted */
274 /* TODO: what about other umount flags? */
275 ret
= c
->umount(c
, "/mnt/mount_injection_test_urandom", MNT_DETACH
, &mnt
);
277 fprintf(stderr
, "Failed to umount2 \"/dev/urandom\"\n");
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
);
287 /* Check dir mounted */
288 ret
= c
->mount(c
, template_dir
, template_dir
, NULL
, MS_BIND
, NULL
, &mnt
);
290 fprintf(stderr
, "Failed to mount \"%s\"\n", template_dir
);
294 ret
= check_containers_mountinfo(c
, &dir
);
298 /* Check dir unmounted */
299 /* TODO: what about other umount flags? */
300 ret
= c
->umount(c
, template_dir
, MNT_DETACH
, &mnt
);
302 fprintf(stderr
, "Failed to umount2 \"%s\"\n", template_dir
);
306 dir
.message
= "Unmounted dir -- should be missing now: ";
307 dir
.should_be_present
= false;
308 ret
= check_containers_mountinfo(c
, &dir
);
312 /* Check fs mounted */
313 ret
= c
->mount(c
, NULL
, "/mnt/mount_injection_test_devtmpfs", "devtmpfs", 0, NULL
, &mnt
);
315 fprintf(stderr
, "Failed to mount devtmpfs\n");
319 ret
= check_containers_mountinfo(c
, &fs
);
323 /* Check fs unmounted */
324 /* TODO: what about other umount flags? */
325 ret
= c
->umount(c
, "/mnt/mount_injection_test_devtmpfs", MNT_DETACH
, &mnt
);
327 fprintf(stderr
, "Failed to umount2 devtmpfs\n");
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
);
337 /* Finalize the container */
339 fprintf(stderr
, "Stopping the container (%s) failed...\n", name
);
343 if (!c
->destroy(c
)) {
344 fprintf(stderr
, "Destroying the container (%s) failed...\n", name
);
350 lxc_container_put(c
);
355 fd
= open(template_log
, O_RDONLY
);
359 while ((buflen
= read(fd
, buf
, 1024)) > 0) {
360 buflen
= write(STDERR_FILENO
, buf
, buflen
);
368 unlink(template_log
);
369 unlink(template_dir
);
374 static int do_priv_container_test()
376 const char *config_items
[] = {"lxc.mount.auto", "shmounts:/tmp/mount_injection_test", NULL
};
377 return perform_container_test(NAME
"privileged", config_items
);
380 static int do_unpriv_container_test()
382 const char *config_items
[] = {
383 "lxc.mount.auto", "shmounts:/tmp/mount_injection_test",
386 return perform_container_test(NAME
"unprivileged", config_items
);
389 int main(int argc
, char *argv
[])
391 if (do_priv_container_test()) {
392 fprintf(stderr
, "Privileged mount injection test failed\n");
396 if(do_unpriv_container_test()) {
397 fprintf(stderr
, "Unprivileged mount injection test failed\n");