mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 10:07:12 +00:00
Merge Port aa-status from python to C
This allows aa-status to be used without a python runtime to support things like https://bugs.launchpad.net/bugs/1865519 Fixes: https://bugs.launchpad.net/bugs/1865519 PR: https://gitlab.com/apparmor/apparmor/-/merge_requests/473 Acked-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
parent
e9c2a6087d
commit
8f9046b1b1
@ -19,8 +19,9 @@ include $(COMMONDIR)/Make.rules
|
|||||||
|
|
||||||
DESTDIR=/
|
DESTDIR=/
|
||||||
BINDIR=${DESTDIR}/usr/bin
|
BINDIR=${DESTDIR}/usr/bin
|
||||||
|
SBINDIR=${DESTDIR}/usr/sbin
|
||||||
LOCALEDIR=/usr/share/locale
|
LOCALEDIR=/usr/share/locale
|
||||||
MANPAGES=aa-enabled.1 aa-exec.1
|
MANPAGES=aa-enabled.1 aa-exec.1 aa-status.8
|
||||||
|
|
||||||
WARNINGS = -Wall
|
WARNINGS = -Wall
|
||||||
EXTRA_WARNINGS = -Wsign-compare -Wmissing-field-initializers -Wformat-security -Wunused-parameter
|
EXTRA_WARNINGS = -Wsign-compare -Wmissing-field-initializers -Wformat-security -Wunused-parameter
|
||||||
@ -50,7 +51,8 @@ EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"
|
|||||||
|
|
||||||
SRCS = aa_enabled.c
|
SRCS = aa_enabled.c
|
||||||
HDRS =
|
HDRS =
|
||||||
TOOLS = aa-enabled aa-exec
|
BINTOOLS = aa-enabled aa-exec
|
||||||
|
SBINTOOLS = aa-status
|
||||||
|
|
||||||
AALIB = -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread
|
AALIB = -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread
|
||||||
|
|
||||||
@ -93,7 +95,7 @@ po/%.pot: %.c
|
|||||||
|
|
||||||
# targets arranged this way so that people who don't want full docs can
|
# targets arranged this way so that people who don't want full docs can
|
||||||
# pick specific targets they want.
|
# pick specific targets they want.
|
||||||
arch: $(TOOLS)
|
arch: $(BINTOOLS) $(SBINTOOLS)
|
||||||
|
|
||||||
manpages: $(MANPAGES)
|
manpages: $(MANPAGES)
|
||||||
|
|
||||||
@ -106,7 +108,7 @@ all: arch indep
|
|||||||
|
|
||||||
.PHONY: coverage
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
$(MAKE) clean $(TOOLS) COVERAGE=1
|
$(MAKE) clean $(BINTOOLS) $(SBINTOOLS) COVERAGE=1
|
||||||
|
|
||||||
ifndef USE_SYSTEM
|
ifndef USE_SYSTEM
|
||||||
$(LIBAPPARMOR_A):
|
$(LIBAPPARMOR_A):
|
||||||
@ -124,12 +126,15 @@ aa-enabled: aa_enabled.c $(LIBAPPARMOR_A)
|
|||||||
aa-exec: aa_exec.c $(LIBAPPARMOR_A)
|
aa-exec: aa_exec.c $(LIBAPPARMOR_A)
|
||||||
$(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB)
|
$(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB)
|
||||||
|
|
||||||
|
aa-status: aa_status.c $(LIBAPPARMOR_A)
|
||||||
|
$(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LIBS) $(AALIB)
|
||||||
|
|
||||||
.SILENT: check
|
.SILENT: check
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: check_pod_files tests
|
check: check_pod_files tests
|
||||||
|
|
||||||
.SILENT: tests
|
.SILENT: tests
|
||||||
tests: $(TOOLS) $(TESTS)
|
tests: $(BINTOOLS) $(SBINTOOLS) $(TESTS)
|
||||||
echo "no tests atm"
|
echo "no tests atm"
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
@ -138,7 +143,11 @@ install: install-indep install-arch
|
|||||||
.PHONY: install-arch
|
.PHONY: install-arch
|
||||||
install-arch: arch
|
install-arch: arch
|
||||||
install -m 755 -d ${BINDIR}
|
install -m 755 -d ${BINDIR}
|
||||||
install -m 755 ${TOOLS} ${BINDIR}
|
install -m 755 ${BINTOOLS} ${BINDIR}
|
||||||
|
install -m 755 -d ${SBINDIR}
|
||||||
|
ln -sf aa-status ${SBINDIR}/apparmor_status
|
||||||
|
install -m 755 ${SBINTOOLS} ${SBINDIR}
|
||||||
|
ln -sf aa-status.8 ${DESTDIR}/${MANDIR}/man8/apparmor_status.8
|
||||||
|
|
||||||
.PHONY: install-indep
|
.PHONY: install-indep
|
||||||
install-indep: indep
|
install-indep: indep
|
||||||
|
@ -119,6 +119,10 @@ if the apparmor control files aren't available under /sys/kernel/security/.
|
|||||||
if the user running the script doesn't have enough privileges to read
|
if the user running the script doesn't have enough privileges to read
|
||||||
the apparmor control files.
|
the apparmor control files.
|
||||||
|
|
||||||
|
=item B<42>
|
||||||
|
|
||||||
|
if an internal error occurred.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
=head1 BUGS
|
=head1 BUGS
|
613
binutils/aa_status.c
Normal file
613
binutils/aa_status.c
Normal file
@ -0,0 +1,613 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Canonical Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE /* for asprintf() */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include <sys/apparmor.h>
|
||||||
|
#include <sys/apparmor_private.h>
|
||||||
|
|
||||||
|
#define autofree __attribute((cleanup(_aa_autofree)))
|
||||||
|
#define autofclose __attribute((cleanup(_aa_autofclose)))
|
||||||
|
|
||||||
|
#define AA_EXIT_ENABLED 0
|
||||||
|
#define AA_EXIT_DISABLED 1
|
||||||
|
#define AA_EXIT_NO_POLICY 2
|
||||||
|
#define AA_EXIT_NO_CONTROL 3
|
||||||
|
#define AA_EXIT_NO_PERM 4
|
||||||
|
#define AA_EXIT_INTERNAL_ERROR 42
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||||
|
|
||||||
|
struct profile {
|
||||||
|
char *name;
|
||||||
|
char *status;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void free_profiles(struct profile *profiles, size_t n) {
|
||||||
|
while (n > 0) {
|
||||||
|
n--;
|
||||||
|
free(profiles[n].name);
|
||||||
|
free(profiles[n].status);
|
||||||
|
}
|
||||||
|
free(profiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct process {
|
||||||
|
char *pid;
|
||||||
|
char *profile;
|
||||||
|
char *exe;
|
||||||
|
char *mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void free_processes(struct process *processes, size_t n) {
|
||||||
|
while (n > 0) {
|
||||||
|
n--;
|
||||||
|
free(processes[n].pid);
|
||||||
|
free(processes[n].profile);
|
||||||
|
free(processes[n].exe);
|
||||||
|
free(processes[n].mode);
|
||||||
|
}
|
||||||
|
free(processes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verbose = 0;
|
||||||
|
|
||||||
|
#define dprintf(...) \
|
||||||
|
do { \
|
||||||
|
if (verbose) \
|
||||||
|
printf(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define dfprintf(...) \
|
||||||
|
do { \
|
||||||
|
if (verbose) \
|
||||||
|
fprintf(__VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
static int get_profiles(struct profile **profiles, size_t *n) {
|
||||||
|
autofree char *apparmorfs = NULL;
|
||||||
|
autofree char *apparmor_profiles = NULL;
|
||||||
|
struct stat st;
|
||||||
|
autofclose FILE *fp = NULL;
|
||||||
|
regex_t regex;
|
||||||
|
autofree char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
*profiles = NULL;
|
||||||
|
*n = 0;
|
||||||
|
|
||||||
|
ret = stat("/sys/module/apparmor", &st);
|
||||||
|
if (ret != 0) {
|
||||||
|
dfprintf(stderr, "apparmor not present.\n");
|
||||||
|
ret = AA_EXIT_DISABLED;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
dprintf("apparmor module is loaded.\n");
|
||||||
|
|
||||||
|
ret = aa_find_mountpoint(&apparmorfs);
|
||||||
|
if (ret == -1) {
|
||||||
|
dfprintf(stderr, "apparmor filesystem is not mounted.\n");
|
||||||
|
ret = AA_EXIT_NO_CONTROL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
apparmor_profiles = malloc(strlen(apparmorfs) + 10); // /profiles\0
|
||||||
|
if (apparmor_profiles == NULL) {
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
sprintf(apparmor_profiles, "%s/profiles", apparmorfs);
|
||||||
|
|
||||||
|
fp = fopen(apparmor_profiles, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
if (errno == EACCES) {
|
||||||
|
dfprintf(stderr, "You do not have enough privilege to read the profile set.\n");
|
||||||
|
} else {
|
||||||
|
dfprintf(stderr, "Could not open %s: %s", apparmor_profiles, strerror(errno));
|
||||||
|
}
|
||||||
|
ret = AA_EXIT_NO_PERM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regcomp(®ex, "^(.+)\\s+\\((.+)\\).*", REG_EXTENDED | REG_NEWLINE);
|
||||||
|
if (ret != 0) {
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (getline(&line, &len, fp) != -1) {
|
||||||
|
regmatch_t match[3];
|
||||||
|
|
||||||
|
ret = regexec(®ex, line, 3, match, 0);
|
||||||
|
if (ret == 0) {
|
||||||
|
size_t i;
|
||||||
|
struct profile *_profiles;
|
||||||
|
autofree char *name = strndup(line + match[1].rm_so,
|
||||||
|
match[1].rm_eo - match[1].rm_so);
|
||||||
|
autofree char *status = strndup(line + match[2].rm_so,
|
||||||
|
match[2].rm_eo - match[2].rm_so);
|
||||||
|
|
||||||
|
// give up if out of memory
|
||||||
|
if (name == NULL || status == NULL) {
|
||||||
|
free_profiles(*profiles, *n);
|
||||||
|
*profiles = NULL;
|
||||||
|
*n = 0;
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_profiles = realloc(*profiles, (*n + 1) * sizeof(**profiles));
|
||||||
|
if (_profiles == NULL) {
|
||||||
|
free_profiles(*profiles, *n);
|
||||||
|
*profiles = NULL;
|
||||||
|
*n = 0;
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// steal name and status
|
||||||
|
_profiles[*n].name = name;
|
||||||
|
_profiles[*n].status = status;
|
||||||
|
name = NULL;
|
||||||
|
status = NULL;
|
||||||
|
*n = *n + 1;
|
||||||
|
*profiles = _profiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
regfree(®ex);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return ret == 0 ? (*n > 0 ? AA_EXIT_ENABLED : AA_EXIT_NO_POLICY) : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_profiles(const void *a, const void *b) {
|
||||||
|
return strcmp(((struct profile *)a)->name,
|
||||||
|
((struct profile *)b)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int filter_profiles(struct profile *profiles,
|
||||||
|
size_t n,
|
||||||
|
const char *filter,
|
||||||
|
struct profile **filtered,
|
||||||
|
size_t *nfiltered)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
*filtered = NULL;
|
||||||
|
*nfiltered = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (filter == NULL || strcmp(profiles[i].status, filter) == 0) {
|
||||||
|
struct profile *_filtered = realloc(*filtered, (*nfiltered + 1) * sizeof(**filtered));
|
||||||
|
if (_filtered == NULL) {
|
||||||
|
free_profiles(*filtered, *nfiltered);
|
||||||
|
*filtered = NULL;
|
||||||
|
*nfiltered = 0;
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_filtered[*nfiltered].name = strdup(profiles[i].name);
|
||||||
|
_filtered[*nfiltered].status = strdup(profiles[i].status);
|
||||||
|
*filtered = _filtered;
|
||||||
|
*nfiltered = *nfiltered + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*nfiltered != 0) {
|
||||||
|
qsort(*filtered, *nfiltered, sizeof(*profiles), compare_profiles);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_processes(struct profile *profiles,
|
||||||
|
size_t n,
|
||||||
|
struct process **processes,
|
||||||
|
size_t *nprocesses)
|
||||||
|
{
|
||||||
|
DIR *dir = NULL;
|
||||||
|
struct dirent *entry = NULL;
|
||||||
|
regex_t regex;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
*processes = NULL;
|
||||||
|
*nprocesses = 0;
|
||||||
|
|
||||||
|
ret = regcomp(®ex, "^(.*)\\s+\\((.*)\\)\n$", REG_EXTENDED | REG_NEWLINE);
|
||||||
|
if (ret != 0) {
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
dir = opendir("/proc");
|
||||||
|
if (dir == NULL) {
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
goto free_regex;
|
||||||
|
}
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
int i;
|
||||||
|
int ispid = 1;
|
||||||
|
autofree char *current = NULL;
|
||||||
|
autofree char *exe = NULL;
|
||||||
|
autofree char *real_exe = NULL;
|
||||||
|
autofclose FILE *fp = NULL;
|
||||||
|
autofree char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
// ignore non-pid entries
|
||||||
|
for (i = 0; ispid && i < strlen(entry->d_name); i++) {
|
||||||
|
ispid = (isdigit(entry->d_name[i]) ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (!ispid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (asprintf(¤t, "/proc/%s/attr/current", entry->d_name) == -1 ||
|
||||||
|
asprintf(&exe, "/proc/%s/exe", entry->d_name) == -1) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to allocate memory\n");
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
goto free_regex;
|
||||||
|
}
|
||||||
|
// get executable - readpath can allocate for us but seems
|
||||||
|
// to fail in some cases with errno 2 - no such file or
|
||||||
|
// directory - whereas readlink() can succeed in these
|
||||||
|
// cases - and readpath() seems to have the same behaviour
|
||||||
|
// as in python with better canonicalized results so try it
|
||||||
|
// first and fallack to readlink if it fails
|
||||||
|
// coverity[toctou]
|
||||||
|
real_exe = realpath(exe, NULL);
|
||||||
|
if (real_exe == NULL) {
|
||||||
|
int res;
|
||||||
|
// ensure enough space for NUL terminator
|
||||||
|
real_exe = calloc(PATH_MAX + 1, sizeof(char));
|
||||||
|
if (real_exe == NULL) {
|
||||||
|
fprintf(stderr, "ERROR: Failed to allocate memory\n");
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
goto free_regex;
|
||||||
|
}
|
||||||
|
res = readlink(exe, real_exe, PATH_MAX);
|
||||||
|
if (res == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
real_exe[res] = '\0';
|
||||||
|
}
|
||||||
|
// see if has a label
|
||||||
|
fp = fopen(current, "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
while (getline(&line, &len, fp) != -1) {
|
||||||
|
autofree char *profile = NULL;
|
||||||
|
autofree char *mode = NULL;
|
||||||
|
regmatch_t match[3];
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = regexec(®ex, line, 3, match, 0);
|
||||||
|
if (res == 0) {
|
||||||
|
profile = strndup(line + match[1].rm_so,
|
||||||
|
match[1].rm_eo - match[1].rm_so);
|
||||||
|
mode = strndup(line + match[2].rm_so,
|
||||||
|
match[2].rm_eo - match[2].rm_so);
|
||||||
|
} else {
|
||||||
|
// is unconfined so keep only if this has a
|
||||||
|
// matching profile
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (strcmp(profiles[i].name, real_exe) == 0) {
|
||||||
|
profile = strdup(real_exe);
|
||||||
|
mode = strdup("unconfined");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (profile != NULL && mode != NULL) {
|
||||||
|
struct process *_processes = realloc(*processes,
|
||||||
|
(*nprocesses + 1) * sizeof(**processes));
|
||||||
|
if (_processes == NULL) {
|
||||||
|
free_processes(*processes, *nprocesses);
|
||||||
|
*processes = NULL;
|
||||||
|
*nprocesses = 0;
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
goto free_regex;
|
||||||
|
}
|
||||||
|
_processes[*nprocesses].pid = strdup(entry->d_name);
|
||||||
|
_processes[*nprocesses].profile = profile;
|
||||||
|
_processes[*nprocesses].exe = strdup(real_exe);
|
||||||
|
_processes[*nprocesses].mode = mode;
|
||||||
|
*processes = _processes;
|
||||||
|
*nprocesses = *nprocesses + 1;
|
||||||
|
profile = NULL;
|
||||||
|
mode = NULL;
|
||||||
|
ret = AA_EXIT_ENABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_regex:
|
||||||
|
regfree(®ex);
|
||||||
|
exit:
|
||||||
|
if (dir != NULL) {
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int filter_processes(struct process *processes,
|
||||||
|
size_t n,
|
||||||
|
const char *filter,
|
||||||
|
struct process **filtered,
|
||||||
|
size_t *nfiltered)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
*filtered = NULL;
|
||||||
|
*nfiltered = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (filter == NULL || strcmp(processes[i].mode, filter) == 0) {
|
||||||
|
struct process *_filtered = realloc(*filtered, (*nfiltered + 1) * sizeof(**filtered));
|
||||||
|
if (_filtered == NULL) {
|
||||||
|
free_processes(*filtered, *nfiltered);
|
||||||
|
*filtered = NULL;
|
||||||
|
*nfiltered = 0;
|
||||||
|
ret = AA_EXIT_INTERNAL_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_filtered[*nfiltered].pid = strdup(processes[i].pid);
|
||||||
|
_filtered[*nfiltered].profile = strdup(processes[i].profile);
|
||||||
|
_filtered[*nfiltered].exe = strdup(processes[i].exe);
|
||||||
|
_filtered[*nfiltered].mode = strdup(processes[i].mode);
|
||||||
|
*filtered = _filtered;
|
||||||
|
*nfiltered = *nfiltered + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns error code if AppArmor is not enabled
|
||||||
|
*/
|
||||||
|
static int simple_filtered_count(const char *filter) {
|
||||||
|
size_t n;
|
||||||
|
struct profile *profiles;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = get_profiles(&profiles, &n);
|
||||||
|
if (ret == 0) {
|
||||||
|
size_t nfiltered;
|
||||||
|
struct profile *filtered = NULL;
|
||||||
|
ret = filter_profiles(profiles, n, filter, &filtered, &nfiltered);
|
||||||
|
printf("%zd\n", nfiltered);
|
||||||
|
free_profiles(filtered, nfiltered);
|
||||||
|
}
|
||||||
|
free_profiles(profiles, n);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_enabled(const char *command) {
|
||||||
|
int res = aa_is_enabled();
|
||||||
|
return res == 1 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int cmd_profiled(const char *command) {
|
||||||
|
return simple_filtered_count(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_enforced(const char *command) {
|
||||||
|
return simple_filtered_count("enforce");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_complaining(const char *command) {
|
||||||
|
return simple_filtered_count("complain");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_processes_by_profile(const void *a, const void *b) {
|
||||||
|
return strcmp(((struct process *)a)->profile,
|
||||||
|
((struct process *)b)->profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_processes_by_executable(const void *a, const void *b) {
|
||||||
|
return strcmp(((struct process *)a)->exe,
|
||||||
|
((struct process *)b)->exe);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int detailed_output(int json) {
|
||||||
|
size_t nprofiles = 0, nprocesses = 0;
|
||||||
|
struct profile *profiles = NULL;
|
||||||
|
struct process *processes = NULL;
|
||||||
|
const char *profile_statuses[] = {"enforce", "complain"};
|
||||||
|
const char *process_statuses[] = {"enforce", "complain", "unconfined"};
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
ret = get_profiles(&profiles, &nprofiles);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = get_processes(profiles, nprofiles, &processes, &nprocesses);
|
||||||
|
if (ret != 0) {
|
||||||
|
dfprintf(stderr, "Failed to get processes: %d....\n", ret);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
printf("{\"version\": \"1\", \"profiles\": {");
|
||||||
|
} else {
|
||||||
|
dprintf("%zd profiles are loaded.\n", nprofiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(profile_statuses); i++) {
|
||||||
|
size_t nfiltered = 0, j;
|
||||||
|
struct profile *filtered = NULL;
|
||||||
|
ret = filter_profiles(profiles, nprofiles, profile_statuses[i], &filtered, &nfiltered);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!json) {
|
||||||
|
dprintf("%zd profiles are in %s mode.\n", nfiltered, profile_statuses[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < nfiltered; j++) {
|
||||||
|
if (json) {
|
||||||
|
printf("%s\"%s\": \"%s\"",
|
||||||
|
i == 0 && j == 0 ? "" : ", ", filtered[j].name, profile_statuses[i]);
|
||||||
|
} else {
|
||||||
|
dprintf(" %s\n", filtered[j].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_profiles(filtered, nfiltered);
|
||||||
|
}
|
||||||
|
if (json) {
|
||||||
|
printf("}, \"processes\": {");
|
||||||
|
} else {
|
||||||
|
dprintf("%zd processes have profiles defined.\n", nprocesses);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(process_statuses); i++) {
|
||||||
|
size_t nfiltered = 0, j;
|
||||||
|
struct process *filtered = NULL;
|
||||||
|
ret = filter_processes(processes, nprocesses, process_statuses[i], &filtered, &nfiltered);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!json) {
|
||||||
|
if (strcmp(process_statuses[i], "unconfined") == 0) {
|
||||||
|
dprintf("%zd processes are unconfined but have a profile defined.\n", nfiltered);
|
||||||
|
} else {
|
||||||
|
dprintf("%zd processes are in %s mode.\n", nfiltered, process_statuses[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json) {
|
||||||
|
qsort(filtered, nfiltered, sizeof(*filtered), compare_processes_by_profile);
|
||||||
|
for (j = 0; j < nfiltered; j++) {
|
||||||
|
dprintf(" %s (%s) %s\n", filtered[j].exe, filtered[j].pid,
|
||||||
|
// hide profile name if matches executable
|
||||||
|
(strcmp(filtered[j].profile, filtered[j].exe) == 0 ?
|
||||||
|
"" :
|
||||||
|
filtered[j].profile));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// json output requires processes to be grouped per executable
|
||||||
|
qsort(filtered, nfiltered, sizeof(*filtered), compare_processes_by_executable);
|
||||||
|
for (j = 0; j < nfiltered; j++) {
|
||||||
|
if (j > 0 && strcmp(filtered[j].exe, filtered[j - 1].exe) == 0) {
|
||||||
|
// same executable
|
||||||
|
printf(", {\"profile\": \"%s\", \"pid\": \"%s\", \"status\": \"%s\"}",
|
||||||
|
filtered[j].profile, filtered[j].pid, filtered[j].mode);
|
||||||
|
} else {
|
||||||
|
printf("%s\"%s\": [{\"profile\": \"%s\", \"pid\": \"%s\", \"status\": \"%s\"}",
|
||||||
|
// first element will be a unique executable
|
||||||
|
i == 0 && j == 0 ? "" : "], ",
|
||||||
|
filtered[j].exe, filtered[j].profile, filtered[j].pid, filtered[j].mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free_processes(filtered, nfiltered);
|
||||||
|
}
|
||||||
|
if (json) {
|
||||||
|
printf("%s}}", nprocesses > 0 ? "]" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
free_processes(processes, nprocesses);
|
||||||
|
free_profiles(profiles, nprofiles);
|
||||||
|
return ret == 0 ? (nprofiles > 0 ? AA_EXIT_ENABLED : AA_EXIT_NO_POLICY) : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_json(const char *command) {
|
||||||
|
detailed_output(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_pretty_json(const char *command) {
|
||||||
|
// TODO - add support for pretty printing json output
|
||||||
|
return cmd_json(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_verbose(const char *command) {
|
||||||
|
verbose = 1;
|
||||||
|
return detailed_output(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_usage(const char *command)
|
||||||
|
{
|
||||||
|
printf("Usage: %s [OPTIONS]\n"
|
||||||
|
"Displays various information about the currently loaded AppArmor policy.\n"
|
||||||
|
"OPTIONS (one only):\n"
|
||||||
|
" --enabled returns error code if AppArmor not enabled\n"
|
||||||
|
" --profiled prints the number of loaded policies\n"
|
||||||
|
" --enforced prints the number of loaded enforcing policies\n"
|
||||||
|
" --complaining prints the number of loaded non-enforcing policies\n"
|
||||||
|
" --json displays multiple data points in machine-readable JSON format\n"
|
||||||
|
" --pretty-json same data as --json, formatted for human consumption as well\n"
|
||||||
|
" --verbose (default) displays multiple data points about loaded policy set\n"
|
||||||
|
" --help this message\n",
|
||||||
|
command);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct command {
|
||||||
|
const char * const name;
|
||||||
|
int (*cmd)(const char *command);
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct command commands[] = {
|
||||||
|
{"--enabled", cmd_enabled},
|
||||||
|
{"--profiled", cmd_profiled},
|
||||||
|
{"--enforced", cmd_enforced},
|
||||||
|
{"--complaining", cmd_complaining},
|
||||||
|
{"--json", cmd_json},
|
||||||
|
{"--pretty-json", cmd_pretty_json},
|
||||||
|
{"--verbose", cmd_verbose},
|
||||||
|
{"-v", cmd_verbose},
|
||||||
|
{"--help", print_usage},
|
||||||
|
{"-h", print_usage},
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int ret = EXIT_SUCCESS;
|
||||||
|
int _ret;
|
||||||
|
int (*cmd)(const char*) = cmd_verbose;
|
||||||
|
|
||||||
|
if (argc > 2) {
|
||||||
|
dfprintf(stderr, "Error: Too many options.\n");
|
||||||
|
cmd = print_usage;
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
} else if (argc == 2) {
|
||||||
|
int (*_cmd)(const char*) = NULL;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||||
|
if (strcmp(argv[1], commands[i].name) == 0) {
|
||||||
|
_cmd = commands[i].cmd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_cmd == NULL) {
|
||||||
|
dfprintf(stderr, "Error: Invalid command.\n");
|
||||||
|
cmd = print_usage;
|
||||||
|
ret = EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
cmd = _cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ret = cmd(argv[0]);
|
||||||
|
exit(ret == EXIT_FAILURE ? ret : _ret);
|
||||||
|
}
|
@ -22,7 +22,7 @@ include $(COMMONDIR)/Make.rules
|
|||||||
|
|
||||||
PYTOOLS = aa-easyprof aa-genprof aa-logprof aa-cleanprof aa-mergeprof \
|
PYTOOLS = aa-easyprof aa-genprof aa-logprof aa-cleanprof aa-mergeprof \
|
||||||
aa-autodep aa-audit aa-complain aa-enforce aa-disable \
|
aa-autodep aa-audit aa-complain aa-enforce aa-disable \
|
||||||
aa-notify aa-status aa-unconfined
|
aa-notify aa-unconfined
|
||||||
TOOLS = ${PYTOOLS} aa-decode aa-remove-unknown
|
TOOLS = ${PYTOOLS} aa-decode aa-remove-unknown
|
||||||
PYSETUP = python-tools-setup.py
|
PYSETUP = python-tools-setup.py
|
||||||
PYMODULES = $(wildcard apparmor/*.py apparmor/rule/*.py)
|
PYMODULES = $(wildcard apparmor/*.py apparmor/rule/*.py)
|
||||||
@ -53,13 +53,11 @@ install: ${MANPAGES} ${HTMLMANPAGES}
|
|||||||
install -d ${CONFDIR}
|
install -d ${CONFDIR}
|
||||||
install -m 644 logprof.conf severity.db notify.conf ${CONFDIR}
|
install -m 644 logprof.conf severity.db notify.conf ${CONFDIR}
|
||||||
install -d ${BINDIR}
|
install -d ${BINDIR}
|
||||||
ln -sf aa-status ${BINDIR}/apparmor_status
|
|
||||||
# aa-easyprof is installed by python-tools-setup.py
|
# aa-easyprof is installed by python-tools-setup.py
|
||||||
install -m 755 $(filter-out aa-easyprof, ${TOOLS}) ${BINDIR}
|
install -m 755 $(filter-out aa-easyprof, ${TOOLS}) ${BINDIR}
|
||||||
$(MAKE) -C po install DESTDIR=${DESTDIR} NAME=${NAME}
|
$(MAKE) -C po install DESTDIR=${DESTDIR} NAME=${NAME}
|
||||||
$(MAKE) install_manpages DESTDIR=${DESTDIR}
|
$(MAKE) install_manpages DESTDIR=${DESTDIR}
|
||||||
$(MAKE) -C vim install DESTDIR=${DESTDIR}
|
$(MAKE) -C vim install DESTDIR=${DESTDIR}
|
||||||
ln -sf aa-status.8 ${DESTDIR}/${MANDIR}/man8/apparmor_status.8
|
|
||||||
${PYTHON} ${PYSETUP} install --prefix=${PYPREFIX} --root=${DESTDIR} --version=${VERSION}
|
${PYTHON} ${PYSETUP} install --prefix=${PYPREFIX} --root=${DESTDIR} --version=${VERSION}
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
267
utils/aa-status
267
utils/aa-status
@ -1,267 +0,0 @@
|
|||||||
#! /usr/bin/python3
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Copyright (C) 2005-2006 Novell/SUSE
|
|
||||||
# Copyright (C) 2011 Canonical Ltd.
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of version 2 of the GNU General Public
|
|
||||||
# License published by the Free Software Foundation.
|
|
||||||
#
|
|
||||||
# ------------------------------------------------------------------
|
|
||||||
|
|
||||||
import re, os, sys, errno, json
|
|
||||||
|
|
||||||
# PLEASE NOTE: we try to keep aa-status as minimal as possible, for
|
|
||||||
# environments where installing all of the python utils and python
|
|
||||||
# apparmor module may not make sense. Please think carefully before
|
|
||||||
# importing anything from apparmor; see how the apparmor.fail import is
|
|
||||||
# handled below.
|
|
||||||
|
|
||||||
# setup exception handling
|
|
||||||
try:
|
|
||||||
from apparmor.fail import enable_aa_exception_handler
|
|
||||||
enable_aa_exception_handler()
|
|
||||||
except ImportError:
|
|
||||||
# just let normal python exceptions happen (LP: #1480492)
|
|
||||||
pass
|
|
||||||
|
|
||||||
def cmd_enabled():
|
|
||||||
'''Returns error code if AppArmor is not enabled'''
|
|
||||||
if get_profiles() == {}:
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
def cmd_profiled():
|
|
||||||
'''Prints the number of loaded profiles'''
|
|
||||||
profiles = get_profiles()
|
|
||||||
sys.stdout.write("%d\n" % len(profiles))
|
|
||||||
if profiles == {}:
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
def cmd_enforced():
|
|
||||||
'''Prints the number of loaded enforcing profiles'''
|
|
||||||
profiles = get_profiles()
|
|
||||||
sys.stdout.write("%d\n" % len(filter_profiles(profiles, 'enforce')))
|
|
||||||
if profiles == {}:
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
def cmd_complaining():
|
|
||||||
'''Prints the number of loaded non-enforcing profiles'''
|
|
||||||
profiles = get_profiles()
|
|
||||||
sys.stdout.write("%d\n" % len(filter_profiles(profiles, 'complain')))
|
|
||||||
if profiles == {}:
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
def cmd_verbose():
|
|
||||||
'''Displays multiple data points about loaded profile set'''
|
|
||||||
global verbose
|
|
||||||
verbose = True
|
|
||||||
profiles = get_profiles()
|
|
||||||
processes = get_processes(profiles)
|
|
||||||
|
|
||||||
stdmsg("%d profiles are loaded." % len(profiles))
|
|
||||||
for status in ('enforce', 'complain'):
|
|
||||||
filtered_profiles = filter_profiles(profiles, status)
|
|
||||||
stdmsg("%d profiles are in %s mode." % (len(filtered_profiles), status))
|
|
||||||
for item in filtered_profiles:
|
|
||||||
stdmsg(" %s" % item)
|
|
||||||
|
|
||||||
stdmsg("%d processes have profiles defined." % len(processes))
|
|
||||||
for status in ('enforce', 'complain', 'unconfined'):
|
|
||||||
filtered_processes = filter_processes(processes, status)
|
|
||||||
if status == 'unconfined':
|
|
||||||
stdmsg("%d processes are unconfined but have a profile defined." % len(filtered_processes))
|
|
||||||
else:
|
|
||||||
stdmsg("%d processes are in %s mode." % (len(filtered_processes), status))
|
|
||||||
# Sort by name, and then by pid
|
|
||||||
filtered_processes.sort(key=lambda x: int(x[0]))
|
|
||||||
filtered_processes.sort(key=lambda x: x[1])
|
|
||||||
for (pid, profile, exe) in filtered_processes:
|
|
||||||
if exe == profile:
|
|
||||||
profile = ""
|
|
||||||
stdmsg(" %s (%s) %s" % (exe, pid, profile))
|
|
||||||
|
|
||||||
if profiles == {}:
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
def cmd_json(pretty_output=False):
|
|
||||||
'''Outputs multiple data points about loaded profile set in a machine-readable JSON format'''
|
|
||||||
global verbose
|
|
||||||
profiles = get_profiles()
|
|
||||||
processes = get_processes(profiles)
|
|
||||||
|
|
||||||
i = {
|
|
||||||
'version': '1',
|
|
||||||
'profiles': {},
|
|
||||||
'processes': {}
|
|
||||||
}
|
|
||||||
|
|
||||||
for status in ('enforce', 'complain'):
|
|
||||||
filtered_profiles = filter_profiles(profiles, status)
|
|
||||||
for item in filtered_profiles:
|
|
||||||
i['profiles'][item] = status
|
|
||||||
|
|
||||||
for status in ('enforce', 'complain', 'unconfined'):
|
|
||||||
filtered_processes = filter_processes(processes, status)
|
|
||||||
for (pid, profile, exe) in filtered_processes:
|
|
||||||
if exe not in i['processes']:
|
|
||||||
i['processes'][exe] = []
|
|
||||||
|
|
||||||
i['processes'][exe].append({
|
|
||||||
'profile': profile,
|
|
||||||
'pid': pid,
|
|
||||||
'status': status
|
|
||||||
})
|
|
||||||
|
|
||||||
if pretty_output:
|
|
||||||
sys.stdout.write(json.dumps(i, sort_keys=True, indent=4, separators=(',', ': ')))
|
|
||||||
else:
|
|
||||||
sys.stdout.write(json.dumps(i))
|
|
||||||
|
|
||||||
def cmd_pretty_json():
|
|
||||||
cmd_json(True)
|
|
||||||
|
|
||||||
def get_profiles():
|
|
||||||
'''Fetch loaded profiles'''
|
|
||||||
|
|
||||||
profiles = {}
|
|
||||||
|
|
||||||
if os.path.exists("/sys/module/apparmor"):
|
|
||||||
stdmsg("apparmor module is loaded.")
|
|
||||||
else:
|
|
||||||
errormsg("apparmor module is not loaded.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
apparmorfs = find_apparmorfs()
|
|
||||||
if not apparmorfs:
|
|
||||||
errormsg("apparmor filesystem is not mounted.")
|
|
||||||
sys.exit(3)
|
|
||||||
|
|
||||||
apparmor_profiles = os.path.join(apparmorfs, "profiles")
|
|
||||||
try:
|
|
||||||
f = open(apparmor_profiles)
|
|
||||||
except IOError as e:
|
|
||||||
if e.errno == errno.EACCES:
|
|
||||||
errormsg("You do not have enough privilege to read the profile set.")
|
|
||||||
else:
|
|
||||||
errormsg("Could not open %s: %s" % (apparmor_profiles, os.strerror(e.errno)))
|
|
||||||
sys.exit(4)
|
|
||||||
|
|
||||||
for p in f.readlines():
|
|
||||||
match = re.search("^(.+)\s+\((\w+)\)$", p)
|
|
||||||
profiles[match.group(1)] = match.group(2)
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
return profiles
|
|
||||||
|
|
||||||
def get_processes(profiles):
|
|
||||||
'''Fetch process list'''
|
|
||||||
processes = {}
|
|
||||||
contents = os.listdir("/proc")
|
|
||||||
for filename in contents:
|
|
||||||
if filename.isdigit():
|
|
||||||
try:
|
|
||||||
for p in open("/proc/%s/attr/current" % filename).readlines():
|
|
||||||
match = re.search("^([^\(]+)\s+\((\w+)\)$", p)
|
|
||||||
exe = os.path.realpath("/proc/%s/exe" % filename)
|
|
||||||
if match:
|
|
||||||
processes[filename] = { 'profile' : match.group(1), \
|
|
||||||
'exe': exe, \
|
|
||||||
'mode' : match.group(2) }
|
|
||||||
elif exe in profiles:
|
|
||||||
# keep only unconfined processes that have a profile defined
|
|
||||||
processes[filename] = { 'profile' : exe, \
|
|
||||||
'exe': exe, \
|
|
||||||
'mode' : 'unconfined' }
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return processes
|
|
||||||
|
|
||||||
def filter_profiles(profiles, status):
|
|
||||||
'''Return a list of profiles that have a particular status'''
|
|
||||||
filtered = []
|
|
||||||
for key, value in list(profiles.items()):
|
|
||||||
if value == status:
|
|
||||||
filtered.append(key)
|
|
||||||
filtered.sort()
|
|
||||||
return filtered
|
|
||||||
|
|
||||||
def filter_processes(processes, status):
|
|
||||||
'''Return a list of processes that have a particular status'''
|
|
||||||
filtered = []
|
|
||||||
for key, value in list(processes.items()):
|
|
||||||
if value['mode'] == status:
|
|
||||||
filtered.append([key, value['profile'], value['exe']])
|
|
||||||
return filtered
|
|
||||||
|
|
||||||
def find_apparmorfs():
|
|
||||||
'''Finds AppArmor mount point'''
|
|
||||||
for p in open("/proc/mounts", "rb").readlines():
|
|
||||||
if p.split()[2].decode() == "securityfs" and \
|
|
||||||
os.path.exists(os.path.join(p.split()[1].decode(), "apparmor")):
|
|
||||||
return os.path.join(p.split()[1].decode(), "apparmor")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def errormsg(message):
|
|
||||||
'''Prints to stderr if verbose mode is on'''
|
|
||||||
global verbose
|
|
||||||
if verbose:
|
|
||||||
sys.stderr.write(message + "\n")
|
|
||||||
|
|
||||||
def stdmsg(message):
|
|
||||||
'''Prints to stdout if verbose mode is on'''
|
|
||||||
global verbose
|
|
||||||
if verbose:
|
|
||||||
sys.stdout.write(message + "\n")
|
|
||||||
|
|
||||||
def print_usage():
|
|
||||||
'''Print usage information'''
|
|
||||||
sys.stdout.write('''Usage: %s [OPTIONS]
|
|
||||||
Displays various information about the currently loaded AppArmor policy.
|
|
||||||
OPTIONS (one only):
|
|
||||||
--enabled returns error code if AppArmor not enabled
|
|
||||||
--profiled prints the number of loaded policies
|
|
||||||
--enforced prints the number of loaded enforcing policies
|
|
||||||
--complaining prints the number of loaded non-enforcing policies
|
|
||||||
--json displays multiple data points in machine-readable JSON format
|
|
||||||
--pretty-json same data as --json, formatted for human consumption as well
|
|
||||||
--verbose (default) displays multiple data points about loaded policy set
|
|
||||||
--help this message
|
|
||||||
''' % sys.argv[0])
|
|
||||||
|
|
||||||
# Main
|
|
||||||
global verbose
|
|
||||||
verbose = False
|
|
||||||
|
|
||||||
if len(sys.argv) > 2:
|
|
||||||
sys.stderr.write("Error: Too many options.\n")
|
|
||||||
print_usage()
|
|
||||||
sys.exit(1)
|
|
||||||
elif len(sys.argv) == 2:
|
|
||||||
cmd = sys.argv.pop(1)
|
|
||||||
else:
|
|
||||||
cmd = '--verbose'
|
|
||||||
|
|
||||||
# Command dispatch:
|
|
||||||
commands = {
|
|
||||||
'--enabled' : cmd_enabled,
|
|
||||||
'--profiled' : cmd_profiled,
|
|
||||||
'--enforced' : cmd_enforced,
|
|
||||||
'--complaining' : cmd_complaining,
|
|
||||||
'--json' : cmd_json,
|
|
||||||
'--pretty-json' : cmd_pretty_json,
|
|
||||||
'--verbose' : cmd_verbose,
|
|
||||||
'-v' : cmd_verbose,
|
|
||||||
'--help' : print_usage,
|
|
||||||
'-h' : print_usage
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd in commands:
|
|
||||||
commands[cmd]()
|
|
||||||
sys.exit(0)
|
|
||||||
else:
|
|
||||||
sys.stderr.write("Error: Invalid command.\n")
|
|
||||||
print_usage()
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user