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

Reply via email to