Commit 905022f7 by Christian Seiler Committed by Serge Hallyn

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: 's avatarChristian Seiler <christian@iwakd.de>
parent 66b50342
......@@ -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
......@@ -274,3 +276,155 @@ int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
return 0;
}
char *lxc_attach_getpwshell(uid_t uid)
{
/* local variables */
pid_t pid;
int pipes[2];
int ret;
int fd;
char *result = NULL;
/* 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;
/* next: dummy password field */
token = strtok_r(NULL, ":", &saveptr);
if (!token)
continue;
/* 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;
/* dummy sanity check: user id matches */
if ((uid_t) value != uid)
continue;
/* skip fields: gid, gecos, dir, go to next field 'shell' */
for (i = 0; i < 4; i++) {
token = strtok_r(NULL, ":", &saveptr);
if (!token)
break;
}
if (!token)
continue;
result = strdup(token);
/* sanity check that there are no fields after that */
token = strtok_r(NULL, ":", &saveptr);
if (token)
continue;
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);
}
}
......@@ -38,4 +38,6 @@ 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);
extern char *lxc_attach_getpwshell(uid_t uid);
#endif
......@@ -132,6 +132,7 @@ int main(int argc, char *argv[])
uid_t uid;
char *curdir;
int cgroup_ipc_sockets[2];
char *user_shell;
ret = lxc_caps_init();
if (ret)
......@@ -438,7 +439,20 @@ int main(int argc, char *argv[])
uid = getuid();
passwd = getpwuid(uid);
if (!passwd) {
/* 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)
user_shell = lxc_attach_getpwshell(uid);
else
user_shell = passwd->pw_shell;
if (!user_shell) {
SYSERROR("failed to get passwd " \
"entry for uid '%d'", uid);
return -1;
......@@ -446,7 +460,7 @@ int main(int argc, char *argv[])
{
char *const args[] = {
passwd->pw_shell,
user_shell,
NULL,
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment