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 <christ...@iwakd.de> --- src/lxc/attach.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lxc/attach.h | 3 + src/lxc/lxc_attach.c | 11 +++ 3 files changed, 218 insertions(+) diff --git a/src/lxc/attach.c b/src/lxc/attach.c index af3d7a0..88356e1 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -32,7 +32,9 @@ #include <sys/prctl.h> #include <sys/mount.h> #include <sys/syscall.h> +#include <sys/wait.h> #include <linux/unistd.h> +#include <pwd.h> #if !HAVE_DECL_PR_CAPBSET_DROP #define PR_CAPBSET_DROP 24 @@ -275,3 +277,205 @@ int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx) return 0; } + +struct passwd *lxc_attach_getpwuid(uid_t uid) +{ + /* static variables for result, we assume that + * 256 is large enough to hold the information + * on username, passwd and gecos. for paths we + * use MAXPATHLEN instead + */ + static char pwstruct_name[256]; + static char pwstruct_passwd[256]; + static char pwstruct_gecos[256]; + static char pwstruct_dir[MAXPATHLEN]; + static char pwstruct_shell[MAXPATHLEN]; + static struct passwd result = { + .pw_name = pwstruct_name, + .pw_passwd = pwstruct_passwd, + .pw_uid = (uid_t) -1, + .pw_gid = (gid_t) -1, + .pw_gecos = pwstruct_gecos, + .pw_dir = pwstruct_dir, + .pw_shell = pwstruct_shell, + }; + + /* local variables */ + pid_t pid; + int pipes[2]; + int ret; + int fd; + + /* we need to fork off a process that runs the + * getent program, and we need to capture its + * output, so we use a pipe for that purpose + */ + ret = pipe(pipes); + if (ret < 0) + return NULL; + + pid = fork(); + if (pid < 0) { + close(pipes[0]); + close(pipes[1]); + return NULL; + } + + if (pid) { + /* parent process */ + FILE *pipe_f; + char *line = NULL; + size_t line_bufsz = 0; + int found = 0; + int status; + + close(pipes[1]); + + pipe_f = fdopen(pipes[0], "r"); + while (getline(&line, &line_bufsz, pipe_f) != -1) { + char *token; + char *saveptr = NULL; + long value; + char *endptr = NULL; + int i; + + /* if we already found something, just continue + * to read until the pipe doesn't deliver any more + * data, but don't modify the existing data + * structure + */ + if (found) + continue; + + /* trim line on the right hand side */ + for (i = strlen(line); line && i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i) + line[i - 1] = '\0'; + + /* split into tokens: first user name */ + token = strtok_r(line, ":", &saveptr); + if (!token) + continue; + snprintf(pwstruct_name, sizeof(pwstruct_name), "%s", token); + + /* next: dummy password field */ + token = strtok_r(NULL, ":", &saveptr); + if (!token) + continue; + snprintf(pwstruct_passwd, sizeof(pwstruct_passwd), "%s", token); + + /* next: user id */ + token = strtok_r(NULL, ":", &saveptr); + value = token ? strtol(token, &endptr, 10) : 0; + if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX) + continue; + result.pw_uid = (uid_t) value; + /* dummy sanity check: user id matches */ + if (result.pw_uid != uid) + continue; + + /* next: gid */ + token = strtok_r(NULL, ":", &saveptr); + value = token ? strtol(token, &endptr, 10) : 0; + if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX) + continue; + result.pw_gid = (gid_t) value; + + /* next: gecos */ + token = strtok_r(NULL, ":", &saveptr); + if (!token) + continue; + snprintf(pwstruct_gecos, sizeof(pwstruct_gecos), "%s", token); + + /* next: dir */ + token = strtok_r(NULL, ":", &saveptr); + if (!token) + continue; + snprintf(pwstruct_dir, sizeof(pwstruct_dir), "%s", token); + + /* next: shell */ + token = strtok_r(NULL, ":", &saveptr); + if (!token) + continue; + snprintf(pwstruct_shell, sizeof(pwstruct_shell), "%s", token); + + /* sanity check */ + token = strtok_r(NULL, ":", &saveptr); + if (token) + continue; + + /* we successfully parsed an entry that matched, we're done, + * just make sure the structure was not modified in the caller + * and make it point back to our own static fields + */ + result.pw_name = pwstruct_name; + result.pw_passwd = pwstruct_passwd; + result.pw_gecos = pwstruct_gecos; + result.pw_dir = pwstruct_dir; + result.pw_shell = pwstruct_shell; + + found = 1; + } + + free(line); + fclose(pipe_f); + again: + if (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) + goto again; + return NULL; + } + + /* some sanity checks: if anything even hinted at going + * wrong: we can't be sure we have a valid result, so + * we assume we don't + */ + + if (!WIFEXITED(status)) + return NULL; + + if (WEXITSTATUS(status) != 0) + return NULL; + + if (!found) + return NULL; + + return &result; + } else { + /* child process */ + char uid_buf[32]; + char *arguments[] = { + "getent", + "passwd", + uid_buf, + NULL + }; + + close(pipes[0]); + + /* we want to capture stdout */ + dup2(pipes[1], 1); + close(pipes[1]); + + /* get rid of stdin/stderr, so we try to associate it + * with /dev/null + */ + fd = open("/dev/null", O_RDWR); + if (fd < 0) { + close(0); + close(2); + } else { + dup2(fd, 0); + dup2(fd, 2); + close(fd); + } + + /* finish argument list */ + ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long) uid); + if (ret <= 0) + exit(-1); + + /* try to run getent program */ + (void) execvp("getent", arguments); + exit(-1); + } +} diff --git a/src/lxc/attach.h b/src/lxc/attach.h index 4d4f719..90e693a 100644 --- a/src/lxc/attach.h +++ b/src/lxc/attach.h @@ -38,4 +38,7 @@ extern int lxc_attach_to_ns(pid_t other_pid, int which); extern int lxc_attach_remount_sys_proc(); extern int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx); +struct passwd; +extern struct passwd *lxc_attach_getpwuid(uid_t uid); + #endif diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c index 1f60266..d84c3d8 100644 --- a/src/lxc/lxc_attach.c +++ b/src/lxc/lxc_attach.c @@ -438,6 +438,17 @@ int main(int argc, char *argv[]) uid = getuid(); passwd = getpwuid(uid); + + /* this probably happens because of incompatible nss + * implementations in host and container (remember, this + * code is still using the host's glibc but our mount + * namespace is in the container) + * we may try to get the information by spawning a + * [getent passwd uid] process and parsing the result + */ + if (!passwd) + passwd = lxc_attach_getpwuid(uid); + if (!passwd) { SYSERROR("failed to get passwd " \ "entry for uid '%d'", uid); -- 1.7.10.4 ------------------------------------------------------------------------------ Symantec Endpoint Protection 12 positioned as A LEADER in The Forrester Wave(TM): Endpoint Security, Q1 2013 and "remains a good choice" in the endpoint security space. For insight on selecting the right partner to tackle endpoint security challenges, access the full report. http://p.sf.net/sfu/symantec-dev2dev _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel