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