Merge pull request #2535 from brauner/2018-08-16/cmd_fixes

log: add logging tools for commands; lxc-usernsexec: cleanup and bugfixes
This commit is contained in:
Stéphane Graber 2018-08-16 11:44:27 -04:00 committed by GitHub
commit d1bf8af11b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 189 additions and 119 deletions

View File

@ -325,7 +325,12 @@ lxc_user_nic_SOURCES = cmd/lxc_user_nic.c \
namespace.c namespace.h \ namespace.c namespace.h \
network.c network.h \ network.c network.h \
parse.c parse.h parse.c parse.h
lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c \
conf.c conf.h \
list.h \
log.c log.h \
namespace.c namespace.h \
utils.c utils.h
endif endif

View File

@ -42,6 +42,8 @@
#include <grp.h> #include <grp.h>
#include "conf.h" #include "conf.h"
#include "list.h"
#include "log.h"
#include "namespace.h" #include "namespace.h"
#include "utils.h" #include "utils.h"
@ -77,21 +79,22 @@ static void usage(const char *name)
static void opentty(const char *tty, int which) static void opentty(const char *tty, int which)
{ {
int fd, flags; int fd, flags, ret;
if (tty[0] == '\0') if (tty[0] == '\0')
return; return;
fd = open(tty, O_RDWR | O_NONBLOCK); fd = open(tty, O_RDWR | O_NONBLOCK);
if (fd < 0) { if (fd < 0) {
printf("WARN: could not reopen tty: %s\n", strerror(errno)); CMD_SYSERROR("Failed to open tty");
return; return;
} }
flags = fcntl(fd, F_GETFL); flags = fcntl(fd, F_GETFL);
flags &= ~O_NONBLOCK; flags &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) { ret = fcntl(fd, F_SETFL, flags);
printf("WARN: could not set fd flags: %s\n", strerror(errno)); if (ret < 0) {
CMD_SYSINFO("Failed to remove O_NONBLOCK from file descriptor %d", fd);
close(fd); close(fd);
return; return;
} }
@ -106,51 +109,61 @@ static void opentty(const char *tty, int which)
static int do_child(void *vargv) static int do_child(void *vargv)
{ {
int ret;
char **argv = (char **)vargv; char **argv = (char **)vargv;
/* Assume we want to become root */ /* Assume we want to become root */
if (setgid(0) < 0) { ret = setgid(0);
perror("setgid"); if (ret < 0) {
CMD_SYSERROR("Failed to set gid to");
return -1; return -1;
} }
if (setuid(0) < 0) {
perror("setuid"); ret = setuid(0);
if (ret < 0) {
CMD_SYSERROR("Failed to set uid to 0");
return -1; return -1;
} }
if (setgroups(0, NULL) < 0) {
perror("setgroups"); ret = setgroups(0, NULL);
if (ret < 0) {
CMD_SYSERROR("Failed to clear supplementary groups");
return -1; return -1;
} }
if (unshare(CLONE_NEWNS) < 0) {
perror("unshare CLONE_NEWNS"); ret = unshare(CLONE_NEWNS);
if (ret < 0) {
CMD_SYSERROR("Failed to unshare mount namespace");
return -1; return -1;
} }
if (detect_shared_rootfs()) { if (detect_shared_rootfs()) {
if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) { ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
printf("Failed to make / rslave\n"); if (ret < 0) {
CMD_SYSINFO("Failed to make \"/\" rslave");
return -1; return -1;
} }
} }
execvp(argv[0], argv); execvp(argv[0], argv);
perror("execvpe"); CMD_SYSERROR("Failed to execute \"%s\"", argv[0]);
return -1; return -1;
} }
static struct lxc_list active_map; static struct lxc_list active_map;
/* /*
* given a string like "b:0:100000:10", map both uids and gids * Given a string like "b:0:100000:10", map both uids and gids 0-10 to 100000
* 0-10 to 100000 to 100010 * to 100010
*/ */
static int parse_map(char *map) static int parse_map(char *map)
{ {
struct id_map *newmap; int i, ret;
struct lxc_list *tmp = NULL;
int ret;
int i;
char types[2] = {'u', 'g'};
char which;
long host_id, ns_id, range; long host_id, ns_id, range;
char which;
struct id_map *newmap;
char types[2] = {'u', 'g'};
struct lxc_list *tmp = NULL;
if (!map) if (!map)
return -1; return -1;
@ -193,45 +206,49 @@ static int parse_map(char *map)
} }
/* /*
* This is called if the user did not pass any uid ranges in * This is called if the user did not pass any uid ranges in through -m flags.
* through -m flags. It's called once to get the default uid * It's called once to get the default uid map, and once for the default gid
* map, and once for the default gid map. * map.
* Go through /etc/subuids and /etc/subgids to find this user's * Go through /etc/subuids and /etc/subgids to find this user's allowed map. We
* allowed map. We only use the first one for each of uid and * only use the first one for each of uid and gid, because otherwise we're not
* gid, because otherwise we're not sure which entries the user * sure which entries the user wanted.
* wanted.
*/ */
static int read_default_map(char *fnam, int which, char *username) static int read_default_map(char *fnam, int which, char *username)
{ {
FILE *fin;
char *line = NULL;
size_t sz = 0;
struct id_map *newmap;
struct lxc_list *tmp = NULL;
char *p1, *p2; char *p1, *p2;
FILE *fin;
struct id_map *newmap;
size_t sz = 0;
char *line = NULL;
struct lxc_list *tmp = NULL;
fin = fopen(fnam, "r"); fin = fopen(fnam, "r");
if (!fin) if (!fin)
return -1; return -1;
while (getline(&line, &sz, fin) != -1) { while (getline(&line, &sz, fin) != -1) {
if (sz <= strlen(username) || if (sz <= strlen(username) ||
strncmp(line, username, strlen(username)) != 0 || strncmp(line, username, strlen(username)) != 0 ||
line[strlen(username)] != ':') line[strlen(username)] != ':')
continue; continue;
p1 = strchr(line, ':'); p1 = strchr(line, ':');
if (!p1) if (!p1)
continue; continue;
p2 = strchr(p1+1, ':');
p2 = strchr(p1 + 1, ':');
if (!p2) if (!p2)
continue; continue;
newmap = malloc(sizeof(*newmap)); newmap = malloc(sizeof(*newmap));
if (!newmap) { if (!newmap) {
fclose(fin); fclose(fin);
free(line); free(line);
return -1; return -1;
} }
newmap->hostid = atol(p1+1);
newmap->range = atol(p2+1); newmap->hostid = atol(p1 + 1);
newmap->range = atol(p2 + 1);
newmap->nsid = 0; newmap->nsid = 0;
newmap->idtype = which; newmap->idtype = which;
@ -250,16 +267,17 @@ static int read_default_map(char *fnam, int which, char *username)
free(line); free(line);
fclose(fin); fclose(fin);
return 0; return 0;
} }
static int find_default_map(void) static int find_default_map(void)
{ {
struct passwd pwent;
struct passwd *pwentp = NULL;
char *buf;
size_t bufsize; size_t bufsize;
int ret; char *buf;
struct passwd pwent;
int ret = -1;
struct passwd *pwentp = NULL;
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (bufsize == -1) if (bufsize == -1)
@ -272,60 +290,58 @@ static int find_default_map(void)
ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp); ret = getpwuid_r(getuid(), &pwent, buf, bufsize, &pwentp);
if (!pwentp) { if (!pwentp) {
if (ret == 0) if (ret == 0)
printf("WARN: could not find matched password record\n"); CMD_SYSERROR("Failed to find matched password record");
printf("Failed to get password record - %u\n", getuid()); CMD_SYSERROR("Failed to get password record for uid %d", getuid());
free(buf); ret = -1;
return -1; goto out;
} }
if (read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name) < 0) { ret = read_default_map(subuidfile, ID_TYPE_UID, pwent.pw_name);
free(buf); if (ret < 0)
return -1; goto out;
}
if (read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name) < 0) { ret = read_default_map(subgidfile, ID_TYPE_GID, pwent.pw_name);
free(buf); if (ret < 0)
return -1; goto out;
}
ret = 0;
out:
free(buf); free(buf);
return 0;
return ret;
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int c; int c, pid, ret, status;
unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
char ttyname0[256], ttyname1[256], ttyname2[256];
int status;
int ret;
int pid;
char *default_args[] = {"/bin/sh", NULL};
char buf[1]; char buf[1];
int pipe_fds1[2], /* child tells parent it has unshared */ int pipe_fds1[2], /* child tells parent it has unshared */
pipe_fds2[2]; /* parent tells child it is mapped and may proceed */ pipe_fds2[2]; /* parent tells child it is mapped and may proceed */
unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
char ttyname0[256] = {0}, ttyname1[256] = {0}, ttyname2[256] = {0};
char *default_args[] = {"/bin/sh", NULL};
lxc_log_fd = STDERR_FILENO; lxc_log_fd = STDERR_FILENO;
memset(ttyname0, '\0', sizeof(ttyname0)); if (isatty(STDIN_FILENO)) {
memset(ttyname1, '\0', sizeof(ttyname1));
memset(ttyname2, '\0', sizeof(ttyname2));
if (isatty(0)) {
ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0)); ret = readlink("/proc/self/fd/0", ttyname0, sizeof(ttyname0));
if (ret < 0) { if (ret < 0) {
perror("unable to open stdin."); CMD_SYSERROR("Failed to open stdin");
exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1)); ret = readlink("/proc/self/fd/1", ttyname1, sizeof(ttyname1));
if (ret < 0) { if (ret < 0) {
printf("Warning: unable to open stdout, continuing.\n"); CMD_SYSINFO("Failed to open stdout. Continuing");
memset(ttyname1, '\0', sizeof(ttyname1)); ttyname1[0] = '\0';
} }
ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2)); ret = readlink("/proc/self/fd/2", ttyname2, sizeof(ttyname2));
if (ret < 0) { if (ret < 0) {
printf("Warning: unable to open stderr, continuing.\n"); CMD_SYSINFO("Failed to open stderr. Continuing");
memset(ttyname2, '\0', sizeof(ttyname2)); ttyname2[0] = '\0';
} }
} }
@ -334,24 +350,26 @@ int main(int argc, char *argv[])
while ((c = getopt(argc, argv, "m:h")) != EOF) { while ((c = getopt(argc, argv, "m:h")) != EOF) {
switch (c) { switch (c) {
case 'm': case 'm':
if (parse_map(optarg)) { ret = parse_map(optarg);
if (ret < 0) {
usage(argv[0]); usage(argv[0]);
exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
break; break;
case 'h': case 'h':
usage(argv[0]); usage(argv[0]);
exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
default: default:
usage(argv[0]); usage(argv[0]);
exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
}; };
if (lxc_list_empty(&active_map)) { if (lxc_list_empty(&active_map)) {
if (find_default_map()) { ret = find_default_map();
fprintf(stderr, "You have no allocated subuids or subgids\n"); if (ret < 0) {
exit(EXIT_FAILURE); fprintf(stderr, "Failed to find subuid or subgid allocation\n");
_exit(EXIT_FAILURE);
} }
} }
@ -360,68 +378,103 @@ int main(int argc, char *argv[])
if (argc < 1) if (argc < 1)
argv = default_args; argv = default_args;
if (pipe2(pipe_fds1, O_CLOEXEC) < 0 || pipe2(pipe_fds2, O_CLOEXEC) < 0) { ret = pipe2(pipe_fds1, O_CLOEXEC);
perror("pipe"); if (ret < 0) {
exit(EXIT_FAILURE); CMD_SYSERROR("Failed to open new pipe");
_exit(EXIT_FAILURE);
} }
ret = pipe2(pipe_fds2, O_CLOEXEC);
if (ret < 0) {
CMD_SYSERROR("Failed to open new pipe");
close(pipe_fds1[0]);
close(pipe_fds1[1]);
_exit(EXIT_FAILURE);
}
pid = fork(); pid = fork();
if (pid == 0) { /* Child. */ if (pid < 0) {
close(pipe_fds1[0]);
close(pipe_fds1[1]);
close(pipe_fds2[0]);
close(pipe_fds2[1]);
_exit(EXIT_FAILURE);
}
if (pid == 0) {
close(pipe_fds1[0]); close(pipe_fds1[0]);
close(pipe_fds2[1]); close(pipe_fds2[1]);
opentty(ttyname0, 0);
opentty(ttyname1, 1); opentty(ttyname0, STDIN_FILENO);
opentty(ttyname2, 2); opentty(ttyname1, STDOUT_FILENO);
opentty(ttyname2, STDERR_FILENO);
ret = unshare(flags); ret = unshare(flags);
if (ret < 0) { if (ret < 0) {
fprintf(stderr, CMD_SYSERROR("Failed to unshare mount and user namespace");
"Failed to unshare mount and user namespace\n"); close(pipe_fds1[1]);
return 1; close(pipe_fds2[0]);
_exit(EXIT_FAILURE);
} }
buf[0] = '1'; buf[0] = '1';
if (lxc_write_nointr(pipe_fds1[1], buf, 1) < 1) { ret = lxc_write_nointr(pipe_fds1[1], buf, 1);
perror("write pipe"); if (ret != 1) {
exit(EXIT_FAILURE); CMD_SYSERROR("Failed to write to pipe file descriptor %d",
pipe_fds1[1]);
close(pipe_fds1[1]);
close(pipe_fds2[0]);
_exit(EXIT_FAILURE);
} }
if (lxc_read_nointr(pipe_fds2[0], buf, 1) < 1) {
perror("read pipe"); ret = lxc_read_nointr(pipe_fds2[0], buf, 1);
exit(EXIT_FAILURE); if (ret != 1) {
} CMD_SYSERROR("Failed to read from pipe file descriptor %d",
if (buf[0] != '1') { pipe_fds2[0]);
fprintf(stderr, "parent had an error, child exiting\n"); close(pipe_fds1[1]);
exit(EXIT_FAILURE); close(pipe_fds2[0]);
_exit(EXIT_FAILURE);
} }
close(pipe_fds1[1]); close(pipe_fds1[1]);
close(pipe_fds2[0]); close(pipe_fds2[0]);
return do_child((void *)argv);
if (buf[0] != '1') {
fprintf(stderr, "Received unexpected value from parent process\n");
_exit(EXIT_FAILURE);
}
ret = do_child((void *)argv);
if (ret < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
} }
close(pipe_fds1[1]); close(pipe_fds1[1]);
close(pipe_fds2[0]); close(pipe_fds2[0]);
ret = lxc_read_nointr(pipe_fds1[0], buf, 1); ret = lxc_read_nointr(pipe_fds1[0], buf, 1);
if (ret < 0) { if (ret <= 0)
perror("read pipe"); CMD_SYSERROR("Failed to read from pipe file descriptor %d", pipe_fds1[0]);
exit(EXIT_FAILURE);
} else if (ret == 0) {
fprintf(stderr, "Failed to read from pipe\n");
exit(EXIT_FAILURE);
}
buf[0] = '1'; buf[0] = '1';
if (lxc_map_ids(&active_map, pid)) ret = lxc_map_ids(&active_map, pid);
fprintf(stderr, "error mapping child\n"); if (ret < 0)
fprintf(stderr, "Failed to write id mapping for child process\n");
if (lxc_write_nointr(pipe_fds2[1], buf, 1) < 0) { ret = lxc_write_nointr(pipe_fds2[1], buf, 1);
perror("write to pipe"); if (ret < 0) {
exit(EXIT_FAILURE); CMD_SYSERROR("Failed to write to pipe file descriptor %d", pipe_fds2[1]);
_exit(EXIT_FAILURE);
} }
ret = waitpid(pid, &status, __WALL); ret = waitpid(pid, &status, __WALL);
if (ret < 0) { if (ret < 0) {
printf("waitpid() returns %d, errno %d\n", ret, errno); CMD_SYSERROR("Failed to wait on child process");
exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
} }
exit(WEXITSTATUS(status)); _exit(WEXITSTATUS(status));
} }

View File

@ -429,6 +429,18 @@ ATTR_UNUSED static inline void LXC_##LEVEL(struct lxc_log_locinfo* locinfo, \
ERROR("%s - " format, ptr, ##__VA_ARGS__); \ ERROR("%s - " format, ptr, ##__VA_ARGS__); \
} while (0) } while (0)
#define CMD_SYSERROR(format, ...) \
do { \
lxc_log_strerror_r; \
fprintf(stderr, "%s - " format, ptr, ##__VA_ARGS__); \
} while (0)
#define CMD_SYSINFO(format, ...) \
do { \
lxc_log_strerror_r; \
printf("%s - " format, ptr, ##__VA_ARGS__); \
} while (0)
extern int lxc_log_fd; extern int lxc_log_fd;
extern int lxc_log_syslog(int facility); extern int lxc_log_syslog(int facility);