diff --git a/test/nstool.c b/test/nstool.c index 345b908..2682c6b 100644 --- a/test/nstool.c +++ b/test/nstool.c @@ -15,8 +15,13 @@ #include #include #include +#include +#include #include #include +#include + +#define ARRAY_SIZE(a) ((int)(sizeof(a) / sizeof((a)[0]))) #define die(...) \ do { \ @@ -24,6 +29,28 @@ exit(1); \ } while (0) +struct ns_type { + int flag; + const char *name; +}; + +const struct ns_type nstypes[] = { + { CLONE_NEWCGROUP, "cgroup" }, + { CLONE_NEWIPC, "ipc" }, + { CLONE_NEWNET, "net" }, + { CLONE_NEWNS, "mnt" }, + { CLONE_NEWPID, "pid" }, + { CLONE_NEWTIME, "time" }, + { CLONE_NEWUSER, "user" }, + { CLONE_NEWUTS, "uts" }, +}; + +struct holder_info { + pid_t pid; + uid_t uid; + gid_t gid; +}; + static void usage(void) { die("Usage:\n" @@ -41,12 +68,16 @@ static void usage(void) " terminate.\n"); } -static int connect_ctl(const char * sockpath, bool wait) +static int connect_ctl(const char *sockpath, bool wait, + struct holder_info *info, + struct ucred *peercred) { int fd = socket(AF_UNIX, SOCK_STREAM, PF_UNIX); struct sockaddr_un addr = { .sun_family = AF_UNIX, }; + struct holder_info discard; + ssize_t len; int rc; if (fd < 0) @@ -61,6 +92,28 @@ static int connect_ctl(const char * sockpath, bool wait) die("connect() to %s: %s\n", sockpath, strerror(errno)); } while (rc < 0); + if (!info) + info = &discard; + + /* Always read the info structure, even if we don't need it, + * so that the holder doesn't get a broken pipe error + */ + len = read(fd, info, sizeof(*info)); + if (len < 0) + die("read() on control socket %s: %s\n", sockpath, strerror(errno)); + if ((size_t)len < sizeof(*info)) + die("short read() on control socket %s\n", sockpath); + + if (peercred) { + socklen_t optlen = sizeof(*peercred); + + rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, + peercred, &optlen); + if (rc < 0) + die("getsockopet(SO_PEERCRED) %s: %s\n", + sockpath, strerror(errno)); + } + return fd; } @@ -71,6 +124,7 @@ static void cmd_hold(int argc, char *argv[]) .sun_family = AF_UNIX, }; const char *sockpath = argv[1]; + struct holder_info info; int rc; if (argc != 2) @@ -89,8 +143,10 @@ static void cmd_hold(int argc, char *argv[]) if (rc < 0) die("listen() on %s: %s\n", sockpath, strerror(errno)); - printf("nstool hold: local PID=%d local UID=%u local GID=%u\n", - getpid(), getuid(), getgid()); + info.pid = getpid(); + info.uid = getuid(); + info.gid = getgid(); + do { int afd = accept(fd, NULL, NULL); char buf; @@ -98,6 +154,12 @@ static void cmd_hold(int argc, char *argv[]) if (afd < 0) die("accept(): %s\n", strerror(errno)); + rc = write(afd, &info, sizeof(info)); + if (rc < 0) + die("write(): %s\n", strerror(errno)); + if ((size_t)rc < sizeof(info)) + die("short write() on control socket\n"); + rc = read(afd, &buf, sizeof(buf)); if (rc < 0) die("read(): %s\n", strerror(errno)); @@ -106,6 +168,68 @@ static void cmd_hold(int argc, char *argv[]) unlink(sockpath); } +static ssize_t getlink(char *buf, size_t bufsiz, const char *fmt, ...) +{ + char linkpath[PATH_MAX]; + ssize_t linklen; + va_list ap; + + va_start(ap, fmt); + if (vsnprintf(linkpath, sizeof(linkpath), fmt, ap) >= PATH_MAX) + die("Truncated path \"%s\"\n", linkpath); + va_end(ap); + + linklen = readlink(linkpath, buf, bufsiz); + if (linklen < 0) + die("readlink() on %s: %s\n", linkpath, strerror(errno)); + if ((size_t)linklen >= bufsiz) + die("Target of symbolic link %s is too long\n", linkpath); + + return linklen; +} + +static int detect_namespaces(pid_t pid) +{ + int i; + int flags = 0; + + for (i = 0; i < ARRAY_SIZE(nstypes); i++) { + const struct ns_type *nst = &nstypes[i]; + char selflink[PATH_MAX], pidlink[PATH_MAX]; + ssize_t selflen, pidlen; + + selflen = getlink(selflink, sizeof(selflink), + "/proc/self/ns/%s", nst->name); + pidlen = getlink(pidlink, sizeof(pidlink), + "/proc/%d/ns/%s", pid, nst->name); + + if ((selflen != pidlen) || memcmp(selflink, pidlink, selflen)) + flags |= nst->flag; + } + + return flags; +} + +static void print_nstypes(int flags) +{ + bool first = true; + int i; + + for (i = 0; i < ARRAY_SIZE(nstypes); i++) { + const struct ns_type *nst = &nstypes[i]; + + if (!(flags & nst->flag)) + continue; + + printf("%s%s", first ? "" : ", " , nst->name); + first = false; + flags &= ~nst->flag; + } + + if (flags) + printf("%s0x%x", first ? "" : ", ", flags); +} + static void cmd_info(int argc, char *argv[]) { const struct option options[] = { @@ -114,11 +238,11 @@ static void cmd_info(int argc, char *argv[]) { 0 }, }; bool pidonly = false, waitforsock = false; - struct ucred peercred; - socklen_t optlen = sizeof(peercred); const char *optstring = "pw"; + struct holder_info info; + struct ucred peercred; const char *sockpath; - int fd, rc, opt; + int fd, opt; do { opt = getopt_long(argc, argv, optstring, options, NULL); @@ -144,23 +268,28 @@ static void cmd_info(int argc, char *argv[]) sockpath = argv[optind]; - fd = connect_ctl(sockpath, waitforsock); - - rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, - &peercred, &optlen); - if (rc < 0) - die("getsockopet(SO_PEERCRED) %s: %s\n", - sockpath, strerror(errno)); + fd = connect_ctl(sockpath, waitforsock, &info, &peercred); close(fd); if (pidonly) { printf("%d\n", peercred.pid); } else { + int flags = detect_namespaces(peercred.pid); + + printf("Namespaces: "); + print_nstypes(flags); + printf("\n"); + printf("As seen from calling context:\n"); printf("\tPID:\t%d\n", peercred.pid); printf("\tUID:\t%u\n", peercred.uid); printf("\tGID:\t%u\n", peercred.gid); + + printf("As seen from holding context:\n"); + printf("\tPID:\t%d\n", info.pid); + printf("\tUID:\t%u\n", info.uid); + printf("\tGID:\t%u\n", info.gid); } } @@ -173,7 +302,7 @@ static void cmd_stop(int argc, char *argv[]) if (argc != 2) usage(); - fd = connect_ctl(sockpath, false); + fd = connect_ctl(sockpath, false, NULL, NULL); rc = write(fd, &buf, sizeof(buf)); if (rc < 0)