diff options
Diffstat (limited to 'utils/pscap.c')
-rw-r--r-- | utils/pscap.c | 117 |
1 files changed, 90 insertions, 27 deletions
diff --git a/utils/pscap.c b/utils/pscap.c index e51b10a..9b96973 100644 --- a/utils/pscap.c +++ b/utils/pscap.c @@ -1,6 +1,6 @@ /* * pscap.c - A program that lists running processes with capabilities - * Copyright (c) 2009 Red Hat Inc., Durham, North Carolina. + * Copyright (c) 2009,2012,2020 Red Hat Inc. * All Rights Reserved. * * This software may be freely redistributed and/or modified under the @@ -15,7 +15,8 @@ * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to the - * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1335, USA. * * Authors: * Steve Grubb <sgrubb@redhat.com> @@ -24,28 +25,63 @@ #include "config.h" #include <stdio.h> #include <stdio_ext.h> +#include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <dirent.h> #include <fcntl.h> #include <pwd.h> +#include <stdbool.h> +#include <sys/stat.h> #include "cap-ng.h" +#define CMD_LEN 16 +#define USERNS_MARK_LEN 2 static void usage(void) { - fprintf(stderr, "usage: pscap [-a]\n"); + fprintf(stderr, "usage: pscap [-a|-p pid]\n"); exit(1); } +/* + * Precise recursive checks for parent-child relation between namespaces + * using ioctl() were avoided, because there didn't seem to be any case when + * we may dereference the namespace symlink in /proc/PID/ns for processes in + * user namespaces other than the current or child ones. Thus, the check just + * tries to dereference the link and checks that it does not point to the + * current NS. + */ +static bool in_child_userns(int pid) +{ + char ns_file_path[32]; + struct stat statbuf; + ino_t own_ns_inode; + dev_t own_ns_dev; + + if (stat("/proc/self/ns/user", &statbuf) < 0) + return false; + + own_ns_inode = statbuf.st_ino; + own_ns_dev = statbuf.st_dev; + + snprintf(ns_file_path, 32, "/proc/%d/ns/user", pid); + if (stat(ns_file_path, &statbuf) < 0) + return false; + + return statbuf.st_ino != own_ns_inode || statbuf.st_dev != own_ns_dev; +} + int main(int argc, char *argv[]) { DIR *d; struct dirent *ent; int header = 0, show_all = 0, caps; + pid_t our_pid = getpid(); + pid_t target_pid = 0; - if (argc > 2) { + if (argc > 3) { fputs("Too many arguments\n", stderr); usage(); } @@ -55,23 +91,29 @@ int main(int argc, char *argv[]) else usage(); } - - /* Drop capabilities so we aren't listed */ - caps = capng_have_capabilities(CAPNG_SELECT_CAPS); - if (caps == CAPNG_FULL) { - capng_clear(CAPNG_SELECT_BOTH); - capng_apply(CAPNG_SELECT_BOTH); + else if (argc == 3) { + if (strcmp(argv[1], "-p") == 0) { + errno = 0; + target_pid = strtol(argv[2], NULL, 10); + if (errno) { + fprintf(stderr, "Can't read pid: %s\n", argv[2]); + return 1; + } + if (target_pid == 1) + show_all = 1; + } else + usage(); } d = opendir("/proc"); if (d == NULL) { - printf("Can't open /proc: %s\n", strerror(errno)); + fprintf(stderr, "Can't open /proc: %s\n", strerror(errno)); return 1; } while (( ent = readdir(d) )) { int pid, ppid, uid = -1, euid = -1; char buf[100]; - char *tmp, cmd[16], state, *name = NULL; + char *tmp, cmd[CMD_LEN + USERNS_MARK_LEN], state, *name = NULL; int fd, len; struct passwd *p; @@ -83,9 +125,16 @@ int main(int argc, char *argv[]) if (errno) continue; + if (target_pid && (pid != target_pid)) + continue; + + /* Skip our pid so we aren't listed */ + if (pid == our_pid) + continue; + // Parse up the stat file for the proc snprintf(buf, 32, "/proc/%d/stat", pid); - fd = open(buf, O_RDONLY, 0); + fd = open(buf, O_RDONLY|O_CLOEXEC, 0); if (fd < 0) continue; len = read(fd, buf, sizeof buf - 1); @@ -110,11 +159,11 @@ int main(int argc, char *argv[]) continue; // now get the capabilities - capng_clear(CAPNG_SELECT_BOTH); + capng_clear(CAPNG_SELECT_ALL); capng_setpid(pid); if (capng_get_caps_process()) continue; - + // And print out anything with capabilities caps = capng_have_capabilities(CAPNG_SELECT_CAPS); if (caps > CAPNG_NONE) { @@ -122,7 +171,7 @@ int main(int argc, char *argv[]) FILE *f; int line; snprintf(buf, 32, "/proc/%d/status", pid); - f = fopen(buf, "rt"); + f = fopen(buf, "rte"); if (f == NULL) euid = 0; else { @@ -142,12 +191,10 @@ int main(int argc, char *argv[]) } fclose(f); } - - len = read(fd, buf, sizeof buf - 1); - close(fd); + if (header == 0) { - printf("%-5s %-5s %-10s %-16s %s\n", - "ppid", "pid", "name", "command", + printf("%-5s %-5s %-10s %-18s %s\n", + "ppid", "pid", "uid", "command", "capabilities"); header = 1; } @@ -163,24 +210,40 @@ int main(int argc, char *argv[]) name = p->pw_name; // If not taking this branch, use last val } + + if (in_child_userns(pid)) + strcat(cmd, " *"); + if (name) { - printf("%-5d %-5d %-10s %-16s ", ppid, pid, + printf("%-5d %-5d %-10s %-18s ", ppid, pid, name, cmd); } else - printf("%-5d %-5d %-10d %-16s ", ppid, pid, + printf("%-5d %-5d %-10d %-18s ", ppid, pid, uid, cmd); if (caps == CAPNG_PARTIAL) { capng_print_caps_text(CAPNG_PRINT_STDOUT, CAPNG_PERMITTED); + if (capng_have_capabilities( + CAPNG_SELECT_AMBIENT) > CAPNG_NONE) + printf(" @"); if (capng_have_capabilities(CAPNG_SELECT_BOUNDS) - == CAPNG_FULL) + > CAPNG_NONE) printf(" +"); printf("\n"); - } else - printf("full\n"); + } else { + printf("full"); + if (capng_have_capabilities( + CAPNG_SELECT_AMBIENT) > CAPNG_NONE) + printf(" @"); + if (capng_have_capabilities(CAPNG_SELECT_BOUNDS) + > CAPNG_NONE) + printf(" +"); + printf("\n"); + } } } - closedir(d); + closedir(d); return 0; } + |