selinux/policycoreutils/secon/secon.c
Christian Göttsche 9229f8b3b7 policycoreutils: handle argument counter of zero
The number of arguments passed to main(), argc, can be zero if the
pathname passed to execve(2) is NULL, e.g. via:

    execve("/path/to/exe", {NULL}, {NULL});

Also avoid NULL pointer dereferences on the argument value.

Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
2022-02-07 11:14:26 -05:00

767 lines
18 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#define xstreq(x, y) !strcmp(x, y)
#include <err.h>
#include <getopt.h>
#include <sys/types.h>
#include <unistd.h>
#include <selinux/selinux.h>
#include <selinux/context.h>
#define TRUE 1
#define FALSE 0
#define SECON_CONF_PROG_NAME "secon" /* default program name */
#define SECON_OPTS_SM "hVurtscmPRCfLp" /* small options available, print */
#define SECON_OPTS_GO "hVurtlscmPRCf:L:p:" /* small options available, getopt */
#define OPTS_FROM_ARG 0
#define OPTS_FROM_FILE 1
#define OPTS_FROM_LINK 2
#define OPTS_FROM_STDIN 3
#define OPTS_FROM_CUR 4
#define OPTS_FROM_CUREXE 5
#define OPTS_FROM_CURFS 6
#define OPTS_FROM_CURKEY 7
#define OPTS_FROM_PROC 8
#define OPTS_FROM_PROCEXE 9
#define OPTS_FROM_PROCFS 10
#define OPTS_FROM_PROCKEY 11
struct context_color_t {
unsigned int valid;
char *user_fg;
char *user_bg;
char *role_fg;
char *role_bg;
char *type_fg;
char *type_bg;
char *range_fg;
char *range_bg;
};
struct {
unsigned int disp_user:1;
unsigned int disp_role:1;
unsigned int disp_type:1;
unsigned int disp_sen:1;
unsigned int disp_clr:1;
unsigned int disp_mlsr:1;
unsigned int disp_raw:1;
unsigned int disp_color:1;
unsigned int disp_prompt:1; /* no return, use : to sep */
unsigned int from_type:8; /* 16 bits, uses 4 bits */
union {
pid_t pid;
const char *file;
const char *link;
const char *arg;
} f;
} opts[1] = { {
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, OPTS_FROM_ARG, {0} } };
static __attribute__((__noreturn__)) void usage(const char *name, int exit_code)
{
fprintf(exit_code ? stderr : stdout,
" Usage: %s [-%s] [ context | - ]\n"
" --help -h Show this message.\n"
" --version -V Show the version.\n"
" --prompt -P Output in a format good for a prompt.\n"
" --user -u Show the user of the context.\n"
" --role -r Show the role of the context.\n"
" --type -t Show the type of the context.\n"
" --sensitivity -s Show the sensitivity level of the context.\n"
" --clearance -c Show the clearance level of the context.\n"
" --mls-range -m Show the sensitivity to clearance range of \n"
" the context.\n"
" --raw -R Show the context in \"raw\" format.\n"
" --color -C Output using ANSI color codes (requires -P).\n"
" --current, --self Get the context for the current process.\n"
" --current-exec, --self-exec Get the exec context for the current process.\n"
" --current-fs, --self-fs Get the fs context for the current process.\n"
" --current-key, --self-key Get the key context for the current process.\n"
" --parent Get the context for the parent process.\n"
" --parent-exec Get the exec context for the parent process.\n"
" --parent-fs Get the fs context for the parent process.\n"
" --parent-key Get the key context for the parent process.\n"
" --pid -p <arg> Use the context from the specified pid.\n"
" --pid-exec <arg> Use the exec context from the specified pid.\n"
" --pid-fs <arg> Use the fs context from the specified pid.\n"
" --pid-key <arg> Use the key context from the specified pid.\n"
" --file -f <arg> Use the context from the specified file.\n"
" --link -L <arg> Use the context from the specified link.\n",
name, SECON_OPTS_SM);
exit(exit_code);
}
static const char *opt_program_name(const char *argv0, const char *def)
{
if (argv0) {
if ((def = strrchr(argv0, '/')))
++def;
else
def = argv0;
/* hack for libtool */
if ((strlen(def) > strlen("lt-"))
&& !memcmp("lt-", def, strlen("lt-")))
def += 3;
}
return (def);
}
static int disp_num(void)
{
int num = 0;
num += opts->disp_user;
num += opts->disp_role;
num += opts->disp_type;
num += opts->disp_sen;
num += opts->disp_clr;
num += opts->disp_mlsr;
return (num);
}
static int disp_none(void)
{
return (!disp_num());
}
static int disp_multi(void)
{
return (disp_num() > 1);
}
static void cmd_line(int argc, char *argv[])
{
int optchar = 0;
const char *program_name = NULL;
struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"prompt", no_argument, NULL, 'P'},
{"user", no_argument, NULL, 'u'},
{"role", no_argument, NULL, 'r'},
{"type", no_argument, NULL, 't'},
{"level", no_argument, NULL, 'l'}, /* compat. */
{"sensitivity", no_argument, NULL, 's'},
{"range", no_argument, NULL, 'm'},
{"clearance", no_argument, NULL, 'c'},
{"mls-range", no_argument, NULL, 'm'},
{"raw", no_argument, NULL, 'R'},
{"color", no_argument, NULL, 'C'},
{"current", no_argument, NULL, 1},
{"self", no_argument, NULL, 1},
{"current-exec", no_argument, NULL, 2},
{"self-exec", no_argument, NULL, 2},
{"current-fs", no_argument, NULL, 3},
{"self-fs", no_argument, NULL, 3},
{"current-key", no_argument, NULL, 4},
{"self-key", no_argument, NULL, 4},
{"parent", no_argument, NULL, 5},
{"parent-exec", no_argument, NULL, 6},
{"parent-fs", no_argument, NULL, 7},
{"parent-key", no_argument, NULL, 8},
{"file", required_argument, NULL, 'f'},
{"link", required_argument, NULL, 'L'},
{"pid", required_argument, NULL, 'p'},
{"pid-exec", required_argument, NULL, 9},
{"pid-fs", required_argument, NULL, 10},
{"pid-key", required_argument, NULL, 11},
{NULL, 0, NULL, 0}
};
int done = FALSE;
program_name = opt_program_name(argv[0], SECON_CONF_PROG_NAME);
while ((optchar = getopt_long(argc, argv, SECON_OPTS_GO,
long_options, NULL)) != -1) {
switch (optchar) {
case '?':
usage(program_name, EXIT_FAILURE);
case 'h':
usage(program_name, EXIT_SUCCESS);
case 'V':
fprintf(stdout,
" %s version %s.\n", program_name, VERSION);
exit(EXIT_SUCCESS);
case 'u':
done = TRUE;
opts->disp_user = !opts->disp_user;
break;
case 'r':
done = TRUE;
opts->disp_role = !opts->disp_role;
break;
case 't':
done = TRUE;
opts->disp_type = !opts->disp_type;
break;
case 'l':
done = TRUE;
opts->disp_sen = !opts->disp_sen;
break;
case 's':
done = TRUE;
opts->disp_sen = !opts->disp_sen;
break;
case 'c':
done = TRUE;
opts->disp_clr = !opts->disp_clr;
break;
case 'm':
done = TRUE;
opts->disp_mlsr = !opts->disp_mlsr;
break;
case 'P':
opts->disp_prompt = !opts->disp_prompt;
break;
case 'R':
opts->disp_raw = !opts->disp_raw;
break;
case 'C':
opts->disp_color = !opts->disp_color;
break;
case 1:
opts->from_type = OPTS_FROM_CUR;
break;
case 2:
opts->from_type = OPTS_FROM_CUREXE;
break;
case 3:
opts->from_type = OPTS_FROM_CURFS;
break;
case 4:
opts->from_type = OPTS_FROM_CURKEY;
break;
case 5:
opts->from_type = OPTS_FROM_PROC;
opts->f.pid = getppid();
break;
case 6:
opts->from_type = OPTS_FROM_PROCEXE;
opts->f.pid = getppid();
break;
case 7:
opts->from_type = OPTS_FROM_PROCFS;
opts->f.pid = getppid();
break;
case 8:
opts->from_type = OPTS_FROM_PROCKEY;
opts->f.pid = getppid();
break;
case 'f':
opts->from_type = OPTS_FROM_FILE;
opts->f.file = optarg;
break;
case 'L':
opts->from_type = OPTS_FROM_LINK;
opts->f.link = optarg;
break;
case 'p':
opts->from_type = OPTS_FROM_PROC;
opts->f.pid = atoi(optarg);
break;
case 9:
opts->from_type = OPTS_FROM_PROCEXE;
opts->f.pid = atoi(optarg);
break;
case 10:
opts->from_type = OPTS_FROM_PROCFS;
opts->f.pid = atoi(optarg);
break;
case 11:
opts->from_type = OPTS_FROM_PROCKEY;
opts->f.pid = atoi(optarg);
break;
default:
assert(FALSE);
}
}
if (!done) { /* default, if nothing specified */
opts->disp_user = TRUE;
opts->disp_role = TRUE;
opts->disp_type = TRUE;
if (!opts->disp_prompt) { /* when displaying prompt, just output "normal" by default */
opts->disp_sen = TRUE;
opts->disp_clr = TRUE;
}
opts->disp_mlsr = TRUE;
}
if (disp_none())
err(EXIT_FAILURE, " Nothing to display");
argc -= optind;
argv += optind;
if (!argc && (opts->from_type == OPTS_FROM_ARG)
&& !isatty(STDIN_FILENO))
opts->from_type = OPTS_FROM_STDIN;
if (!argc && (opts->from_type == OPTS_FROM_ARG))
opts->from_type = OPTS_FROM_CUR;
if (opts->from_type == OPTS_FROM_ARG) {
if (!argv[0])
errx(EXIT_FAILURE, "No argument given");
opts->f.arg = argv[0];
if (xstreq(argv[0], "-"))
opts->from_type = OPTS_FROM_STDIN;
} else if (!is_selinux_enabled())
errx(EXIT_FAILURE, "SELinux is not enabled");
}
static int my_getXcon_raw(pid_t pid, char **con, const char *val)
{
char buf[4096];
FILE *fp = NULL;
const char *ptr = NULL;
snprintf(buf, sizeof(buf), "%s/%ld/attr/%s", "/proc", (long int)pid,
val);
if (!(fp = fopen(buf, "rb")))
return (-1);
ptr = fgets(buf, sizeof(buf), fp);
fclose(fp);
*con = NULL;
if (ptr) { /* return *con = NULL, when proc file is empty */
char *tmp = strchr(ptr, '\n');
if (tmp)
*tmp = 0;
if (*ptr && !(*con = strdup(ptr)))
return (-1);
}
return (0);
}
static int my_getpidexeccon_raw(pid_t pid, char **con)
{
return (my_getXcon_raw(pid, con, "exec"));
}
static int my_getpidfscreatecon_raw(pid_t pid, char **con)
{
return (my_getXcon_raw(pid, con, "fscreate"));
}
static int my_getpidkeycreatecon_raw(pid_t pid, char **con)
{
return (my_getXcon_raw(pid, con, "keycreate"));
}
static char *get_scon(void)
{
static char dummy_NIL[1] = "";
char *con = NULL, *con_tmp;
int ret = -1;
switch (opts->from_type) {
case OPTS_FROM_ARG:
if (!(con_tmp = strdup(opts->f.arg)))
err(EXIT_FAILURE,
" Couldn't allocate security context");
if (selinux_trans_to_raw_context(con_tmp, &con) < 0)
err(EXIT_FAILURE,
" Couldn't translate security context");
freecon(con_tmp);
break;
case OPTS_FROM_STDIN:
{
char buf[4096] = "";
char *ptr = buf;
while (!*ptr) {
if (!(ptr = fgets(buf, sizeof(buf), stdin)))
err(EXIT_FAILURE,
" Couldn't read security context");
ptr += strspn(ptr, " \n\t");
ptr[strcspn(ptr, " \n\t")] = 0;
}
if (!(con_tmp = strdup(ptr)))
err(EXIT_FAILURE,
" Couldn't allocate security context");
if (selinux_trans_to_raw_context(con_tmp, &con) < 0)
err(EXIT_FAILURE,
" Couldn't translate security context");
freecon(con_tmp);
break;
}
case OPTS_FROM_CUR:
ret = getcon_raw(&con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get current security context");
break;
case OPTS_FROM_CUREXE:
ret = getexeccon_raw(&con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get current exec security context");
if (!con)
con = strdup(dummy_NIL);
break;
case OPTS_FROM_CURFS:
ret = getfscreatecon_raw(&con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get current fs security context");
if (!con)
con = strdup(dummy_NIL);
break;
case OPTS_FROM_CURKEY:
ret = getkeycreatecon_raw(&con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get current key security context");
if (!con)
con = strdup(dummy_NIL);
break;
case OPTS_FROM_PROC:
ret = getpidcon_raw(opts->f.pid, &con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get security context for pid %lu",
(unsigned long)opts->f.pid);
break;
case OPTS_FROM_PROCEXE:
ret = my_getpidexeccon_raw(opts->f.pid, &con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get security context for pid %lu",
(unsigned long)opts->f.pid);
if (!con)
con = strdup(dummy_NIL);
break;
case OPTS_FROM_PROCFS:
ret = my_getpidfscreatecon_raw(opts->f.pid, &con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get security context for pid %lu",
(unsigned long)opts->f.pid);
if (!con)
con = strdup(dummy_NIL);
/* disabled -- override with normal context ...
{
opts->from_type = OPTS_FROM_PROC;
return (get_scon());
} */
break;
case OPTS_FROM_PROCKEY:
ret = my_getpidkeycreatecon_raw(opts->f.pid, &con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get security context for pid %lu",
(unsigned long)opts->f.pid);
if (!con)
con = strdup(dummy_NIL);
break;
case OPTS_FROM_FILE:
ret = getfilecon_raw(opts->f.file, &con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get security context for file %s",
opts->f.file);
break;
case OPTS_FROM_LINK:
ret = lgetfilecon_raw(opts->f.link, &con);
if (ret == -1)
err(EXIT_FAILURE,
" Couldn't get security context for symlink %s",
opts->f.link);
break;
default:
assert(FALSE);
}
return (con);
}
static unsigned int disp__color_to_ansi(const char *color_str)
{
int val = 30;
/* NOTE: ansi black is 30 for foreground colors */
/* red */
if (strncasecmp(&color_str[1], "7f", 2) >= 0)
val += 1;
/* green */
if (strncasecmp(&color_str[3], "7f", 2) >= 0)
val += 2;
/* blue */
if (strncasecmp(&color_str[5], "7f", 2) >= 0)
val += 4;
return val;
}
static char *disp__con_color_ansi(const char *name,
struct context_color_t *color)
{
unsigned int fg, bg;
char *ansi;
int ansi_len = strlen("\e[99;99m") + 1;
/* NOTE: ansi background codes are the same as foreground codes +10 */
if (xstreq("user", name)) {
fg = disp__color_to_ansi(color->user_fg);
bg = disp__color_to_ansi(color->user_bg) + 10;
} else if (xstreq("role", name)) {
fg = disp__color_to_ansi(color->role_fg);
bg = disp__color_to_ansi(color->role_bg) + 10;
} else if (xstreq("type", name)) {
fg = disp__color_to_ansi(color->type_fg);
bg = disp__color_to_ansi(color->type_bg) + 10;
} else if (xstreq("sensitivity", name) ||
xstreq("clearance", name) ||
xstreq("mls-range", name)) {
fg = disp__color_to_ansi(color->range_fg);
bg = disp__color_to_ansi(color->range_bg) + 10;
} else
err(EXIT_FAILURE, " No color information for context field");
if (!(ansi = malloc(ansi_len)))
err(EXIT_FAILURE, " Unable to allocate memory");
if (snprintf(ansi, ansi_len, "\e[%d;%dm", fg, bg) > ansi_len)
err(EXIT_FAILURE, " Unable to convert colors to ANSI codes");
return ansi;
}
static void disp__con_val(const char *name, const char *val,
struct context_color_t *color)
{
static int done = FALSE;
assert(name);
assert(color);
if (!val)
val = ""; /* targeted has no "level" etc.,
any errors should happen at context_new() time */
if (opts->disp_prompt) {
if (xstreq("mls-range", name) && !*val)
return; /* skip, mls-range if it's empty */
if (opts->disp_color && color->valid) {
char *ansi = disp__con_color_ansi(name, color);
fprintf(stdout, "%s", ansi);
free(ansi);
}
fprintf(stdout, "%s%s", done ? ":" : "", val);
if (opts->disp_color && color->valid)
fprintf(stdout, "\e[0m");
} else if (disp_multi())
fprintf(stdout, "%s: %s\n", name, val);
else
fprintf(stdout, "%s\n", val);
done = TRUE;
}
static void disp_con(const char *scon_raw)
{
char *scon_trans;
const char *scon;
context_t con = NULL;
char *color_str = NULL;
struct context_color_t color = { .valid = 0 };
selinux_raw_to_trans_context(scon_raw, &scon_trans);
if (opts->disp_raw)
scon = scon_raw;
else
scon = scon_trans;
if (!*scon) { /* --self-exec and --self-fs etc. */
if (opts->disp_user)
disp__con_val("user", NULL, &color);
if (opts->disp_role)
disp__con_val("role", NULL, &color);
if (opts->disp_type)
disp__con_val("type", NULL, &color);
if (opts->disp_sen)
disp__con_val("sensitivity", NULL, &color);
if (opts->disp_clr)
disp__con_val("clearance", NULL, &color);
if (opts->disp_mlsr)
disp__con_val("mls-range", NULL, &color);
freecon(scon_trans);
return;
}
if (opts->disp_color) {
if (selinux_raw_context_to_color(scon_raw, &color_str) < 0)
errx(EXIT_FAILURE, "Couldn't determine colors for: %s",
scon);
color.user_fg = strtok(color_str, " ");
if (!color.user_fg)
errx(EXIT_FAILURE, "Invalid color string");
color.user_bg = strtok(NULL, " ");
if (!color.user_bg)
errx(EXIT_FAILURE, "Invalid color string");
color.role_fg = strtok(NULL, " ");
if (!color.role_fg)
errx(EXIT_FAILURE, "Invalid color string");
color.role_bg = strtok(NULL, " ");
if (!color.role_bg)
errx(EXIT_FAILURE, "Invalid color string");
color.type_fg = strtok(NULL, " ");
if (!color.type_fg)
errx(EXIT_FAILURE, "Invalid color string");
color.type_bg = strtok(NULL, " ");
if (!color.type_bg)
errx(EXIT_FAILURE, "Invalid color string");
color.range_fg = strtok(NULL, " ");
if (!color.range_fg)
errx(EXIT_FAILURE, "Invalid color string");
color.range_bg = strtok(NULL, " ");
color.valid = 1;
}
if (!(con = context_new(scon)))
errx(EXIT_FAILURE, "Couldn't create context from: %s", scon);
if (opts->disp_user) {
disp__con_val("user", context_user_get(con), &color);
}
if (opts->disp_role) {
disp__con_val("role", context_role_get(con), &color);
}
if (opts->disp_type) {
disp__con_val("type", context_type_get(con), &color);
}
if (opts->disp_sen) {
const char *val = NULL;
char *tmp = NULL;
val = context_range_get(con);
if (!val)
val = ""; /* targeted has no "level" etc.,
any errors should happen at context_new() time */
tmp = strdup(val);
if (!tmp)
errx(EXIT_FAILURE, "Couldn't create context from: %s",
scon);
if (strchr(tmp, '-'))
*strchr(tmp, '-') = 0;
disp__con_val("sensitivity", tmp, &color);
free(tmp);
}
if (opts->disp_clr) {
const char *val = NULL;
char *tmp = NULL;
val = context_range_get(con);
if (!val)
val = ""; /* targeted has no "level" etc.,
any errors should happen at context_new() time */
tmp = strdup(val);
if (!tmp)
errx(EXIT_FAILURE, "Couldn't create context from: %s",
scon);
if (strchr(tmp, '-'))
disp__con_val("clearance", strchr(tmp, '-') + 1, &color);
else
disp__con_val("clearance", tmp, &color);
free(tmp);
}
if (opts->disp_mlsr)
disp__con_val("mls-range", context_range_get(con), &color);
context_free(con);
freecon(scon_trans);
if (color_str)
free(color_str);
}
int main(int argc, char *argv[])
{
char *scon_raw = NULL;
cmd_line(argc, argv);
scon_raw = get_scon();
disp_con(scon_raw);
freecon(scon_raw);
exit(EXIT_SUCCESS);
}