Skip to content
This repository
Browse code

lxc-attach: Try really hard to determine login shell

If no command is specified, and using getpwuid() to determine the login
shell fails, try to spawn a process that executes the utility 'getent'.
getpwuid() may fail because of incompatibilities between the NSS
implementations on the host and in the container.

Signed-off-by: Christian Seiler <christian@iwakd.de>
  • Loading branch information...
commit 905022f73f5dee19cd6a880f8112b0a6aa45ba22 1 parent 66b5034
Christian Seiler chris-se authored Serge Hallyn committed

Showing 3 changed files with 172 additions and 2 deletions. Show diff stats Hide diff stats

  1. +154 0 src/lxc/attach.c
  2. +2 0  src/lxc/attach.h
  3. +16 2 src/lxc/lxc_attach.c
154 src/lxc/attach.c
@@ -32,7 +32,9 @@
32 32 #include <sys/prctl.h>
33 33 #include <sys/mount.h>
34 34 #include <sys/syscall.h>
  35 +#include <sys/wait.h>
35 36 #include <linux/unistd.h>
  37 +#include <pwd.h>
36 38
37 39 #if !HAVE_DECL_PR_CAPBSET_DROP
38 40 #define PR_CAPBSET_DROP 24
@@ -274,3 +276,155 @@ int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
274 276
275 277 return 0;
276 278 }
  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;
  358 + result = strdup(token);
  359 +
  360 + /* sanity check that there are no fields after that */
  361 + token = strtok_r(NULL, ":", &saveptr);
  362 + if (token)
  363 + continue;
  364 +
  365 + found = 1;
  366 + }
  367 +
  368 + free(line);
  369 + fclose(pipe_f);
  370 + again:
  371 + if (waitpid(pid, &status, 0) < 0) {
  372 + if (errno == EINTR)
  373 + goto again;
  374 + return NULL;
  375 + }
  376 +
  377 + /* some sanity checks: if anything even hinted at going
  378 + * wrong: we can't be sure we have a valid result, so
  379 + * we assume we don't
  380 + */
  381 +
  382 + if (!WIFEXITED(status))
  383 + return NULL;
  384 +
  385 + if (WEXITSTATUS(status) != 0)
  386 + return NULL;
  387 +
  388 + if (!found)
  389 + return NULL;
  390 +
  391 + return result;
  392 + } else {
  393 + /* child process */
  394 + char uid_buf[32];
  395 + char *arguments[] = {
  396 + "getent",
  397 + "passwd",
  398 + uid_buf,
  399 + NULL
  400 + };
  401 +
  402 + close(pipes[0]);
  403 +
  404 + /* we want to capture stdout */
  405 + dup2(pipes[1], 1);
  406 + close(pipes[1]);
  407 +
  408 + /* get rid of stdin/stderr, so we try to associate it
  409 + * with /dev/null
  410 + */
  411 + fd = open("/dev/null", O_RDWR);
  412 + if (fd < 0) {
  413 + close(0);
  414 + close(2);
  415 + } else {
  416 + dup2(fd, 0);
  417 + dup2(fd, 2);
  418 + close(fd);
  419 + }
  420 +
  421 + /* finish argument list */
  422 + ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long) uid);
  423 + if (ret <= 0)
  424 + exit(-1);
  425 +
  426 + /* try to run getent program */
  427 + (void) execvp("getent", arguments);
  428 + exit(-1);
  429 + }
  430 +}
2  src/lxc/attach.h
@@ -38,4 +38,6 @@ extern int lxc_attach_to_ns(pid_t other_pid, int which);
38 38 extern int lxc_attach_remount_sys_proc();
39 39 extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx);
40 40
  41 +extern char *lxc_attach_getpwshell(uid_t uid);
  42 +
41 43 #endif
18 src/lxc/lxc_attach.c
@@ -132,6 +132,7 @@ int main(int argc, char *argv[])
132 132 uid_t uid;
133 133 char *curdir;
134 134 int cgroup_ipc_sockets[2];
  135 + char *user_shell;
135 136
136 137 ret = lxc_caps_init();
137 138 if (ret)
@@ -438,7 +439,20 @@ int main(int argc, char *argv[])
438 439 uid = getuid();
439 440
440 441 passwd = getpwuid(uid);
441   - if (!passwd) {
  442 +
  443 + /* this probably happens because of incompatible nss
  444 + * implementations in host and container (remember, this
  445 + * code is still using the host's glibc but our mount
  446 + * namespace is in the container)
  447 + * we may try to get the information by spawning a
  448 + * [getent passwd uid] process and parsing the result
  449 + */
  450 + if (!passwd)
  451 + user_shell = lxc_attach_getpwshell(uid);
  452 + else
  453 + user_shell = passwd->pw_shell;
  454 +
  455 + if (!user_shell) {
442 456 SYSERROR("failed to get passwd " \
443 457 "entry for uid '%d'", uid);
444 458 return -1;
@@ -446,7 +460,7 @@ int main(int argc, char *argv[])
446 460
447 461 {
448 462 char *const args[] = {
449   - passwd->pw_shell,
  463 + user_shell,
450 464 NULL,
451 465 };
452 466

0 comments on commit 905022f

Please sign in to comment.
Something went wrong with that request. Please try again.