Logo Search packages:      
Sourcecode: pax-utils version File versions  Download package

pspax.c

/******************************************************************************/
/* THE BEER-WARE LICENSE   (Revision 42):                                     */
/*  As long as you retain this notice you can do whatever you want with this  */
/*   stuff. If we meet some day, and you think this stuff is worth it,        */
/*   you can buy me a beer in return.    Ned Ludd. --solarx                   */
/******************************************************************************/

/*
 * normal compile.
 *  cc -o pspax pspax.c
 * or with libcap.
 *  cc -o pspax pspax.c -DWANT_SYSCAP -lcap
 */

static const char *rcsid = "$Id: pspax.c,v 1.49 2010/12/08 01:24:01 vapier Exp $";
const char argv0[] = "pspax";

#include "paxinc.h"
#include <grp.h>

#ifdef WANT_SYSCAP
# undef _POSIX_SOURCE
# include <sys/capability.h>
# define WRAP_SYSCAP(x) x
#else
# define WRAP_SYSCAP(x)
#endif

#define PROC_DIR "/proc"

/* variables to control behavior */
static char show_all = 0;
static char verbose = 0;
static char show_banner = 1;
static char show_phdr = 0;
static char show_addr = 0;
static char noexec = 1;
static char writeexec = 1;
static char wide_output = 0;
static pid_t show_pid = 0;
static uid_t show_uid = -1;
static gid_t show_gid = -1;

static FILE *proc_fopen(pid_t pid, const char *file)
{
      char path[__PAX_UTILS_PATH_MAX];
      snprintf(path, sizeof(path), PROC_DIR "/%u/%s", pid, file);
      path[sizeof(path) - 1] = '\0';
      return fopen(path, "r");
}

static char *get_proc_name_cmdline(pid_t pid)
{
      FILE *fp;
      static char str[1024];

      fp = proc_fopen(pid, "cmdline");
      if (fp == NULL)
            return NULL;

      if (fscanf(fp, "%s.1023", str) != 1) {
            fclose(fp);
            return NULL;
      }
      return (str);
}

static char *get_proc_name(pid_t pid)
{
      FILE *fp;
      static char str[BUFSIZ];

      if (wide_output)
            return get_proc_name_cmdline(pid);

      fp = proc_fopen(pid, "stat");
      if (fp == NULL)
            return NULL;

      if (fscanf(fp, "%*d %s.16", str) != 1) {
            fclose(fp);
            return NULL;
      }

      if (*str) {
            str[strlen(str) - 1] = '\0';
            str[16] = 0;
      }
      fclose(fp);

      return (str+1);
}

static int get_proc_maps(pid_t pid)
{
      FILE *fp;
      static char str[BUFSIZ];

      if ((fp = proc_fopen(pid, "maps")) == NULL)
            return -1;

      while (fgets(str, sizeof(str), fp)) {
            char *p;
            if ((p = strchr(str, ' ')) != NULL) {
                  if (strlen(p) < 6)
                        continue;
                  /* 0x0-0x0 rwxp fffff000 00:00 0 */
                  /* 0x0-0x0 R+W+XP fffff000 00:00 0 */
                  ++p; /* ' ' */
                  ++p; /*  r  */
                  if (*p == '+')
                        ++p;
                  /* FIXME: all of wx, w+, +x, ++ indicate w|x */
                  if (tolower(*p) == 'w') {
                        ++p;
                        if (*p == '+')
                              ++p;
                        if (tolower(*p) == 'x') {
                              fclose(fp);
                              return 1;
                        }
                  }
            }
      }
      fclose(fp);

      return 0;
}

static int print_executable_mappings(pid_t pid)
{
      FILE *fp;
      static char str[BUFSIZ];

      if ((fp = proc_fopen(pid, "maps")) == NULL)
            return -1;

      while (fgets(str, sizeof(str), fp)) {
            char *p;
            if ((p = strchr(str, ' ')) != NULL) {
                  if (strlen(p) < 6)
                        continue;
                  /* 0x0-0x0 rwxp fffff000 00:00 0 */
                  /* 0x0-0x0 R+W+XP fffff000 00:00 0 */
                  ++p; /* ' ' */
                  ++p; /*  r  */
                  if (*p == '+')
                        ++p;
                  /* FIXME: all of wx, w+, +x, ++ indicate w|x */
                  if (tolower(*p) == 'w') {
                        ++p;
                        if (*p == '+')
                              ++p;
                        if (tolower(*p) == 'x')
                              printf(" %s", str);
                  }
            }
      }
      fclose(fp);

      return 0;
}

#ifdef __BOUNDS_CHECKING_ON
# define NOTE_TO_SELF warn( \
      "This is bullshit but getpwuid() is leaking memory and I wasted a few hrs 1 day tracking it down in pspax\n" \
      "Later on I forgot I tracked it down before and saw pspax leaking memory so I tracked it down all over again (silly me)\n" \
      "Hopefully the getpwuid()/nis/nss/pam or whatever wont suck later on in the future.")
#else
# define NOTE_TO_SELF
#endif

static struct passwd *get_proc_passwd(pid_t pid)
{
      struct stat st;
      struct passwd *pwd;
      char path[__PAX_UTILS_PATH_MAX];

      snprintf(path, sizeof(path), PROC_DIR "/%u/stat", pid);

      if (stat(path, &st) != -1)
            if ((pwd = getpwuid(st.st_uid)) != NULL)
                  return pwd;

      return NULL;
}

static char *get_proc_status(pid_t pid, const char *name)
{
      FILE *fp;
      size_t len;
      static char str[BUFSIZ];

      if ((fp = proc_fopen(pid, "status")) == NULL)
            return NULL;

      len = strlen(name);
      while (fgets(str, sizeof(str), fp)) {
            if (strncasecmp(str, name, len) != 0)
                  continue;
            if (str[len] == ':') {
                  fclose(fp);
                  str[strlen(str) - 1] = 0;
                  return (str + len + 2);
            }
      }
      fclose(fp);

      return NULL;
}

static char *get_pid_attr(pid_t pid)
{
      FILE *fp;
      char *p;
      static char buf[BUFSIZ];

      if ((fp = proc_fopen(pid, "attr/current")) == NULL)
            return NULL;

      if (fgets(buf, sizeof(buf), fp) != NULL)
            if ((p = strchr(buf, '\n')) != NULL)
                  *p = 0;
      fclose(fp);

      return buf;
}

static char *get_pid_addr(pid_t pid)
{
      FILE *fp;
      char *p;
      static char buf[BUFSIZ];

      if ((fp = proc_fopen(pid, "ipaddr")) == NULL)
            return NULL;

      if (fgets(buf, sizeof(buf), fp) != NULL)
            if ((p = strchr(buf, '\n')) != NULL)
                  *p = 0;
      fclose(fp);

      return buf;
}

static const char *get_proc_type(pid_t pid)
{
      char fname[32];
      elfobj *elf;
      const char *ret;

      snprintf(fname, sizeof(fname), PROC_DIR "/%u/exe", pid);
      if ((elf = readelf(fname)) == NULL)
            return NULL;
      ret = get_elfetype(elf);
      unreadelf(elf);
      return ret;
}

static char *scanelf_file_phdr(elfobj *elf)
{
      static char ret[8];
      unsigned long i, off, multi_stack, multi_load;
      int max_pt_load;

      memcpy(ret, "--- ---\0", 8);

      multi_stack = multi_load = 0;
      max_pt_load = elf_max_pt_load(elf);

      if (elf->phdr) {
      uint32_t flags;
#define SHOW_PHDR(B) \
      if (elf->elf_class == ELFCLASS ## B) { \
      Elf ## B ## _Ehdr *ehdr = EHDR ## B (elf->ehdr); \
      Elf ## B ## _Phdr *phdr = PHDR ## B (elf->phdr); \
      for (i = 0; i < EGET(ehdr->e_phnum); i++) { \
            if (EGET(phdr[i].p_type) == PT_GNU_STACK) { \
                  if (multi_stack++) warnf("%s: multiple PT_GNU_STACK's !?", elf->filename); \
                  off = 0; \
            } else if (EGET(phdr[i].p_type) == PT_LOAD) { \
                  if (multi_load++ > max_pt_load) warnf("%s: more than %i PT_LOAD's !?", elf->filename, max_pt_load); \
                  off = 4; \
            } else \
                  continue; \
            flags = EGET(phdr[i].p_flags); \
            memcpy(ret+off, gnu_short_stack_flags(flags), 3); \
      } \
      }
      SHOW_PHDR(32)
      SHOW_PHDR(64)
      }

      return ret;
}
/* we scan the elf file two times when the -e flag is given. But we don't need -e very often so big deal */
static const char *get_proc_phdr(pid_t pid)
{
      char fname[32];
      elfobj *elf;
      const char *ret;

      snprintf(fname, sizeof(fname), PROC_DIR "/%u/exe", pid);
      if ((elf = readelf(fname)) == NULL)
            return NULL;
      ret = scanelf_file_phdr(elf);
      unreadelf(elf);
      return ret;
}

static void pspax(const char *find_name)
{
      register DIR *dir;
      register struct dirent *de;
      pid_t pid;
      pid_t ppid = show_pid;
      int have_attr, have_addr, wx;
      struct passwd *pwd;
      struct stat st;
      const char *pax, *type, *name, *attr, *addr;
      char *caps;
      WRAP_SYSCAP(ssize_t length; cap_t cap_d;)

      WRAP_SYSCAP(cap_d = cap_init());

      dir = opendir(PROC_DIR);
      if (dir == NULL || chdir(PROC_DIR))
            errp(PROC_DIR);

      if (access("/proc/self/attr/current", R_OK) != -1)
            have_attr = 1;
      else
            have_attr = 0;

      if ((access("/proc/self/ipaddr", R_OK) != -1) && show_addr)
            have_addr = 1;
      else
            have_addr = 0;

      if (show_banner)
            printf("%-8s %-6s %-6s %-4s %-10s %-16s %-4s %-4s %s %s\n",
                   "USER", "PID", "PAX", "MAPS", "ETYPE", "NAME", "CAPS", have_attr ? "ATTR" : "",
                  have_addr ? "IPADDR" : "", show_phdr ? "STACK LOAD" : "");

      while ((de = readdir(dir))) {
            errno = 0;
            stat(de->d_name, &st);
            if ((errno != ENOENT) && (errno != EACCES)) {
                  pid = (pid_t) atoi((char *) basename((char *) de->d_name));
                  if (find_name && pid) {
                        char *str = get_proc_name(pid);
                        if (!str)
                              continue;
                        if (strcmp(str, find_name) != 0)
                              pid = 0;
                  }
                  if (((ppid > 0) && (pid != ppid)) || !pid)
                        continue;

                  wx = get_proc_maps(pid);

                  if (noexec != writeexec) {
                        if ((wx == 1) && (writeexec != wx))
                              goto next_pid;

                        if ((wx == 0) && writeexec)
                              goto next_pid;
                  }

                  pwd  = get_proc_passwd(pid);
                  pax  = get_proc_status(pid, "PAX");
                  type = get_proc_type(pid);
                  name = get_proc_name(pid);
                  attr = (have_attr ? get_pid_attr(pid) : NULL);
                  addr = (have_addr ? get_pid_addr(pid) : NULL);

                  if (show_uid != -1 && pwd)
                        if (pwd->pw_uid != show_uid)
                              continue;

                  if (show_gid != -1 && pwd)
                        if (pwd->pw_gid != show_gid)
                              continue;

                  /* this is a non-POSIX function */
                  caps = NULL;
                  WRAP_SYSCAP(capgetp(pid, cap_d));
                  WRAP_SYSCAP(caps = cap_to_text(cap_d, &length));

                  if (pwd && strlen(pwd->pw_name) >= 8)
                        pwd->pw_name[8] = 0;

                  if (show_all || type) {
                        printf("%-8s %-6d %-6s %-4s %-10s %-16s %-4s %s %s %s\n",
                               pwd  ? pwd->pw_name : "--------",
                               pid,
                               pax  ? pax  : "---",
                               (wx == 1) ? "w|x" : (wx == -1) ? "---" : "w^x",
                               type ? type : "-------",
                               name ? name : "-----",
                               caps ? caps : " = ",
                               attr ? attr : "",
                               addr ? addr : "",
                               show_phdr ? get_proc_phdr(pid) : "");
                        if (verbose && wx)
                              print_executable_mappings(pid);
                  }

                  WRAP_SYSCAP(if (caps) cap_free(caps));

            next_pid:
                  continue;
            }
      }
      closedir(dir);
}

/* usage / invocation handling functions */
#define PARSE_FLAGS "aeip:u:g:nwWvCBhV"
#define a_argument required_argument
static struct option const long_opts[] = {
      {"all",       no_argument, NULL, 'a'},
      {"header",    no_argument, NULL, 'e'},
      {"ipaddr",    no_argument, NULL, 'i'},
      {"pid",        a_argument, NULL, 'p'},
      {"user",       a_argument, NULL, 'u'},
      {"group",      a_argument, NULL, 'g'},
      {"nx",        no_argument, NULL, 'n'},
      {"wx",        no_argument, NULL, 'w'},
      {"wide",      no_argument, NULL, 'W'},
      {"verbose",   no_argument, NULL, 'v'},
      {"nocolor",   no_argument, NULL, 'C'},
      {"nobanner",  no_argument, NULL, 'B'},
      {"help",      no_argument, NULL, 'h'},
      {"version",   no_argument, NULL, 'V'},
      {NULL,        no_argument, NULL, 0x0}
};

static const char * const opts_help[] = {
      "Show all processes",
      "Print GNU_STACK/PT_LOAD markings",
      "Print ipaddr info if supported",
      "Process ID/pid #",
      "Process user/uid #",
      "Process group/gid #",
      "Only display w^x processes",
      "Only display w|x processes",
      "Wide output display of cmdline",
      "Be verbose about executable mappings",
      "Don't emit color in output",
      "Don't display the header",
      "Print this help and exit",
      "Print version and exit",
      NULL
};

/* display usage and exit */
static void usage(int status)
{
      int i;
      printf("* List ELF/PaX information about running processes\n\n"
             "Usage: %s [options]\n\n", argv0);
      fputs("Options:\n", stdout);
      for (i = 0; long_opts[i].name; ++i)
            printf("  -%c, --%-12s* %s\n", long_opts[i].val,
                   long_opts[i].name, opts_help[i]);
#ifdef MANLYPAGE
      for (i = 0; long_opts[i].name; ++i)
            printf(".TP\n\\fB\\-%c, \\-\\-%s\\fR\n%s\n", long_opts[i].val,
                   long_opts[i].name, opts_help[i]);
#endif
      exit(status);
}

/* parse command line arguments and preform needed actions */
static void parseargs(int argc, char *argv[])
{
      int flag;
      struct passwd *pwd = NULL;
      struct  group *gwd = NULL;

      opterr = 0;
      while ((flag=getopt_long(argc, argv, PARSE_FLAGS, long_opts, NULL)) != -1) {
            switch (flag) {

            case 'V':                        /* version info */
                  printf("pax-utils-%s: %s compiled %s\n%s\n"
                         "%s written for Gentoo by <solar and vapier @ gentoo.org>\n",
                         VERSION, __FILE__, __DATE__, rcsid, argv0);
                  exit(EXIT_SUCCESS);
                  break;
            case 'h': usage(EXIT_SUCCESS); break;

            case 'C': color_init(true); break;
            case 'B': show_banner = 0; break;
            case 'a': show_all = 1; break;
            case 'e': show_phdr = 1; break;
            case 'i': show_addr = 1; break;
            case 'p': show_pid = atoi(optarg); break;
            case 'n': noexec = 1; writeexec = 0; break;
            case 'w': noexec = 0; writeexec = 1; break;
            case 'W': wide_output = 1; break;
            case 'v': verbose++; break;
            case 'u':
                  show_uid = atoi(optarg);
                  if (show_uid == 0 && (strcmp(optarg, "0") != 0)) {
                        pwd = getpwnam(optarg);
                        if (pwd)
                              show_uid = pwd->pw_uid;
                        else
                              err("unknown uid");
                  }
                  break;
            case 'g':
                  show_gid = atoi(optarg);
                  if (show_gid == 0 && (strcmp(optarg, "0") != 0)) {
                        gwd = getgrnam(optarg);
                        if (gwd)
                              show_gid = gwd->gr_gid;
                        else
                              err("unknown gid");
                  }
                  break;
            case ':':
            case '?':
                  warn("Unknown option or missing parameter");
                  usage(EXIT_FAILURE);
                  break;
            default:
                  err("Unhandled option '%c'", flag);
                  break;
            }
      }
}

int main(int argc, char *argv[])
{
      char *name = NULL;

      color_init(false);
      parseargs(argc, argv);

      if ((optind < argc) && (show_pid == 0))
            name = argv[optind];

      pspax(name);

      NOTE_TO_SELF;
      return EXIT_SUCCESS;
}

Generated by  Doxygen 1.6.0   Back to index