[lxc-devel] [PATCH 1/3] lxc-attach: Try really hard to determine login shell
Christian Seiler
christian at iwakd.de
Tue Mar 5 21:46:43 UTC 2013
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 at 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
More information about the lxc-devel
mailing list