/******************************************************************************/
/* 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
*/
#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"
static const char *rcsid = "$Id: pspax.c,v 1.37 2006/12/12 23:34:34 solar Exp $";
#define argv0 "pspax"
/* 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 noexec = 1;
static char writeexec = 1;
static pid_t show_pid = 0;
static uid_t show_uid = -1;
static gid_t show_gid = -1;
static char *get_proc_name(pid_t pid)
{
FILE *fp;
static char str[__PAX_UTILS_PATH_MAX];
memset(&str, 0, sizeof(str));
snprintf(str, sizeof(str), PROC_DIR "/%u/stat", pid);
if ((fp = fopen(str, "r")) == NULL)
return NULL;
memset(&str, 0, sizeof(str));
fscanf(fp, "%*d %s.16", str);
if (*str) {
str[strlen(str) - 1] = '\0';
str[16] = 0;
}
fclose(fp);
return (str+1);
}
static int get_proc_maps(pid_t pid)
{
static char str[__PAX_UTILS_PATH_MAX];
FILE *fp;
snprintf(str, sizeof(str), PROC_DIR "/%u/maps", pid);
if ((fp = fopen(str, "r")) == 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)
{
static char str[__PAX_UTILS_PATH_MAX];
FILE *fp;
snprintf(str, sizeof(str), PROC_DIR "/%u/maps", pid);
if ((fp = fopen(str, "r")) == 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;
static char str[__PAX_UTILS_PATH_MAX];
snprintf(str, sizeof(str), PROC_DIR "/%u/stat", pid);
if ((stat(str, &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[__PAX_UTILS_PATH_MAX];
snprintf(str, sizeof(str), PROC_DIR "/%u/status", pid);
if ((fp = fopen(str, "r")) == 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;
char str[32];
static char buf[BUFSIZ];
memset(buf, 0, sizeof(buf));
snprintf(str, sizeof(str), PROC_DIR "/%u/attr/current", pid);
if ((fp = fopen(str, "r")) == 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 = NULL;
char *ret = NULL;
snprintf(fname, sizeof(fname), PROC_DIR "/%u/exe", pid);
if ((elf = readelf(fname)) == NULL)
return ret;
ret = (char *)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 = NULL;
char *ret = NULL;
snprintf(fname, sizeof(fname), PROC_DIR "/%u/exe", pid);
if ((elf = readelf(fname)) == NULL)
return ret;
ret = (char *) 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, wx;
struct passwd *pwd;
struct stat st;
const char *pax, *type, *name, *caps, *attr;
WRAP_SYSCAP(ssize_t length; cap_t cap_d;);
WRAP_SYSCAP(cap_d = cap_init());
caps = NULL;
chdir(PROC_DIR);
if (!(dir = opendir(PROC_DIR))) {
perror(PROC_DIR);
exit(EXIT_FAILURE);
}
if (access("/proc/self/attr/current", R_OK) != (-1))
have_attr = 1;
else
have_attr = 0;
if (show_banner)
printf("%-8s %-6s %-6s %-4s %-10s %-16s %-4s %-4s %s\n",
"USER", "PID", "PAX", "MAPS", "ETYPE", "NAME", "CAPS", "ATTR", 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);
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 */
WRAP_SYSCAP(capgetp(pid, cap_d));
WRAP_SYSCAP(caps = cap_to_text(cap_d, &length));
if (show_all || type) {
printf("%-8s %-6d %-6s %-4s %-10s %-16s %-4s %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 : "-", show_phdr ? get_proc_phdr(pid) : "");
if (verbose && wx)
print_executable_mappings(pid);
}
WRAP_SYSCAP(if (caps) cap_free((void *)caps));
next_pid:
continue;
}
}
closedir(dir);
}
/* usage / invocation handling functions */
#define PARSE_FLAGS "aep:u:g:nwvBhV"
#define a_argument required_argument
static struct option const long_opts[] = {
{"all", no_argument, NULL, 'a'},
{"header", no_argument, NULL, 'e'},
{"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'},
{"verbose", no_argument, NULL, 'v'},
{"nobanner", no_argument, NULL, 'B'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, no_argument, NULL, 0x0}
};
static const char *opts_help[] = {
"Show all processes",
"Print GNU_STACK/PT_LOAD markings",
"Process ID/pid #",
"Process user/uid #",
"Process group/gid #",
"Only display w^x processes",
"Only display w|x processes",
"Be verbose about executable mappings",
"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 'B': show_banner = 0; break;
case 'a': show_all = 1; break;
case 'e': show_phdr = 1; break;
case 'p': show_pid = atoi(optarg); break;
case 'n': noexec = 1; writeexec = 0; break;
case 'w': noexec = 0; writeexec = 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;
parseargs(argc, argv);
if ((optind < argc) && (show_pid == 0))
name = argv[optind];
pspax(name);
NOTE_TO_SELF;
return EXIT_SUCCESS;
}
syntax highlighted by Code2HTML, v. 0.9.1