]>
Commit | Line | Data |
---|---|---|
72863294 DE |
1 | /* liblxcapi |
2 | * | |
3 | * Copyright © 2013 Oracle. | |
4 | * | |
5 | * Authors: | |
6 | * Dwight Engen <dwight.engen@oracle.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2, as | |
10 | * published by the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
20 | */ | |
21 | ||
e49c56d6 CB |
22 | #include "config.h" |
23 | ||
ba2be1a8 | 24 | #include <errno.h> |
95ee490b | 25 | #include <string.h> |
94ac256f | 26 | #include <unistd.h> |
95ee490b | 27 | #include <sys/stat.h> |
94ac256f | 28 | #include <sys/syscall.h> |
ba2be1a8 CB |
29 | #include <sys/types.h> |
30 | ||
31 | #include "lxctest.h" | |
32 | #include "utils.h" | |
33 | #include "lsm/lsm.h" | |
34 | ||
35 | #include <lxc/lxccontainer.h> | |
72863294 | 36 | |
34498dea | 37 | #if !HAVE_STRLCPY |
58db1a61 | 38 | #include "strlcpy.h" |
18cd4b54 DJ |
39 | #endif |
40 | ||
72863294 | 41 | #define TSTNAME "lxc-attach-test" |
c59ce7de SH |
42 | #define TSTOUT(fmt, ...) do { \ |
43 | fprintf(stdout, fmt, ##__VA_ARGS__); fflush(NULL); \ | |
44 | } while (0) | |
72863294 | 45 | #define TSTERR(fmt, ...) do { \ |
c59ce7de | 46 | fprintf(stderr, "%s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); fflush(NULL); \ |
72863294 DE |
47 | } while (0) |
48 | ||
72863294 DE |
49 | static const char *lsm_config_key = NULL; |
50 | static const char *lsm_label = NULL; | |
51 | ||
af04d847 | 52 | struct lsm_ops *lsm_ops; |
d701d729 | 53 | |
72863294 DE |
54 | static void test_lsm_detect(void) |
55 | { | |
af04d847 | 56 | if (lsm_ops->enabled(lsm_ops)) { |
d701d729 | 57 | if (!strcmp(lsm_ops->name, "SELinux")) { |
b84702ab | 58 | lsm_config_key = "lxc.selinux.context"; |
72863294 DE |
59 | lsm_label = "unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023"; |
60 | } | |
d701d729 | 61 | else if (!strcmp(lsm_ops->name, "AppArmor")) { |
a1d5fdfd | 62 | lsm_config_key = "lxc.apparmor.profile"; |
f58236fd SH |
63 | if (file_exists("/proc/self/ns/cgroup")) |
64 | lsm_label = "lxc-container-default-cgns"; | |
65 | else | |
66 | lsm_label = "lxc-container-default"; | |
72863294 DE |
67 | } |
68 | else { | |
d701d729 | 69 | TSTERR("unknown lsm %s enabled, add test code here", lsm_ops->name); |
72863294 DE |
70 | exit(EXIT_FAILURE); |
71 | } | |
72 | } | |
73 | } | |
74 | ||
bc605ac6 | 75 | #if HAVE_APPARMOR || HAVE_SELINUX |
72863294 DE |
76 | static void test_attach_lsm_set_config(struct lxc_container *ct) |
77 | { | |
78 | ct->load_config(ct, NULL); | |
79 | ct->set_config_item(ct, lsm_config_key, lsm_label); | |
80 | ct->save_config(ct, NULL); | |
81 | } | |
82 | ||
83 | static int test_attach_lsm_func_func(void* payload) | |
84 | { | |
af04d847 | 85 | TSTOUT("%s", lsm_ops->process_label_get(lsm_ops, syscall(SYS_getpid))); |
72863294 DE |
86 | return 0; |
87 | } | |
88 | ||
89 | static int test_attach_lsm_func(struct lxc_container *ct) | |
90 | { | |
91 | int ret; | |
92 | pid_t pid; | |
93 | int pipefd[2]; | |
94 | char result[1024]; | |
95 | lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; | |
96 | ||
c59ce7de | 97 | TSTOUT("Testing attach lsm label with func...\n"); |
72863294 DE |
98 | |
99 | ret = pipe(pipefd); | |
100 | if (ret < 0) { | |
101 | TSTERR("pipe failed %d", ret); | |
102 | return ret; | |
103 | } | |
104 | attach_options.stdout_fd = pipefd[1]; | |
105 | attach_options.attach_flags &= ~(LXC_ATTACH_LSM_EXEC|LXC_ATTACH_DROP_CAPABILITIES); | |
106 | attach_options.attach_flags |= LXC_ATTACH_LSM_NOW; | |
107 | ret = ct->attach(ct, test_attach_lsm_func_func, NULL, &attach_options, &pid); | |
108 | if (ret < 0) { | |
109 | TSTERR("attach failed"); | |
110 | goto err1; | |
111 | } | |
112 | ||
113 | ret = read(pipefd[0], result, sizeof(result)-1); | |
114 | if (ret < 0) { | |
115 | TSTERR("read failed %d", ret); | |
116 | goto err2; | |
117 | } | |
118 | ||
119 | result[ret] = '\0'; | |
120 | if (strcmp(lsm_label, result)) { | |
121 | TSTERR("LSM label mismatch expected:%s got:%s", lsm_label, result); | |
122 | ret = -1; | |
123 | goto err2; | |
124 | } | |
125 | ret = 0; | |
126 | ||
127 | err2: | |
dbdf8cf4 | 128 | (void)wait_for_pid(pid); |
72863294 DE |
129 | err1: |
130 | close(pipefd[0]); | |
131 | close(pipefd[1]); | |
132 | return ret; | |
133 | } | |
134 | ||
135 | static int test_attach_lsm_cmd(struct lxc_container *ct) | |
136 | { | |
137 | int ret; | |
138 | pid_t pid; | |
139 | int pipefd[2]; | |
140 | char result[1024]; | |
141 | char *space; | |
142 | char *argv[] = {"cat", "/proc/self/attr/current", NULL}; | |
143 | lxc_attach_command_t command = {"cat", argv}; | |
144 | lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; | |
145 | ||
c59ce7de | 146 | TSTOUT("Testing attach lsm label with cmd...\n"); |
72863294 DE |
147 | |
148 | ret = pipe(pipefd); | |
149 | if (ret < 0) { | |
150 | TSTERR("pipe failed %d", ret); | |
151 | return ret; | |
152 | } | |
153 | attach_options.stdout_fd = pipefd[1]; | |
154 | ||
155 | ret = ct->attach(ct, lxc_attach_run_command, &command, &attach_options, &pid); | |
156 | if (ret < 0) { | |
157 | TSTERR("attach failed"); | |
158 | goto err1; | |
159 | } | |
160 | ||
161 | ret = read(pipefd[0], result, sizeof(result)-1); | |
162 | if (ret < 0) { | |
163 | TSTERR("read failed %d", ret); | |
164 | goto err2; | |
165 | } | |
166 | result[ret] = '\0'; | |
46cd2845 | 167 | space = strchr(result, '\n'); |
72863294 DE |
168 | if (space) |
169 | *space = '\0'; | |
46cd2845 | 170 | space = strchr(result, ' '); |
72863294 DE |
171 | if (space) |
172 | *space = '\0'; | |
173 | ||
174 | ret = -1; | |
175 | if (strcmp(lsm_label, result)) { | |
176 | TSTERR("LSM label mismatch expected:%s got:%s", lsm_label, result); | |
177 | goto err2; | |
178 | } | |
179 | ret = 0; | |
180 | ||
181 | err2: | |
dbdf8cf4 | 182 | (void)wait_for_pid(pid); |
72863294 DE |
183 | err1: |
184 | close(pipefd[0]); | |
185 | close(pipefd[1]); | |
186 | return ret; | |
187 | } | |
188 | #else | |
189 | static void test_attach_lsm_set_config(struct lxc_container *ct) {} | |
190 | static int test_attach_lsm_func(struct lxc_container *ct) { return 0; } | |
191 | static int test_attach_lsm_cmd(struct lxc_container *ct) { return 0; } | |
192 | #endif /* HAVE_APPARMOR || HAVE_SELINUX */ | |
193 | ||
194 | static int test_attach_func_func(void* payload) | |
195 | { | |
94ac256f | 196 | TSTOUT("%d", (int)syscall(SYS_getpid)); |
72863294 DE |
197 | return 0; |
198 | } | |
199 | ||
200 | static int test_attach_func(struct lxc_container *ct) | |
201 | { | |
202 | int ret; | |
203 | pid_t pid,nspid; | |
204 | int pipefd[2]; | |
205 | char result[1024]; | |
206 | lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; | |
207 | ||
c59ce7de | 208 | TSTOUT("Testing attach with func...\n"); |
72863294 DE |
209 | |
210 | /* XXX: We can't just use &nspid and have test_attach_func_func fill | |
211 | * it in because the function doesn't run in our process context but | |
212 | * in a fork()ed from us context. We read the result through a pipe. | |
213 | */ | |
214 | ret = pipe(pipefd); | |
215 | if (ret < 0) { | |
216 | TSTERR("pipe failed %d", ret); | |
217 | return ret; | |
218 | } | |
219 | attach_options.stdout_fd = pipefd[1]; | |
220 | ||
221 | ret = ct->attach(ct, test_attach_func_func, NULL, &attach_options, &pid); | |
222 | if (ret < 0) { | |
223 | TSTERR("attach failed"); | |
224 | goto err1; | |
225 | } | |
226 | ||
227 | ret = read(pipefd[0], result, sizeof(result)-1); | |
228 | if (ret < 0) { | |
229 | TSTERR("read failed %d", ret); | |
230 | goto err2; | |
231 | } | |
232 | result[ret] = '\0'; | |
233 | ||
234 | /* There is a small chance the pid is reused inside the NS, so we | |
235 | * just print it and don't actually do this check | |
236 | * | |
237 | * if (pid == nspid) TSTERR(...) | |
238 | */ | |
239 | nspid = atoi(result); | |
c59ce7de | 240 | TSTOUT("Pid:%d in NS:%d\n", pid, nspid); |
72863294 DE |
241 | ret = 0; |
242 | ||
243 | err2: | |
dbdf8cf4 | 244 | (void)wait_for_pid(pid); |
72863294 DE |
245 | err1: |
246 | close(pipefd[0]); | |
247 | close(pipefd[1]); | |
248 | return ret; | |
249 | } | |
250 | ||
251 | static int test_attach_cmd(struct lxc_container *ct) | |
252 | { | |
253 | int ret; | |
254 | pid_t pid; | |
255 | char *argv[] = {"cmp", "-s", "/sbin/init", "/bin/busybox", NULL}; | |
256 | lxc_attach_command_t command = {"cmp", argv}; | |
257 | lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT; | |
258 | ||
c59ce7de | 259 | TSTOUT("Testing attach with success command...\n"); |
72863294 DE |
260 | ret = ct->attach(ct, lxc_attach_run_command, &command, &attach_options, &pid); |
261 | if (ret < 0) { | |
262 | TSTERR("attach failed"); | |
263 | return ret; | |
264 | } | |
265 | ||
266 | ret = wait_for_pid(pid); | |
267 | if (ret < 0) { | |
268 | TSTERR("attach success command got bad return %d", ret); | |
269 | return ret; | |
270 | } | |
271 | ||
c59ce7de | 272 | TSTOUT("Testing attach with failure command...\n"); |
72863294 DE |
273 | argv[2] = "/etc/fstab"; |
274 | ret = ct->attach(ct, lxc_attach_run_command, &command, &attach_options, &pid); | |
275 | if (ret < 0) { | |
276 | TSTERR("attach failed"); | |
277 | return ret; | |
278 | } | |
279 | ||
280 | ret = wait_for_pid(pid); | |
281 | if (ret == 0) { | |
282 | TSTERR("attach failure command got bad return %d", ret); | |
283 | return -1; | |
284 | } | |
285 | return 0; | |
286 | } | |
287 | ||
288 | /* test_ct_destroy: stop and destroy the test container | |
289 | * | |
290 | * @ct : the container | |
291 | */ | |
292 | static void test_ct_destroy(struct lxc_container *ct) | |
293 | { | |
294 | ct->stop(ct); | |
295 | ct->destroy(ct); | |
296 | lxc_container_put(ct); | |
297 | } | |
298 | ||
299 | /* test_ct_create: create and start test container | |
300 | * | |
301 | * @lxcpath : the lxcpath in which to create the container | |
302 | * @group : name of the container group or NULL for default "lxc" | |
303 | * @name : name of the container | |
304 | * @template : template to use when creating the container | |
305 | */ | |
306 | static struct lxc_container *test_ct_create(const char *lxcpath, | |
307 | const char *group, const char *name, | |
308 | const char *template) | |
309 | { | |
310 | int ret; | |
311 | struct lxc_container *ct = NULL; | |
312 | ||
313 | if (lxcpath) { | |
314 | ret = mkdir(lxcpath, 0755); | |
315 | if (ret < 0 && errno != EEXIST) { | |
316 | TSTERR("failed to mkdir %s %s", lxcpath, strerror(errno)); | |
317 | goto out1; | |
318 | } | |
319 | } | |
320 | ||
321 | if ((ct = lxc_container_new(name, lxcpath)) == NULL) { | |
322 | TSTERR("instantiating container %s", name); | |
323 | goto out1; | |
324 | } | |
325 | if (ct->is_defined(ct)) { | |
621c7cc7 | 326 | test_ct_destroy(ct); |
72863294 DE |
327 | ct = lxc_container_new(name, lxcpath); |
328 | } | |
329 | if (!ct->createl(ct, template, NULL, NULL, 0, NULL)) { | |
330 | TSTERR("creating container %s", name); | |
331 | goto out2; | |
332 | } | |
333 | ||
af04d847 | 334 | if (lsm_ops->enabled(lsm_ops)) |
72863294 DE |
335 | test_attach_lsm_set_config(ct); |
336 | ||
540f932a | 337 | ct->want_daemonize(ct, true); |
72863294 DE |
338 | if (!ct->startl(ct, 0, NULL)) { |
339 | TSTERR("starting container %s", name); | |
340 | goto out2; | |
341 | } | |
342 | return ct; | |
343 | ||
344 | out2: | |
345 | test_ct_destroy(ct); | |
346 | ct = NULL; | |
347 | out1: | |
348 | return ct; | |
349 | } | |
350 | ||
351 | ||
0b98289e | 352 | static int test_attach(const char *lxcpath, const char *name, const char *template) |
72863294 DE |
353 | { |
354 | int ret = -1; | |
355 | struct lxc_container *ct; | |
356 | ||
c59ce7de | 357 | TSTOUT("Testing attach with on lxcpath:%s\n", lxcpath ? lxcpath : "<default>"); |
72863294 DE |
358 | ct = test_ct_create(lxcpath, NULL, name, template); |
359 | if (!ct) | |
360 | goto err1; | |
361 | ||
362 | ret = test_attach_cmd(ct); | |
363 | if (ret < 0) { | |
364 | TSTERR("attach cmd test failed"); | |
365 | goto err2; | |
366 | } | |
367 | ||
368 | ret = test_attach_func(ct); | |
369 | if (ret < 0) { | |
370 | TSTERR("attach func test failed"); | |
371 | goto err2; | |
372 | } | |
373 | ||
af04d847 | 374 | if (lsm_ops->enabled(lsm_ops)) { |
72863294 DE |
375 | ret = test_attach_lsm_cmd(ct); |
376 | if (ret < 0) { | |
377 | TSTERR("attach lsm cmd test failed"); | |
378 | goto err2; | |
379 | } | |
380 | ||
381 | ret = test_attach_lsm_func(ct); | |
382 | if (ret < 0) { | |
383 | TSTERR("attach lsm func test failed"); | |
384 | goto err2; | |
385 | } | |
386 | } | |
387 | ret = 0; | |
388 | ||
389 | err2: | |
390 | test_ct_destroy(ct); | |
391 | err1: | |
392 | return ret; | |
393 | } | |
394 | ||
395 | int main(int argc, char *argv[]) | |
396 | { | |
ba2be1a8 CB |
397 | int i, ret; |
398 | struct lxc_log log; | |
399 | char template[sizeof(P_tmpdir"/attach_XXXXXX")]; | |
400 | int fret = EXIT_FAILURE; | |
401 | ||
18cd4b54 DJ |
402 | (void)strlcpy(template, P_tmpdir"/attach_XXXXXX", sizeof(template)); |
403 | ||
4eb19ac0 | 404 | lsm_ops = lsm_init_static(); |
d701d729 | 405 | |
ba2be1a8 CB |
406 | i = lxc_make_tmpfile(template, false); |
407 | if (i < 0) { | |
408 | lxc_error("Failed to create temporary log file for container %s\n", TSTNAME); | |
409 | exit(EXIT_FAILURE); | |
410 | } else { | |
411 | lxc_debug("Using \"%s\" as temporary log file for container %s\n", template, TSTNAME); | |
412 | close(i); | |
413 | } | |
414 | ||
415 | log.name = TSTNAME; | |
416 | log.file = template; | |
417 | log.level = "TRACE"; | |
418 | log.prefix = "attach"; | |
419 | log.quiet = false; | |
420 | log.lxcpath = NULL; | |
421 | if (lxc_log_init(&log)) | |
422 | goto on_error; | |
72863294 DE |
423 | |
424 | test_lsm_detect(); | |
425 | ret = test_attach(NULL, TSTNAME, "busybox"); | |
426 | if (ret < 0) | |
ba2be1a8 | 427 | goto on_error; |
72863294 | 428 | |
c59ce7de | 429 | TSTOUT("\n"); |
72863294 DE |
430 | ret = test_attach(LXCPATH "/alternate-path-test", TSTNAME, "busybox"); |
431 | if (ret < 0) | |
ba2be1a8 | 432 | goto on_error; |
72863294 | 433 | |
c59ce7de | 434 | TSTOUT("All tests passed\n"); |
ba2be1a8 CB |
435 | fret = EXIT_SUCCESS; |
436 | ||
437 | on_error: | |
438 | if (fret != EXIT_SUCCESS) { | |
439 | int fd; | |
440 | ||
441 | fd = open(template, O_RDONLY); | |
442 | if (fd >= 0) { | |
443 | char buf[4096]; | |
444 | ssize_t buflen; | |
445 | while ((buflen = read(fd, buf, 1024)) > 0) { | |
446 | buflen = write(STDERR_FILENO, buf, buflen); | |
447 | if (buflen <= 0) | |
448 | break; | |
449 | } | |
450 | close(fd); | |
451 | } | |
452 | } | |
453 | (void)rmdir(LXCPATH "/alternate-path-test"); | |
454 | (void)unlink(template); | |
455 | exit(fret); | |
72863294 | 456 | } |