#include #include #include #include #define xstreq(x, y) !strcmp(x, y) #include #include #include #include #include #include #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 Use the context from the specified pid.\n" " --pid-exec Use the exec context from the specified pid.\n" " --pid-fs Use the fs context from the specified pid.\n" " --pid-key Use the key context from the specified pid.\n" " --file -f Use the context from the specified file.\n" " --link -L 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) { /* 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, 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(security_context_t scon_raw) { security_context_t scon_trans, 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); 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[]) { security_context_t scon_raw = NULL; cmd_line(argc, argv); scon_raw = get_scon(); disp_con(scon_raw); freecon(scon_raw); exit(EXIT_SUCCESS); }