]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/attach.c
attach: and cgroup.c: be overly cautious
[mirror_lxc.git] / src / lxc / attach.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #define _GNU_SOURCE
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <sys/param.h>
32 #include <sys/prctl.h>
33 #include <sys/mount.h>
34 #include <sys/syscall.h>
35 #include <sys/wait.h>
36 #include <linux/unistd.h>
37 #include <pwd.h>
38
39 #if !HAVE_DECL_PR_CAPBSET_DROP
40 #define PR_CAPBSET_DROP 24
41 #endif
42
43 #include "namespace.h"
44 #include "log.h"
45 #include "attach.h"
46 #include "caps.h"
47 #include "config.h"
48 #include "apparmor.h"
49
50 lxc_log_define(lxc_attach, lxc);
51
52 /* Define setns() if missing from the C library */
53 #ifndef HAVE_SETNS
54 static int setns(int fd, int nstype)
55 {
56 #ifdef __NR_setns
57 return syscall(__NR_setns, fd, nstype);
58 #else
59 errno = ENOSYS;
60 return -1;
61 #endif
62 }
63 #endif
64
65 /* Define unshare() if missing from the C library */
66 #ifndef HAVE_UNSHARE
67 static int unshare(int flags)
68 {
69 #ifdef __NR_unshare
70 return syscall(__NR_unshare, flags);
71 #else
72 errno = ENOSYS;
73 return -1;
74 #endif
75 }
76 #endif
77
78 /* Define getline() if missing from the C library */
79 #ifndef HAVE_GETLINE
80 #ifdef HAVE_FGETLN
81 #include <../include/getline.h>
82 #endif
83 #endif
84
85 struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
86 {
87 struct lxc_proc_context_info *info = calloc(1, sizeof(*info));
88 FILE *proc_file;
89 char proc_fn[MAXPATHLEN];
90 char *line = NULL;
91 size_t line_bufsz = 0;
92 int ret, found;
93
94 if (!info) {
95 SYSERROR("Could not allocate memory.");
96 return NULL;
97 }
98
99 /* read capabilities */
100 snprintf(proc_fn, MAXPATHLEN, "/proc/%d/status", pid);
101
102 proc_file = fopen(proc_fn, "r");
103 if (!proc_file) {
104 SYSERROR("Could not open %s", proc_fn);
105 goto out_error;
106 }
107
108 found = 0;
109 while (getline(&line, &line_bufsz, proc_file) != -1) {
110 ret = sscanf(line, "CapBnd: %llx", &info->capability_mask);
111 if (ret != EOF && ret > 0) {
112 found = 1;
113 break;
114 }
115 }
116
117 if (line)
118 free(line);
119 fclose(proc_file);
120
121 if (!found) {
122 SYSERROR("Could not read capability bounding set from %s", proc_fn);
123 errno = ENOENT;
124 goto out_error;
125 }
126
127 /* read personality */
128 snprintf(proc_fn, MAXPATHLEN, "/proc/%d/personality", pid);
129
130 proc_file = fopen(proc_fn, "r");
131 if (!proc_file) {
132 SYSERROR("Could not open %s", proc_fn);
133 goto out_error;
134 }
135
136 ret = fscanf(proc_file, "%lx", &info->personality);
137 fclose(proc_file);
138
139 if (ret == EOF || ret == 0) {
140 SYSERROR("Could not read personality from %s", proc_fn);
141 errno = ENOENT;
142 goto out_error;
143 }
144 info->aa_profile = aa_get_profile(pid);
145
146 return info;
147
148 out_error:
149 free(info);
150 return NULL;
151 }
152
153 int lxc_attach_to_ns(pid_t pid, int which)
154 {
155 char path[MAXPATHLEN];
156 /* according to <http://article.gmane.org/gmane.linux.kernel.containers.lxc.devel/1429>,
157 * the file for user namepsaces in /proc/$pid/ns will be called
158 * 'user' once the kernel supports it
159 */
160 static char *ns[] = { "mnt", "pid", "uts", "ipc", "user", "net" };
161 static int flags[] = {
162 CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC,
163 CLONE_NEWUSER, CLONE_NEWNET
164 };
165 static const int size = sizeof(ns) / sizeof(char *);
166 int fd[size];
167 int i, j, saved_errno;
168
169
170 snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid);
171 if (access(path, X_OK)) {
172 ERROR("Does this kernel version support 'attach' ?");
173 return -1;
174 }
175
176 for (i = 0; i < size; i++) {
177 /* ignore if we are not supposed to attach to that
178 * namespace
179 */
180 if (which != -1 && !(which & flags[i])) {
181 fd[i] = -1;
182 continue;
183 }
184
185 snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns[i]);
186 fd[i] = open(path, O_RDONLY);
187 if (fd[i] < 0) {
188 saved_errno = errno;
189
190 /* close all already opened file descriptors before
191 * we return an error, so we don't leak them
192 */
193 for (j = 0; j < i; j++)
194 close(fd[j]);
195
196 errno = saved_errno;
197 SYSERROR("failed to open '%s'", path);
198 return -1;
199 }
200 }
201
202 for (i = 0; i < size; i++) {
203 if (fd[i] >= 0 && setns(fd[i], 0) != 0) {
204 saved_errno = errno;
205
206 for (j = i; j < size; j++)
207 close(fd[j]);
208
209 errno = saved_errno;
210 SYSERROR("failed to set namespace '%s'", ns[i]);
211 return -1;
212 }
213
214 close(fd[i]);
215 }
216
217 return 0;
218 }
219
220 int lxc_attach_remount_sys_proc()
221 {
222 int ret;
223
224 ret = unshare(CLONE_NEWNS);
225 if (ret < 0) {
226 SYSERROR("failed to unshare mount namespace");
227 return -1;
228 }
229
230 /* assume /proc is always mounted, so remount it */
231 ret = umount2("/proc", MNT_DETACH);
232 if (ret < 0) {
233 SYSERROR("failed to unmount /proc");
234 return -1;
235 }
236
237 ret = mount("none", "/proc", "proc", 0, NULL);
238 if (ret < 0) {
239 SYSERROR("failed to remount /proc");
240 return -1;
241 }
242
243 /* try to umount /sys - if it's not a mount point,
244 * we'll get EINVAL, then we ignore it because it
245 * may not have been mounted in the first place
246 */
247 ret = umount2("/sys", MNT_DETACH);
248 if (ret < 0 && errno != EINVAL) {
249 SYSERROR("failed to unmount /sys");
250 return -1;
251 } else if (ret == 0) {
252 /* remount it */
253 ret = mount("none", "/sys", "sysfs", 0, NULL);
254 if (ret < 0) {
255 SYSERROR("failed to remount /sys");
256 return -1;
257 }
258 }
259
260 return 0;
261 }
262
263 int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
264 {
265 int last_cap = lxc_caps_last_cap();
266 int cap;
267
268 for (cap = 0; cap <= last_cap; cap++) {
269 if (ctx->capability_mask & (1LL << cap))
270 continue;
271
272 if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) {
273 SYSERROR("failed to remove capability id %d", cap);
274 return -1;
275 }
276 }
277
278 return 0;
279 }
280
281 int lxc_attach_set_environment(enum lxc_attach_env_policy_t policy, char** extra_env, char** extra_keep)
282 {
283 /* TODO: implement extra_env, extra_keep
284 * Rationale:
285 * - extra_env is an array of strings of the form
286 * "VAR=VALUE", which are to be set (after clearing or not,
287 * depending on the value of the policy variable)
288 * - extra_keep is an array of strings of the form
289 * "VAR", which are extra environment variables to be kept
290 * around after clearing (if that is done, otherwise, the
291 * remain anyway)
292 */
293 (void) extra_env;
294 (void) extra_keep;
295
296 if (policy == LXC_ATTACH_CLEAR_ENV) {
297 if (clearenv()) {
298 SYSERROR("failed to clear environment");
299 /* don't error out though */
300 }
301 }
302
303 if (putenv("container=lxc")) {
304 SYSERROR("failed to set environment variable");
305 return -1;
306 }
307
308 return 0;
309 }
310
311 char *lxc_attach_getpwshell(uid_t uid)
312 {
313 /* local variables */
314 pid_t pid;
315 int pipes[2];
316 int ret;
317 int fd;
318 char *result = NULL;
319
320 /* we need to fork off a process that runs the
321 * getent program, and we need to capture its
322 * output, so we use a pipe for that purpose
323 */
324 ret = pipe(pipes);
325 if (ret < 0)
326 return NULL;
327
328 pid = fork();
329 if (pid < 0) {
330 close(pipes[0]);
331 close(pipes[1]);
332 return NULL;
333 }
334
335 if (pid) {
336 /* parent process */
337 FILE *pipe_f;
338 char *line = NULL;
339 size_t line_bufsz = 0;
340 int found = 0;
341 int status;
342
343 close(pipes[1]);
344
345 pipe_f = fdopen(pipes[0], "r");
346 while (getline(&line, &line_bufsz, pipe_f) != -1) {
347 char *token;
348 char *saveptr = NULL;
349 long value;
350 char *endptr = NULL;
351 int i;
352
353 /* if we already found something, just continue
354 * to read until the pipe doesn't deliver any more
355 * data, but don't modify the existing data
356 * structure
357 */
358 if (found)
359 continue;
360
361 /* trim line on the right hand side */
362 for (i = strlen(line); i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i)
363 line[i - 1] = '\0';
364
365 /* split into tokens: first user name */
366 token = strtok_r(line, ":", &saveptr);
367 if (!token)
368 continue;
369 /* next: dummy password field */
370 token = strtok_r(NULL, ":", &saveptr);
371 if (!token)
372 continue;
373 /* next: user id */
374 token = strtok_r(NULL, ":", &saveptr);
375 value = token ? strtol(token, &endptr, 10) : 0;
376 if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX)
377 continue;
378 /* dummy sanity check: user id matches */
379 if ((uid_t) value != uid)
380 continue;
381 /* skip fields: gid, gecos, dir, go to next field 'shell' */
382 for (i = 0; i < 4; i++) {
383 token = strtok_r(NULL, ":", &saveptr);
384 if (!token)
385 break;
386 }
387 if (!token)
388 continue;
389 if (result)
390 free(result);
391 result = strdup(token);
392
393 /* sanity check that there are no fields after that */
394 token = strtok_r(NULL, ":", &saveptr);
395 if (token)
396 continue;
397
398 found = 1;
399 }
400
401 free(line);
402 fclose(pipe_f);
403 again:
404 if (waitpid(pid, &status, 0) < 0) {
405 if (errno == EINTR)
406 goto again;
407 return NULL;
408 }
409
410 /* some sanity checks: if anything even hinted at going
411 * wrong: we can't be sure we have a valid result, so
412 * we assume we don't
413 */
414
415 if (!WIFEXITED(status))
416 return NULL;
417
418 if (WEXITSTATUS(status) != 0)
419 return NULL;
420
421 if (!found)
422 return NULL;
423
424 return result;
425 } else {
426 /* child process */
427 char uid_buf[32];
428 char *arguments[] = {
429 "getent",
430 "passwd",
431 uid_buf,
432 NULL
433 };
434
435 close(pipes[0]);
436
437 /* we want to capture stdout */
438 dup2(pipes[1], 1);
439 close(pipes[1]);
440
441 /* get rid of stdin/stderr, so we try to associate it
442 * with /dev/null
443 */
444 fd = open("/dev/null", O_RDWR);
445 if (fd < 0) {
446 close(0);
447 close(2);
448 } else {
449 dup2(fd, 0);
450 dup2(fd, 2);
451 close(fd);
452 }
453
454 /* finish argument list */
455 ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long) uid);
456 if (ret <= 0)
457 exit(-1);
458
459 /* try to run getent program */
460 (void) execvp("getent", arguments);
461 exit(-1);
462 }
463 }
464
465 void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid)
466 {
467 FILE *proc_file;
468 char proc_fn[MAXPATHLEN];
469 char *line = NULL;
470 size_t line_bufsz = 0;
471 int ret;
472 long value = -1;
473 uid_t uid = (uid_t)-1;
474 gid_t gid = (gid_t)-1;
475
476 /* read capabilities */
477 snprintf(proc_fn, MAXPATHLEN, "/proc/%d/status", 1);
478
479 proc_file = fopen(proc_fn, "r");
480 if (!proc_file)
481 return;
482
483 while (getline(&line, &line_bufsz, proc_file) != -1) {
484 /* format is: real, effective, saved set user, fs
485 * we only care about real uid
486 */
487 ret = sscanf(line, "Uid: %ld", &value);
488 if (ret != EOF && ret > 0) {
489 uid = (uid_t) value;
490 } else {
491 ret = sscanf(line, "Gid: %ld", &value);
492 if (ret != EOF && ret > 0)
493 gid = (gid_t) value;
494 }
495 if (uid != (uid_t)-1 && gid != (gid_t)-1)
496 break;
497 }
498
499 fclose(proc_file);
500 free(line);
501
502 /* only override arguments if we found something */
503 if (uid != (uid_t)-1)
504 *init_uid = uid;
505 if (gid != (gid_t)-1)
506 *init_gid = gid;
507
508 /* TODO: we should also parse supplementary groups and use
509 * setgroups() to set them */
510 }