Add audit log entry to specify whether the decision was made in permissive mode/permissive domain or enforcing mode. There are two utilities for testing: utils/avc_has_perm - This can set the AVC mode to follow SELinux, set AVC permissive, or set AVC enforcing. utils/selinux_check_access - This follows SELinux as it calls selinux_check_access(3). Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx> --- libselinux/src/avc.c | 4 + libselinux/utils/avc_has_perm.c | 203 ++++++++++++++++++++++++++++++++ libselinux/utils/selinux_check_access.c | 145 +++++++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 libselinux/utils/avc_has_perm.c create mode 100644 libselinux/utils/selinux_check_access.c diff --git a/libselinux/src/avc.c b/libselinux/src/avc.c index b1ec57f..5600f80 100644 --- a/libselinux/src/avc.c +++ b/libselinux/src/avc.c @@ -723,6 +723,10 @@ void avc_audit(security_id_t ssid, security_id_t tsid, log_append(avc_audit_buf, " "); avc_dump_query(ssid, tsid, tclass); + + log_append(avc_audit_buf, " permissive=%u", avc_enforcing && + !(avd->flags & SELINUX_AVD_FLAGS_PERMISSIVE) ? 0 : 1); + log_append(avc_audit_buf, "\n"); avc_log(SELINUX_AVC, "%s", avc_audit_buf); diff --git a/libselinux/utils/avc_has_perm.c b/libselinux/utils/avc_has_perm.c new file mode 100644 index 0000000..3d4bfc0 --- /dev/null +++ b/libselinux/utils/avc_has_perm.c @@ -0,0 +1,203 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdbool.h> +#include <selinux/selinux.h> +#include <selinux/avc.h> + +static void usage(char *progname) +{ + fprintf(stderr, "usage: %s [-f | -p] [-i] scon tcon class perm\n" + "\nWhere:\n\t" + "-f Follow SELinux permissive or enforcing mode, or\n\t" + "-p Set avc_open to permissive mode.\n\t" + " The default is to set avc_open to enforcing mode.\n\t" + "-i Interactive mode. Once displayed first result, can\n\t" + " enter additional entries and display AVC cache info.\n", + progname); + exit(1); +} + +static void get_entry(char **buffer) +{ + char *buf; + int len; +#define BUF_LEN 81 + + buf = malloc(BUF_LEN * sizeof(char)); + if (!buf) { + perror("malloc"); + exit(1); + } + + if (fgets(buf, BUF_LEN - 1, stdin) == NULL) { + perror("fgets"); + exit(1); + } + + len = strlen(buf); + if (buf[len - 1] == '\n') + buf[len - 1] = 0; + + *buffer = buf; +} + +/* + * Function to print the AVC statistics. Because no audit logging call back + * has been set, the avc_cache_stats will be displayed on stderr. + */ +static void print_avc_stats(void) +{ + struct avc_cache_stats acs; + + avc_cache_stats(&acs); + printf("\nThe avc_cache_stats are as follows:\n"); + printf("entry_hits: %d\t(Decisions found in aeref)\n", + acs.entry_hits); + printf("entry_misses: %d\t(Decisions not found in aeref)\n", + acs.entry_misses); + printf("entry_discards: %d\t(Decisions not found in aeref that were " + "also non-NULL)\n", acs.entry_discards); + printf("entry_lookups: %d\t(Queries made)\n", acs.entry_lookups); + printf("cav_lookups: %d\t(Cache lookups)\n", acs.cav_lookups); + printf("cav_hits: %d\t(Cache hits)\n", acs.cav_hits); + printf("cav_probes: %d\t(Entries examined searching the cache)\n", + acs.cav_probes); + printf("cav_misses: %d\t(Cache misses)\n\n", acs.cav_misses); +} + +struct avc_entry_ref aeref; +static void exec_func(char *scon, char *tcon, char *class, char *perm) +{ + int rc; + security_id_t scon_id; + security_id_t tcon_id; + security_class_t sclass; + access_vector_t av; + + rc = avc_context_to_sid(scon, &scon_id); + if (rc < 0) { + perror("Error scon avc_context_to_sid"); + exit(1); + } + + rc = avc_context_to_sid(tcon, &tcon_id); + if (rc < 0) { + perror("Error tcon avc_context_to_sid"); + exit(1); + } + + sclass = string_to_security_class(class); + av = string_to_av_perm(sclass, perm); + + printf("\nAny avc_log error messages are shown on stderr:\n"); + rc = avc_has_perm(scon_id, tcon_id, sclass, av, &aeref, NULL); + printf("\nEnd of avc_log error messages.\n\n"); + + if (rc < 0) + printf("Error avc_has_perm: %s\n", strerror(errno)); + else + printf("Permission ALLOWED.\n"); +} + +int main(int argc, char **argv) +{ + int opt, rc; + bool interactive = false, follow = false; + char *scon, *tcon, *class, *perm; + struct selinux_opt avc_option; + + avc_option.type = AVC_OPT_SETENFORCE; + avc_option.value = (char *)1; + + while ((opt = getopt(argc, argv, "ifp")) != -1) { + switch (opt) { + case 'i': + interactive = true; + break; + case 'f': + follow = true; + break; + case 'p': + avc_option.value = NULL; + break; + default: + usage(argv[0]); + } + } + + if ((argc - optind) != 4) + usage(argv[0]); + + rc = is_selinux_enabled(); + if (rc == 0) { + printf("SELinux is not enabled.\n"); + exit(1); + } else if (rc == 1) { + printf("SELinux is enabled.\n"); + } else { + perror("Error is_selinux_enabled"); + exit(1); + } + + rc = security_getenforce(); + if (rc == 0) + printf("SELinux running in PERMISSIVE mode.\n"); + else if (rc == 1) + printf("SELinux running in ENFORCING mode.\n"); + else { + perror("Error security_getenforce"); + exit(1); + } + + rc = security_deny_unknown(); + if (rc == 0) + printf("Undefined object classes or permissions: ALLOWED.\n"); + else if (rc == 1) + printf("Undefined object classes or permissions: DENIED.\n"); + else { + perror("Error security_deny_unknown"); + exit(1); + } + + if (follow) { + if (avc_open(NULL, 0)) { + perror("Error avc_open"); + exit(1); + } + printf("avc_open - Following SELinux mode.\n"); + } else { + if (avc_open(&avc_option, 1)) { + perror("Error avc_open"); + exit(1); + } + + if (avc_option.value == NULL) + printf("avc_open - PERMISSIVE mode.\n"); + else + printf("avc_open - ENFORCING mode.\n"); + } + + avc_entry_ref_init(&aeref); + + exec_func(argv[optind], argv[optind + 1], argv[optind + 2], + argv[optind + 3]); + + while (interactive) { + printf("\nEnter scon: "); + get_entry(&scon); + printf("Enter tcon: "); + get_entry(&tcon); + printf("Enter class: "); + get_entry(&class); + printf("Enter perm: "); + get_entry(&perm); + + exec_func(scon, tcon, class, perm); + print_avc_stats(); + } + + exit(0); +} diff --git a/libselinux/utils/selinux_check_access.c b/libselinux/utils/selinux_check_access.c new file mode 100644 index 0000000..8e354c1 --- /dev/null +++ b/libselinux/utils/selinux_check_access.c @@ -0,0 +1,145 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdbool.h> +#include <selinux/selinux.h> +#include <selinux/avc.h> + +static void usage(char *progname) +{ + fprintf(stderr, "usage: %s [-i] scon tcon class perm\n" + "\nWhere:\n\t" + "-i Interactive mode. Once displayed first result, can\n\t" + " enter additional entries and display AVC cache info.\n", + progname); + exit(1); +} + +static void get_entry(char **buffer) +{ + char *buf; + int len; +#define BUF_LEN 81 + + buf = malloc(BUF_LEN * sizeof(char)); + if (!buf) { + perror("malloc"); + exit(1); + } + + if (fgets(buf, BUF_LEN - 1, stdin) == NULL) { + perror("fgets"); + exit(1); + } + + len = strlen(buf); + if (buf[len - 1] == '\n') + buf[len - 1] = 0; + + *buffer = buf; +} + +/* + * Function to print the AVC statistics. Because no audit logging call back + * has been set, the avc_cache_stats will be displayed on stderr. + * selinux_check_access* sets aeref = NULL, so do not print these stats. + */ +static void print_avc_stats(void) +{ + struct avc_cache_stats acs; + + avc_cache_stats(&acs); + printf("\nThe avc_cache_stats are as follows:\n"); + printf("entry_lookups: %d\t(Queries made)\n", acs.entry_lookups); + printf("cav_lookups: %d\t(Cache lookups)\n", acs.cav_lookups); + printf("cav_hits: %d\t(Cache hits)\n", acs.cav_hits); + printf("cav_probes: %d\t(Entries examined searching the cache)\n", + acs.cav_probes); + printf("cav_misses: %d\t(Cache misses)\n\n", acs.cav_misses); +} + +static void exec_func(char *scon, char *tcon, char *class, char *perm) +{ + int rc; + + printf("\nAny avc_log error messages are shown on stderr:\n"); + rc = selinux_check_access(scon, tcon, class, perm, NULL); + printf("\nEnd of avc_log error messages.\n\n"); + + if (rc < 0) + printf("Error selinux_check_access: %s\n", strerror(errno)); + else + printf("Permission ALLOWED.\n"); +} + +int main(int argc, char **argv) +{ + int opt, rc; + bool interactive = false; + char *scon, *tcon, *class, *perm; + + while ((opt = getopt(argc, argv, "i")) != -1) { + switch (opt) { + case 'i': + interactive = true; + break; + default: + usage(argv[0]); + } + } + + if ((argc - optind) != 4) + usage(argv[0]); + + rc = is_selinux_enabled(); + if (rc == 0) { + printf("SELinux is not enabled.\n"); + exit(1); + } else if (rc == 1) { + printf("SELinux is enabled.\n"); + } else { + perror("Error is_selinux_enabled"); + exit(1); + } + + rc = security_getenforce(); + if (rc == 0) + printf("SELinux running in PERMISSIVE mode.\n"); + else if (rc == 1) + printf("SELinux running in ENFORCING mode.\n"); + else { + perror("Error security_getenforce"); + exit(1); + } + + rc = security_deny_unknown(); + if (rc == 0) + printf("Undefined object classes or permissions: ALLOWED.\n"); + else if (rc == 1) + printf("Undefined object classes or permissions: DENIED.\n"); + else { + perror("Error security_deny_unknown"); + exit(1); + } + + exec_func(argv[optind], argv[optind + 1], argv[optind + 2], + argv[optind + 3]); + + while (interactive) { + printf("\nEnter scon: "); + get_entry(&scon); + printf("Enter tcon: "); + get_entry(&tcon); + printf("Enter class: "); + get_entry(&class); + printf("Enter perm: "); + get_entry(&perm); + + exec_func(scon, tcon, class, perm); + print_avc_stats(); + } + + exit(0); +} -- 2.9.3