aa-status: switch commands dispatch to get_opt_long

To allow for a richer command set switch arg handling to use
get_opt_long.

Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
John Johansen 2022-05-28 02:12:29 -07:00
parent cacb812b58
commit ce13b28154

View file

@ -7,6 +7,7 @@
*/ */
#define _GNU_SOURCE /* for asprintf() */ #define _GNU_SOURCE /* for asprintf() */
#include <getopt.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -87,6 +88,13 @@ do { \
} while (0) } while (0)
/**
* get_profiles - get a listing of profiles on the system
* @profiles: return: list of profiles
* @n: return: number of elements in @profiles
*
* Return: 0 on success, shell error on failure
*/
static int get_profiles(struct profile **profiles, size_t *n) { static int get_profiles(struct profile **profiles, size_t *n) {
autofree char *apparmorfs = NULL; autofree char *apparmorfs = NULL;
autofree char *apparmor_profiles = NULL; autofree char *apparmor_profiles = NULL;
@ -182,6 +190,16 @@ static int compare_profiles(const void *a, const void *b) {
((struct profile *)b)->name); ((struct profile *)b)->name);
} }
/**
* filter_profiles - create a filtered profile list
* @profiles: list of profiles
* @n: number of elements in @profiles
* @filter: string to match against profile mode, if NULL no filter
* @filtered: return: new list of profiles that match the filter
* @nfiltered: return: number of elements in @filtered
*
* Return: 0 on success, shell error on failure
*/
static int filter_profiles(struct profile *profiles, static int filter_profiles(struct profile *profiles,
size_t n, size_t n,
const char *filter, const char *filter,
@ -216,6 +234,17 @@ static int filter_profiles(struct profile *profiles,
return ret; return ret;
} }
/**
* get_processes - get a list of processes that are confined
* @profiles: list of profiles, used to filter out unconfined processes
* @n: number of entries in @procfiles
* @processes: return: list of confined processes
* @nprocesses: return: number of entries in @processes
*
* Return: 0 on success, shell exit code on failure
*
* profiles is used to find prcesses that should be confined but aren't.
*/
static int get_processes(struct profile *profiles, static int get_processes(struct profile *profiles,
size_t n, size_t n,
struct process **processes, struct process **processes,
@ -293,6 +322,8 @@ static int get_processes(struct profile *profiles,
if (mode == NULL) { if (mode == NULL) {
// is unconfined so keep only if this has a // is unconfined so keep only if this has a
// matching profile. TODO: fix to use attachment // matching profile. TODO: fix to use attachment
// ideally would walk process tree and apply
// according to x rules and attachments
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (strcmp(profiles[i].name, real_exe) == 0) { if (strcmp(profiles[i].name, real_exe) == 0) {
profile = strdup(real_exe); profile = strdup(real_exe);
@ -330,6 +361,16 @@ exit:
return ret; return ret;
} }
/**
* filter_processes: create a new filtered process list by applying @filter
* @processes: list of processes to filter
* @n: number of entries in @processes
* @filter: mode string to filter @processes against, if NULL no filter
* @filtered: return: new list of processes matching filter
* @nfiltered: number of entries in @filtered
*
* Return: 0 on success, shell exit value on failure
*/
static int filter_processes(struct process *processes, static int filter_processes(struct process *processes,
size_t n, size_t n,
const char *filter, const char *filter,
@ -360,11 +401,15 @@ static int filter_processes(struct process *processes,
*nfiltered = *nfiltered + 1; *nfiltered = *nfiltered + 1;
} }
} }
return ret; return ret;
} }
/** /**
* Returns error code if AppArmor is not enabled * simple_filtered_count - count the number of profiles with mode == filter
* @filter: mode string to filter profiles on
*
* Return: 0 on success, else shell error code
*/ */
static int simple_filtered_count(const char *filter) { static int simple_filtered_count(const char *filter) {
size_t n; size_t n;
@ -383,6 +428,12 @@ static int simple_filtered_count(const char *filter) {
return ret; return ret;
} }
/**
* simple_filtered_process_count - count processes with mode == filter
* @filter: mode string to filter processes on
*
* Return: 0 on success, else shell error code
*/
static int simple_filtered_process_count(const char *filter) { static int simple_filtered_process_count(const char *filter) {
size_t nprocesses, nprofiles; size_t nprocesses, nprofiles;
struct profile *profiles = NULL; struct profile *profiles = NULL;
@ -405,36 +456,6 @@ static int simple_filtered_process_count(const char *filter) {
return ret; return ret;
} }
static int cmd_enabled(__unused const char *command) {
int res = aa_is_enabled();
return res == 1 ? 0 : 1;
}
static int cmd_profiled(__unused const char *command) {
return simple_filtered_count(NULL);
}
static int cmd_enforced(__unused const char *command) {
return simple_filtered_count("enforce");
}
static int cmd_complaining(__unused const char *command) {
return simple_filtered_count("complain");
}
static int cmd_kill(__unused const char *command) {
return simple_filtered_count("kill");
}
static int cmd_unconfined(__unused const char *command) {
return simple_filtered_count("unconfined");
}
static int cmd_process_mixed(__unused const char *command) {
return simple_filtered_process_count("mixed");
}
static int compare_processes_by_profile(const void *a, const void *b) { static int compare_processes_by_profile(const void *a, const void *b) {
return strcmp(((struct process *)a)->profile, return strcmp(((struct process *)a)->profile,
@ -446,6 +467,12 @@ static int compare_processes_by_executable(const void *a, const void *b) {
((struct process *)b)->exe); ((struct process *)b)->exe);
} }
/**
* detailed_out - output a detailed listing of apparmor status
* @json: if specified file to output json to other wise regular text output
*
* Return: 0 on success, else shell error
*/
static int detailed_output(FILE *json) { static int detailed_output(FILE *json) {
size_t nprofiles = 0, nprocesses = 0; size_t nprofiles = 0, nprocesses = 0;
struct profile *profiles = NULL; struct profile *profiles = NULL;
@ -555,12 +582,14 @@ exit:
return ret == 0 ? (nprofiles > 0 ? AA_EXIT_ENABLED : AA_EXIT_NO_POLICY) : ret; return ret == 0 ? (nprofiles > 0 ? AA_EXIT_ENABLED : AA_EXIT_NO_POLICY) : ret;
} }
static int cmd_json(__unused const char *command) { /**
detailed_output(stdout); * cmd_pretty_json - output nicelye formatted json to stdout
return 0; * @command: command name - currently unused
} *
* Return: 0 on success, shell error on failure
static int cmd_pretty_json(__unused const char *command) { */
static int cmd_pretty_json()
{
autofree char *buffer = NULL; autofree char *buffer = NULL;
autofree char *pretty = NULL; autofree char *pretty = NULL;
cJSON *json; cJSON *json;
@ -595,13 +624,14 @@ static int cmd_pretty_json(__unused const char *command) {
return AA_EXIT_ENABLED; return AA_EXIT_ENABLED;
} }
static int cmd_verbose(__unused const char *command) { static int print_usage(const char *command, bool error)
verbose = 1;
return detailed_output(NULL);
}
static int print_usage(const char *command)
{ {
int status = EXIT_SUCCESS;
if (error) {
status = EXIT_FAILURE;
}
printf("Usage: %s [OPTIONS]\n" printf("Usage: %s [OPTIONS]\n"
"Displays various information about the currently loaded AppArmor policy.\n" "Displays various information about the currently loaded AppArmor policy.\n"
"OPTIONS (one only):\n" "OPTIONS (one only):\n"
@ -617,58 +647,107 @@ static int print_usage(const char *command)
" --verbose (default) displays multiple data points about loaded policy set\n" " --verbose (default) displays multiple data points about loaded policy set\n"
" --help this message\n", " --help this message\n",
command); command);
exit(status);
return 0; return 0;
} }
struct command {
const char * const name;
int (*cmd)(const char *command);
};
static struct command commands[] = { #define ARG_ENABLED 129
{"--enabled", cmd_enabled}, #define ARG_PROFILED 130
{"--profiled", cmd_profiled}, #define ARG_ENFORCED 131
{"--enforced", cmd_enforced}, #define ARG_COMPLAIN 132
{"--complaining", cmd_complaining}, #define ARG_KILL 133
{"--kill", cmd_kill}, #define ARG_UNCONFINED 134
{"--special-unconfined", cmd_unconfined}, #define ARG_PS_MIXED 135
{"--process-mixed", cmd_process_mixed}, #define ARG_JSON 136
{"--json", cmd_json}, #define ARG_PRETTY 137
{"--pretty-json", cmd_pretty_json}, #define ARG_VERBOSE 'v'
{"--verbose", cmd_verbose}, #define ARG_HELP 'h'
{"-v", cmd_verbose},
{"--help", print_usage}, static char **parse_args(int argc, char **argv)
{"-h", print_usage}, {
}; int opt;
struct option long_opts[] = {
{"enabled", no_argument, 0, ARG_ENABLED},
{"profiled", no_argument, 0, ARG_PROFILED},
{"enforced", no_argument, 0, ARG_ENFORCED},
{"complaining", no_argument, 0, ARG_COMPLAIN},
{"kill", no_argument, 0, ARG_KILL},
{"special-unconfined", no_argument, 0, ARG_UNCONFINED},
{"process-mixed", no_argument, 0, ARG_PS_MIXED},
{"json", no_argument, 0, ARG_JSON},
{"pretty-json", no_argument, 0, ARG_PRETTY},
{"verbose", no_argument, 0, ARG_VERBOSE},
{"help", no_argument, 0, ARG_HELP},
{NULL, 0, 0, 0},
};
// Using exit here is temporary
while ((opt = getopt_long(argc, argv, "+vh", long_opts, NULL)) != -1) {
switch (opt) {
case ARG_ENABLED:
exit(aa_is_enabled() == 1 ? 0 : AA_EXIT_DISABLED);
break;
case ARG_VERBOSE:
verbose = 1;
exit(detailed_output(NULL));
break;
case ARG_HELP:
print_usage(argv[0], false);
break;
case ARG_PROFILED:
exit(simple_filtered_count(NULL));
break;
case ARG_ENFORCED:
exit(simple_filtered_count("enforce"));
break;
case ARG_COMPLAIN:
exit(simple_filtered_count("complain"));
break;
case ARG_UNCONFINED:
exit(simple_filtered_count("unconfined"));
break;
case ARG_KILL:
exit(simple_filtered_count("kill"));
break;
case ARG_PS_MIXED:
exit(simple_filtered_process_count("mixed"));
break;
case ARG_JSON:
exit(detailed_output(stdout));
break;
case ARG_PRETTY:
exit(cmd_pretty_json());
break;
default:
dfprintf(stderr, "Error: Invalid command.\n");
print_usage(argv[0], true);
break;
}
}
return argv + optind;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int ret = EXIT_SUCCESS; int ret = EXIT_SUCCESS;
int _ret; const char *progname = argv[0];
int (*cmd)(const char*) = cmd_verbose;
if (argc > 2) { if (argc > 2) {
dfprintf(stderr, "Error: Too many options.\n"); dfprintf(stderr, "Error: Too many options.\n");
cmd = print_usage; print_usage(progname, true);
ret = EXIT_FAILURE;
} else if (argc == 2) { } else if (argc == 2) {
int (*_cmd)(const char*) = NULL; argv = parse_args(argc, argv);
size_t i; // temporary if we get here its an error
for (i = 0; i < ARRAY_SIZE(commands); i++) { ret = EXIT_FAILURE;
if (strcmp(argv[1], commands[i].name) == 0) { } else {
_cmd = commands[i].cmd; verbose = 1;
break; ret = detailed_output(NULL);
}
}
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(ret == EXIT_FAILURE ? ret : _ret);
} }