]>
Commit | Line | Data |
---|---|---|
e0732705 CS |
1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
9afe19d6 | 7 | * Daniel Lezcano <daniel.lezcano at free.fr> |
e0732705 CS |
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> | |
7a0b0b56 | 33 | #include <sys/mount.h> |
1ba0013f | 34 | #include <sys/syscall.h> |
905022f7 | 35 | #include <sys/wait.h> |
910bb4fa | 36 | #include <linux/unistd.h> |
905022f7 | 37 | #include <pwd.h> |
e0732705 CS |
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" | |
e0732705 | 47 | #include "config.h" |
9958532b | 48 | #include "apparmor.h" |
e0732705 | 49 | |
e0732705 CS |
50 | lxc_log_define(lxc_attach, lxc); |
51 | ||
2d76d1d7 SG |
52 | /* Define setns() if missing from the C library */ |
53 | #ifndef HAVE_SETNS | |
54 | static int setns(int fd, int nstype) | |
e0732705 | 55 | { |
2d76d1d7 SG |
56 | #ifdef __NR_setns |
57 | return syscall(__NR_setns, fd, nstype); | |
e0732705 | 58 | #else |
2d76d1d7 SG |
59 | errno = ENOSYS; |
60 | return -1; | |
e0732705 CS |
61 | #endif |
62 | } | |
2d76d1d7 SG |
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 | |
e0732705 | 77 | |
1ba0013f SG |
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 | ||
e0732705 CS |
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]; | |
460a1cf0 | 90 | char *line = NULL; |
e0732705 | 91 | size_t line_bufsz = 0; |
460a1cf0 | 92 | int ret, found; |
e0732705 CS |
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 | fclose(proc_file); | |
118 | ||
119 | if (!found) { | |
120 | SYSERROR("Could not read capability bounding set from %s", proc_fn); | |
121 | errno = ENOENT; | |
122 | goto out_error; | |
123 | } | |
124 | ||
125 | /* read personality */ | |
126 | snprintf(proc_fn, MAXPATHLEN, "/proc/%d/personality", pid); | |
127 | ||
128 | proc_file = fopen(proc_fn, "r"); | |
129 | if (!proc_file) { | |
130 | SYSERROR("Could not open %s", proc_fn); | |
131 | goto out_error; | |
132 | } | |
133 | ||
134 | ret = fscanf(proc_file, "%lx", &info->personality); | |
135 | fclose(proc_file); | |
136 | ||
137 | if (ret == EOF || ret == 0) { | |
138 | SYSERROR("Could not read personality from %s", proc_fn); | |
139 | errno = ENOENT; | |
140 | goto out_error; | |
141 | } | |
9958532b | 142 | info->aa_profile = aa_get_profile(pid); |
e0732705 | 143 | |
e0732705 CS |
144 | return info; |
145 | ||
146 | out_error: | |
460a1cf0 | 147 | free(info); |
e0732705 CS |
148 | free(line); |
149 | return NULL; | |
150 | } | |
151 | ||
fc763ab7 | 152 | int lxc_attach_to_ns(pid_t pid, int which) |
99d50954 CS |
153 | { |
154 | char path[MAXPATHLEN]; | |
fc763ab7 CS |
155 | /* according to <http://article.gmane.org/gmane.linux.kernel.containers.lxc.devel/1429>, |
156 | * the file for user namepsaces in /proc/$pid/ns will be called | |
157 | * 'user' once the kernel supports it | |
158 | */ | |
159 | static char *ns[] = { "mnt", "pid", "uts", "ipc", "user", "net" }; | |
160 | static int flags[] = { | |
161 | CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, | |
162 | CLONE_NEWUSER, CLONE_NEWNET | |
163 | }; | |
164 | static const int size = sizeof(ns) / sizeof(char *); | |
99d50954 | 165 | int fd[size]; |
fc763ab7 CS |
166 | int i, j, saved_errno; |
167 | ||
99d50954 CS |
168 | |
169 | snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid); | |
170 | if (access(path, X_OK)) { | |
171 | ERROR("Does this kernel version support 'attach' ?"); | |
172 | return -1; | |
173 | } | |
174 | ||
175 | for (i = 0; i < size; i++) { | |
fc763ab7 CS |
176 | /* ignore if we are not supposed to attach to that |
177 | * namespace | |
178 | */ | |
179 | if (which != -1 && !(which & flags[i])) { | |
180 | fd[i] = -1; | |
181 | continue; | |
182 | } | |
183 | ||
99d50954 CS |
184 | snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns[i]); |
185 | fd[i] = open(path, O_RDONLY); | |
186 | if (fd[i] < 0) { | |
fc763ab7 CS |
187 | saved_errno = errno; |
188 | ||
189 | /* close all already opened file descriptors before | |
190 | * we return an error, so we don't leak them | |
191 | */ | |
192 | for (j = 0; j < i; j++) | |
193 | close(fd[j]); | |
194 | ||
195 | errno = saved_errno; | |
99d50954 CS |
196 | SYSERROR("failed to open '%s'", path); |
197 | return -1; | |
198 | } | |
199 | } | |
200 | ||
201 | for (i = 0; i < size; i++) { | |
fc763ab7 CS |
202 | if (fd[i] >= 0 && setns(fd[i], 0) != 0) { |
203 | saved_errno = errno; | |
204 | ||
205 | for (j = i; j < size; j++) | |
206 | close(fd[j]); | |
207 | ||
208 | errno = saved_errno; | |
99d50954 CS |
209 | SYSERROR("failed to set namespace '%s'", ns[i]); |
210 | return -1; | |
211 | } | |
212 | ||
213 | close(fd[i]); | |
214 | } | |
215 | ||
216 | return 0; | |
217 | } | |
218 | ||
7a0b0b56 CS |
219 | int lxc_attach_remount_sys_proc() |
220 | { | |
221 | int ret; | |
222 | ||
223 | ret = unshare(CLONE_NEWNS); | |
224 | if (ret < 0) { | |
225 | SYSERROR("failed to unshare mount namespace"); | |
226 | return -1; | |
227 | } | |
228 | ||
229 | /* assume /proc is always mounted, so remount it */ | |
230 | ret = umount2("/proc", MNT_DETACH); | |
231 | if (ret < 0) { | |
232 | SYSERROR("failed to unmount /proc"); | |
233 | return -1; | |
234 | } | |
235 | ||
236 | ret = mount("none", "/proc", "proc", 0, NULL); | |
237 | if (ret < 0) { | |
238 | SYSERROR("failed to remount /proc"); | |
239 | return -1; | |
240 | } | |
241 | ||
242 | /* try to umount /sys - if it's not a mount point, | |
243 | * we'll get EINVAL, then we ignore it because it | |
244 | * may not have been mounted in the first place | |
245 | */ | |
246 | ret = umount2("/sys", MNT_DETACH); | |
247 | if (ret < 0 && errno != EINVAL) { | |
248 | SYSERROR("failed to unmount /sys"); | |
249 | return -1; | |
250 | } else if (ret == 0) { | |
251 | /* remount it */ | |
252 | ret = mount("none", "/sys", "sysfs", 0, NULL); | |
253 | if (ret < 0) { | |
254 | SYSERROR("failed to remount /sys"); | |
255 | return -1; | |
256 | } | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | ||
e0732705 CS |
262 | int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) |
263 | { | |
264 | int last_cap = lxc_caps_last_cap(); | |
265 | int cap; | |
266 | ||
267 | for (cap = 0; cap <= last_cap; cap++) { | |
268 | if (ctx->capability_mask & (1LL << cap)) | |
269 | continue; | |
270 | ||
271 | if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) { | |
272 | SYSERROR("failed to remove capability id %d", cap); | |
273 | return -1; | |
274 | } | |
275 | } | |
276 | ||
277 | return 0; | |
278 | } | |
905022f7 CS |
279 | |
280 | char *lxc_attach_getpwshell(uid_t uid) | |
281 | { | |
282 | /* local variables */ | |
283 | pid_t pid; | |
284 | int pipes[2]; | |
285 | int ret; | |
286 | int fd; | |
287 | char *result = NULL; | |
288 | ||
289 | /* we need to fork off a process that runs the | |
290 | * getent program, and we need to capture its | |
291 | * output, so we use a pipe for that purpose | |
292 | */ | |
293 | ret = pipe(pipes); | |
294 | if (ret < 0) | |
295 | return NULL; | |
296 | ||
297 | pid = fork(); | |
298 | if (pid < 0) { | |
299 | close(pipes[0]); | |
300 | close(pipes[1]); | |
301 | return NULL; | |
302 | } | |
303 | ||
304 | if (pid) { | |
305 | /* parent process */ | |
306 | FILE *pipe_f; | |
307 | char *line = NULL; | |
308 | size_t line_bufsz = 0; | |
309 | int found = 0; | |
310 | int status; | |
311 | ||
312 | close(pipes[1]); | |
313 | ||
314 | pipe_f = fdopen(pipes[0], "r"); | |
315 | while (getline(&line, &line_bufsz, pipe_f) != -1) { | |
316 | char *token; | |
317 | char *saveptr = NULL; | |
318 | long value; | |
319 | char *endptr = NULL; | |
320 | int i; | |
321 | ||
322 | /* if we already found something, just continue | |
323 | * to read until the pipe doesn't deliver any more | |
324 | * data, but don't modify the existing data | |
325 | * structure | |
326 | */ | |
327 | if (found) | |
328 | continue; | |
329 | ||
330 | /* trim line on the right hand side */ | |
331 | for (i = strlen(line); line && i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i) | |
332 | line[i - 1] = '\0'; | |
333 | ||
334 | /* split into tokens: first user name */ | |
335 | token = strtok_r(line, ":", &saveptr); | |
336 | if (!token) | |
337 | continue; | |
338 | /* next: dummy password field */ | |
339 | token = strtok_r(NULL, ":", &saveptr); | |
340 | if (!token) | |
341 | continue; | |
342 | /* next: user id */ | |
343 | token = strtok_r(NULL, ":", &saveptr); | |
344 | value = token ? strtol(token, &endptr, 10) : 0; | |
345 | if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX) | |
346 | continue; | |
347 | /* dummy sanity check: user id matches */ | |
348 | if ((uid_t) value != uid) | |
349 | continue; | |
350 | /* skip fields: gid, gecos, dir, go to next field 'shell' */ | |
351 | for (i = 0; i < 4; i++) { | |
352 | token = strtok_r(NULL, ":", &saveptr); | |
353 | if (!token) | |
354 | break; | |
355 | } | |
356 | if (!token) | |
357 | continue; | |
53a54099 SH |
358 | if (result) |
359 | free(result); | |
905022f7 CS |
360 | result = strdup(token); |
361 | ||
362 | /* sanity check that there are no fields after that */ | |
363 | token = strtok_r(NULL, ":", &saveptr); | |
364 | if (token) | |
365 | continue; | |
366 | ||
367 | found = 1; | |
368 | } | |
369 | ||
370 | free(line); | |
371 | fclose(pipe_f); | |
372 | again: | |
373 | if (waitpid(pid, &status, 0) < 0) { | |
374 | if (errno == EINTR) | |
375 | goto again; | |
376 | return NULL; | |
377 | } | |
378 | ||
379 | /* some sanity checks: if anything even hinted at going | |
380 | * wrong: we can't be sure we have a valid result, so | |
381 | * we assume we don't | |
382 | */ | |
383 | ||
384 | if (!WIFEXITED(status)) | |
385 | return NULL; | |
386 | ||
387 | if (WEXITSTATUS(status) != 0) | |
388 | return NULL; | |
389 | ||
390 | if (!found) | |
391 | return NULL; | |
392 | ||
393 | return result; | |
394 | } else { | |
395 | /* child process */ | |
396 | char uid_buf[32]; | |
397 | char *arguments[] = { | |
398 | "getent", | |
399 | "passwd", | |
400 | uid_buf, | |
401 | NULL | |
402 | }; | |
403 | ||
404 | close(pipes[0]); | |
405 | ||
406 | /* we want to capture stdout */ | |
407 | dup2(pipes[1], 1); | |
408 | close(pipes[1]); | |
409 | ||
410 | /* get rid of stdin/stderr, so we try to associate it | |
411 | * with /dev/null | |
412 | */ | |
413 | fd = open("/dev/null", O_RDWR); | |
414 | if (fd < 0) { | |
415 | close(0); | |
416 | close(2); | |
417 | } else { | |
418 | dup2(fd, 0); | |
419 | dup2(fd, 2); | |
420 | close(fd); | |
421 | } | |
422 | ||
423 | /* finish argument list */ | |
424 | ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long) uid); | |
425 | if (ret <= 0) | |
426 | exit(-1); | |
427 | ||
428 | /* try to run getent program */ | |
429 | (void) execvp("getent", arguments); | |
430 | exit(-1); | |
431 | } | |
432 | } | |
cb3e61fa CS |
433 | |
434 | void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid) | |
435 | { | |
436 | FILE *proc_file; | |
437 | char proc_fn[MAXPATHLEN]; | |
438 | char *line = NULL; | |
439 | size_t line_bufsz = 0; | |
440 | int ret; | |
441 | long value = -1; | |
442 | uid_t uid = (uid_t)-1; | |
443 | gid_t gid = (gid_t)-1; | |
444 | ||
445 | /* read capabilities */ | |
446 | snprintf(proc_fn, MAXPATHLEN, "/proc/%d/status", 1); | |
447 | ||
448 | proc_file = fopen(proc_fn, "r"); | |
449 | if (!proc_file) | |
450 | return; | |
451 | ||
452 | while (getline(&line, &line_bufsz, proc_file) != -1) { | |
453 | /* format is: real, effective, saved set user, fs | |
454 | * we only care about real uid | |
455 | */ | |
456 | ret = sscanf(line, "Uid: %ld", &value); | |
457 | if (ret != EOF && ret > 0) { | |
458 | uid = (uid_t) value; | |
459 | } else { | |
460 | ret = sscanf(line, "Gid: %ld", &value); | |
461 | if (ret != EOF && ret > 0) | |
462 | gid = (gid_t) value; | |
463 | } | |
464 | if (uid != (uid_t)-1 && gid != (gid_t)-1) | |
465 | break; | |
466 | } | |
467 | ||
468 | fclose(proc_file); | |
469 | free(line); | |
470 | ||
471 | /* only override arguments if we found something */ | |
472 | if (uid != (uid_t)-1) | |
473 | *init_uid = uid; | |
474 | if (gid != (gid_t)-1) | |
475 | *init_gid = gid; | |
476 | ||
477 | /* TODO: we should also parse supplementary groups and use | |
478 | * setgroups() to set them */ | |
479 | } |