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.
25 #include <lxc/lxccontainer.h>
27 #include <sys/mount.h>
29 #include <sys/types.h>
37 #define NAME "mount_injection_test-"
38 #define TEMPLATE P_tmpdir"/mount_injection_XXXXXX"
40 struct mountinfo_data
{
41 const char *mount_root
;
42 const char *mount_point
;
44 const char *mount_source
;
46 bool should_be_present
;
49 static int comp_field(char *line
, const char *str
, int nfields
)
60 for (p
= line
, i
= 0; p
&& i
< nfields
; i
++)
61 p
= strchr(p
+ 1, ' ');
64 p2
= strchr(p
+ 1, ' ');
67 ret
= strcmp(p
+ 1, str
);
73 static int find_in_proc_mounts(void *data
)
75 char buf
[LXC_LINELEN
];
77 struct mountinfo_data
*mdata
= (struct mountinfo_data
*)data
;
79 fprintf(stderr
, "%s", mdata
->message
);
81 f
= fopen("/proc/self/mountinfo", "r");
85 while (fgets(buf
, LXC_LINELEN
, f
)) {
88 /* checking mount_root is tricky since it will be prefixed with
89 * whatever path was the source of the mount in the original
90 * mount namespace. So only verify it when we know that root is
93 if (mdata
->mount_root
&& comp_field(buf
, mdata
->mount_root
, 3) != 0)
96 if (comp_field(buf
, mdata
->mount_point
, 4) != 0)
99 if (!mdata
->fstype
|| !mdata
->mount_source
)
102 buf2
= strchr(buf
, '-');
103 if (comp_field(buf2
, mdata
->fstype
, 1) != 0 ||
104 comp_field(buf2
, mdata
->mount_source
, 2) != 0)
109 fprintf(stderr
, "PRESENT\n");
110 if (mdata
->should_be_present
)
117 fprintf(stderr
, "MISSING\n");
118 if (!mdata
->should_be_present
)
124 static int check_containers_mountinfo(struct lxc_container
*c
, struct mountinfo_data
*d
)
128 lxc_attach_options_t attach_options
= LXC_ATTACH_OPTIONS_DEFAULT
;
130 ret
= c
->attach(c
, find_in_proc_mounts
, d
, &attach_options
, &pid
);
132 fprintf(stderr
, "Check of the container's mountinfo failed\n");
136 ret
= wait_for_pid(pid
);
138 fprintf(stderr
, "Attached function failed\n");
143 /* config_items: NULL-terminated array of config pairs */
144 static int perform_container_test(const char *name
, const char *config_items
[])
148 char template_log
[sizeof(TEMPLATE
)], template_dir
[sizeof(TEMPLATE
)],
149 device_message
[4096],
152 struct lxc_container
*c
;
153 struct lxc_mount mnt
;
155 int ret
= -1, dev_msg_size
= sizeof("Check urandom device injected into "" - ") - 1 + strlen(name
) + 1,
156 dir_msg_size
= sizeof("Check dir "" injected into "" - ") - 1 + sizeof(TEMPLATE
) - 1 + strlen(name
) + 1,
157 fs_msg_size
= sizeof("Check devtmpfs injected into "" - ") - 1 + strlen(name
) + 1;
158 struct mountinfo_data device
= {
159 .mount_root
= "/urandom",
160 .mount_point
= "/mnt/mount_injection_test_urandom",
162 .mount_source
= "/dev/urandom",
164 .should_be_present
= true
167 .mount_point
= template_dir
,
169 .mount_source
= NULL
,
171 .should_be_present
= true
174 .mount_point
= "/mnt/mount_injection_test_devtmpfs",
175 .fstype
= "devtmpfs",
176 .mount_source
= NULL
,
178 .should_be_present
= true
181 /* Temp paths and messages setup */
182 strcpy(template_dir
, TEMPLATE
);
183 sret
= mkdtemp(template_dir
);
185 lxc_error("Failed to create temporary src file for container %s\n", name
);
189 ret
= snprintf(device_message
, dev_msg_size
, "Check urandom device injected into %s - ", name
);
190 if (ret
< 0 || ret
>= dev_msg_size
) {
191 fprintf(stderr
, "Failed to create message for dev\n");
194 device
.message
= &device_message
[0];
196 ret
= snprintf(dir_message
, dir_msg_size
, "Check dir %s injected into %s - ", template_dir
, name
);
197 if (ret
< 0 || ret
>= dir_msg_size
) {
198 fprintf(stderr
, "Failed to create message for dir\n");
201 dir
.message
= &dir_message
[0];
203 ret
= snprintf(fs_message
, fs_msg_size
, "Check devtmpfs injected into %s - ", name
);
204 if (ret
< 0 || ret
>= fs_msg_size
) {
205 fprintf(stderr
, "Failed to create message for fs\n");
208 fs
.message
= &fs_message
[0];
211 strcpy(template_log
, TEMPLATE
);
212 i
= lxc_make_tmpfile(template_log
, false);
214 lxc_error("Failed to create temporary log file for container %s\n", name
);
217 lxc_debug("Using \"%s\" as temporary log file for container %s\n", template_log
, name
);
222 log
.file
= template_log
;
224 log
.prefix
= "mount-injection";
227 if (lxc_log_init(&log
))
230 /* Container setup */
231 c
= lxc_container_new(name
, NULL
);
233 fprintf(stderr
, "Unable to instantiate container (%s)...\n", name
);
237 if (c
->is_defined(c
)) {
238 fprintf(stderr
, "Container (%s) already exists\n", name
);
242 for (i
= 0; config_items
[i
]; i
+= 2) {
243 if (!c
->set_config_item(c
, config_items
[i
], config_items
[i
+ 1])) {
244 fprintf(stderr
, "Failed to set \"%s\" config option to \"%s\"\n", config_items
[i
], config_items
[i
+ 1]);
249 if (!c
->create(c
, "busybox", NULL
, NULL
, 1, NULL
)) {
250 fprintf(stderr
, "Creating the container (%s) failed...\n", name
);
254 c
->want_daemonize(c
, true);
256 if (!c
->start(c
, false, NULL
)) {
257 fprintf(stderr
, "Starting the container (%s) failed...\n", name
);
261 mnt
.version
= LXC_MOUNT_API_V1
;
263 /* Check device mounted */
264 ret
= c
->mount(c
, "/dev/urandom", "/mnt/mount_injection_test_urandom", NULL
, MS_BIND
, NULL
, &mnt
);
266 fprintf(stderr
, "Failed to mount \"/dev/urandom\"\n");
270 ret
= check_containers_mountinfo(c
, &device
);
274 /* Check device unmounted */
275 /* TODO: what about other umount flags? */
276 ret
= c
->umount(c
, "/mnt/mount_injection_test_urandom", MNT_DETACH
, &mnt
);
278 fprintf(stderr
, "Failed to umount2 \"/dev/urandom\"\n");
282 device
.message
= "Unmounted \"/mnt/mount_injection_test_urandom\" -- should be missing now: ";
283 device
.should_be_present
= false;
284 ret
= check_containers_mountinfo(c
, &device
);
288 /* Check dir mounted */
289 ret
= c
->mount(c
, template_dir
, template_dir
, NULL
, MS_BIND
, NULL
, &mnt
);
291 fprintf(stderr
, "Failed to mount \"%s\"\n", template_dir
);
295 ret
= check_containers_mountinfo(c
, &dir
);
299 /* Check dir unmounted */
300 /* TODO: what about other umount flags? */
301 ret
= c
->umount(c
, template_dir
, MNT_DETACH
, &mnt
);
303 fprintf(stderr
, "Failed to umount2 \"%s\"\n", template_dir
);
307 dir
.message
= "Unmounted dir -- should be missing now: ";
308 dir
.should_be_present
= false;
309 ret
= check_containers_mountinfo(c
, &dir
);
313 /* Check fs mounted */
314 ret
= c
->mount(c
, NULL
, "/mnt/mount_injection_test_devtmpfs", "devtmpfs", 0, NULL
, &mnt
);
316 fprintf(stderr
, "Failed to mount devtmpfs\n");
320 ret
= check_containers_mountinfo(c
, &fs
);
324 /* Check fs unmounted */
325 /* TODO: what about other umount flags? */
326 ret
= c
->umount(c
, "/mnt/mount_injection_test_devtmpfs", MNT_DETACH
, &mnt
);
328 fprintf(stderr
, "Failed to umount2 devtmpfs\n");
332 fs
.message
= "Unmounted \"/mnt/mount_injection_test_devtmpfs\" -- should be missing now: ";
333 fs
.should_be_present
= false;
334 ret
= check_containers_mountinfo(c
, &fs
);
338 /* Finalize the container */
340 fprintf(stderr
, "Stopping the container (%s) failed...\n", name
);
344 if (!c
->destroy(c
)) {
345 fprintf(stderr
, "Destroying the container (%s) failed...\n", name
);
351 lxc_container_put(c
);
356 fd
= open(template_log
, O_RDONLY
);
360 while ((buflen
= read(fd
, buf
, 1024)) > 0) {
361 buflen
= write(STDERR_FILENO
, buf
, buflen
);
369 unlink(template_log
);
370 unlink(template_dir
);
375 static int do_priv_container_test(void)
377 const char *config_items
[] = {"lxc.mount.auto", "shmounts:/tmp/mount_injection_test", NULL
};
378 return perform_container_test(NAME
"privileged", config_items
);
381 static int do_unpriv_container_test(void)
383 const char *config_items
[] = {
384 "lxc.mount.auto", "shmounts:/tmp/mount_injection_test",
387 return perform_container_test(NAME
"unprivileged", config_items
);
390 static bool lxc_setup_shmount(const char *shmount_path
)
394 ret
= mkdir_p(shmount_path
, 0711);
395 if (ret
< 0 && errno
!= EEXIST
) {
396 fprintf(stderr
, "Failed to create directory \"%s\"\n", shmount_path
);
400 /* Prepare host mountpoint */
401 ret
= mount("tmpfs", shmount_path
, "tmpfs", 0, "size=100k,mode=0711");
403 fprintf(stderr
, "Failed to mount \"%s\"\n", shmount_path
);
407 ret
= mount(shmount_path
, shmount_path
, "none", MS_REC
| MS_SHARED
, "");
409 fprintf(stderr
, "Failed to make shared \"%s\"\n", shmount_path
);
416 static void lxc_teardown_shmount(char *shmount_path
)
418 (void)umount2(shmount_path
, MNT_DETACH
);
419 (void)lxc_rm_rf(shmount_path
);
422 int main(int argc
, char *argv
[])
424 if (!lxc_setup_shmount("/tmp/mount_injection_test"))
427 if (do_priv_container_test()) {
428 fprintf(stderr
, "Privileged mount injection test failed\n");
432 if (do_unpriv_container_test()) {
433 fprintf(stderr
, "Unprivileged mount injection test failed\n");
437 lxc_teardown_shmount("/tmp/mount_injection_test");