642 lines
15 KiB
C
642 lines
15 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 "hVurtscmPRfLp" /* small options available, print */
|
|
#define SECON_OPTS_GO "hVurtlscmPRf: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 {
|
|
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_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, OPTS_FROM_ARG, {
|
|
0}}};
|
|
|
|
static 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"
|
|
" --current Get the context for the current process.\n"
|
|
" --self Get the context for the current process.\n"
|
|
" --self-exec Get the exec context for the current process.\n"
|
|
" --self-fs Get the fs context for the current process.\n"
|
|
" --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'},
|
|
|
|
{"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 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) { /* defualt, 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) {
|
|
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, security_context_t * 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, security_context_t * con)
|
|
{
|
|
return (my_getXcon_raw(pid, con, "exec"));
|
|
}
|
|
static int my_getpidfscreatecon_raw(pid_t pid, security_context_t * con)
|
|
{
|
|
return (my_getXcon_raw(pid, con, "fscreate"));
|
|
}
|
|
static int my_getpidkeycreatecon_raw(pid_t pid, security_context_t * con)
|
|
{
|
|
return (my_getXcon_raw(pid, con, "keycreate"));
|
|
}
|
|
|
|
static security_context_t get_scon(void)
|
|
{
|
|
static char dummy_NIL[1] = "";
|
|
security_context_t con = NULL;
|
|
int ret = -1;
|
|
int raw = TRUE;
|
|
|
|
switch (opts->from_type) {
|
|
case OPTS_FROM_ARG:
|
|
if (!(con = strdup(opts->f.arg)))
|
|
err(EXIT_FAILURE,
|
|
" Couldn't allocate security context");
|
|
raw = !opts->disp_raw; /* always do conversion */
|
|
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 = strdup(ptr)))
|
|
err(EXIT_FAILURE,
|
|
" Couldn't allocate security context");
|
|
|
|
raw = !opts->disp_raw; /* always do conversion */
|
|
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);
|
|
}
|
|
|
|
if (opts->disp_raw != raw) {
|
|
security_context_t ncon = NULL;
|
|
|
|
if (opts->disp_raw)
|
|
selinux_trans_to_raw_context(con, &ncon);
|
|
else
|
|
selinux_raw_to_trans_context(con, &ncon);
|
|
|
|
freecon(con);
|
|
con = ncon;
|
|
}
|
|
|
|
return (con);
|
|
}
|
|
|
|
static void disp__con_val(const char *name, const char *val)
|
|
{
|
|
static int done = FALSE;
|
|
|
|
assert(name);
|
|
|
|
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 */
|
|
|
|
fprintf(stdout, "%s%s", done ? ":" : "", val);
|
|
} else if (disp_multi())
|
|
fprintf(stdout, "%s: %s\n", name, val);
|
|
else
|
|
fprintf(stdout, "%s\n", val);
|
|
|
|
done = TRUE;
|
|
}
|
|
|
|
static void disp_con(security_context_t scon)
|
|
{
|
|
context_t con = NULL;
|
|
|
|
if (!*scon) { /* --self-exec and --self-fs etc. */
|
|
if (opts->disp_user)
|
|
disp__con_val("user", NULL);
|
|
if (opts->disp_role)
|
|
disp__con_val("role", NULL);
|
|
if (opts->disp_type)
|
|
disp__con_val("type", NULL);
|
|
if (opts->disp_sen)
|
|
disp__con_val("sensitivity", NULL);
|
|
if (opts->disp_clr)
|
|
disp__con_val("clearance", NULL);
|
|
if (opts->disp_mlsr)
|
|
disp__con_val("mls-range", NULL);
|
|
return;
|
|
}
|
|
|
|
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));
|
|
if (opts->disp_role)
|
|
disp__con_val("role", context_role_get(con));
|
|
if (opts->disp_type)
|
|
disp__con_val("type", context_type_get(con));
|
|
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);
|
|
|
|
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);
|
|
else
|
|
disp__con_val("clearance", tmp);
|
|
|
|
free(tmp);
|
|
}
|
|
|
|
if (opts->disp_mlsr)
|
|
disp__con_val("mls-range", context_range_get(con));
|
|
|
|
context_free(con);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
security_context_t scon = NULL;
|
|
|
|
cmd_line(argc, argv);
|
|
|
|
scon = get_scon();
|
|
|
|
disp_con(scon);
|
|
|
|
freecon(scon);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|