diff options
author | Miroslav Grepl <mgrepl@redhat.com> | 2014-04-11 09:37:53 +0200 |
---|---|---|
committer | Miroslav Grepl <mgrepl@redhat.com> | 2014-04-11 09:37:53 +0200 |
commit | 47be9ff57e72906660bb62a515222f482131e1fb (patch) | |
tree | 2cb0ef0ba48d73b1df7cc0915754a17e19464bb6 /secmds | |
download | setools-47be9ff57e72906660bb62a515222f482131e1fb.tar.gz setools-47be9ff57e72906660bb62a515222f482131e1fb.tar.xz setools-47be9ff57e72906660bb62a515222f482131e1fb.zip |
Create setools-3.3.7 git repomaster
Diffstat (limited to 'secmds')
-rw-r--r-- | secmds/Makefile.am | 40 | ||||
-rw-r--r-- | secmds/findcon.cc | 245 | ||||
-rw-r--r-- | secmds/indexcon.cc | 121 | ||||
-rw-r--r-- | secmds/replcon.cc | 358 | ||||
-rw-r--r-- | secmds/seinfo.c | 2337 | ||||
-rw-r--r-- | secmds/sesearch.c | 1173 |
6 files changed, 4274 insertions, 0 deletions
diff --git a/secmds/Makefile.am b/secmds/Makefile.am new file mode 100644 index 0000000..ddc88b1 --- /dev/null +++ b/secmds/Makefile.am @@ -0,0 +1,40 @@ +# various setools command line tools + +bin_PROGRAMS = seinfo sesearch findcon replcon indexcon + +# These are for indexcon so that it is usable on machines without setools +STATICLIBS = ../libsefs/src/libsefs.a ../libapol/src/libapol.a ../libqpol/src/libqpol.a -lsqlite3 + +AM_CFLAGS = @DEBUGCFLAGS@ @WARNCFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ @APOL_CFLAGS@ +AM_CXXFLAGS = @DEBUGCXXFLAGS@ @WARNCXXFLAGS@ @PROFILECFLAGS@ @SELINUX_CFLAGS@ \ + @QPOL_CFLAGS@ @APOL_CFLAGS@ @SEFS_CFLAGS@ +AM_LDFLAGS = @DEBUGLDFLAGS@ @WARNLDFLAGS@ @PROFILELDFLAGS@ + +LDADD = @SELINUX_LIB_FLAG@ @APOL_LIB_FLAG@ @QPOL_LIB_FLAG@ +DEPENDENCIES = $(top_builddir)/libapol/src/libapol.so $(top_builddir)/libqpol/src/libqpol.so + +seinfo_SOURCES = seinfo.c + +sesearch_SOURCES = sesearch.c + +indexcon_SOURCES = indexcon.cc +indexcon_LDADD = @SELINUX_LIB_FLAG@ $(STATICLIBS) +indexcon_DEPENDENCIES = $(DEPENDENCIES) $(top_builddir)/libsefs/src/libsefs.so + +findcon_SOURCES = findcon.cc +findcon_LDADD = @SEFS_LIB_FLAG@ $(LDADD) +findcon_DEPENDENCIES = $(DEPENDENCIES) $(top_builddir)/libsefs/src/libsefs.so + +replcon_SOURCES = replcon.cc +replcon_LDADD = @SEFS_LIB_FLAG@ $(LDADD) +replcon_DEPENDENCIES = $(DEPENDENCIES) $(top_builddir)/libsefs/src/libsefs.so + +$(top_builddir)/libapol/src/libapol.so: + $(MAKE) -C $(top_builddir)/libapol/src $(notdir $@) + +$(top_builddir)/libqpol/src/libqpol.so: + $(MAKE) -C $(top_builddir)/libqpol/src $(notdir $@) + +$(top_builddir)/libsefs/src/libsefs.so: + $(MAKE) -C $(top_builddir)/libsefs/src $(notdir $@) diff --git a/secmds/findcon.cc b/secmds/findcon.cc new file mode 100644 index 0000000..325c2f0 --- /dev/null +++ b/secmds/findcon.cc @@ -0,0 +1,245 @@ +/** + * @file + * + * Search a fclist (either the disk, a database generated by indexcon + * or apol, or a file_contexts file for entries that match a given + * SELinux file context. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <sefs/db.hh> +#include <sefs/fcfile.hh> +#include <sefs/filesystem.hh> +#include <sefs/entry.hh> +#include <sefs/query.hh> + +using namespace std; + +#include <errno.h> +#include <getopt.h> +#include <iostream> +#include <string> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC" + +enum OPTIONS +{ + OPTION_CONTEXT = 256 +}; + +static struct option const longopts[] = { + {"class", required_argument, NULL, 'c'}, + {"type", required_argument, NULL, 't'}, + {"user", required_argument, NULL, 'u'}, + {"role", required_argument, NULL, 'r'}, + {"mls-range", required_argument, NULL, 'm'}, + {"path", required_argument, NULL, 'p'}, + {"regex", no_argument, NULL, 'R'}, + {"context", required_argument, NULL, OPTION_CONTEXT}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +static void usage(const char *program_name, bool brief) +{ + cout << "Usage: " << program_name << " FCLIST [OPTIONS] [EXPRESSION]" << endl << endl; + if (brief) + { + cout << "\tTry " << program_name << " --help for more help." << endl << endl; + return; + } + + cout << "Find files matching the given SELinux context." << endl << endl; + + cout << "FCLIST is a directory name, a file_contexts file, or a database" << endl; + cout << "created by a previous run of indexcon." << endl; + cout << endl; + + cout << "EXPRESSION:" << endl; + cout << " -t TYPE, --type=TYPE find contexts with type TYPE" << endl; + cout << " -u USER, --user=USER find contexts with user USER" << endl; + cout << " -r ROLE, --role=ROLE find contexts with role ROLE" << endl; + cout << " -m RANGE, --mls-range=RANGE find contexts with MLS range RANGE" << endl; + cout << " --context=CONTEXT partial or full context to find" << endl; + cout << " (overrides expression options above)" << endl; + cout << " -p PATH, --path=PATH find files in PATH" << endl; + cout << " -c CLASS, --class=CLASS find files of object class CLASS" << endl; + cout << endl; + + cout << "OPTIONS:" << endl; + cout << " -R, --regex enable regular expressions" << endl; + cout << " -h, --help print this help text and exit" << endl; + cout << " -V, --version print version information and exit" << endl; + cout << endl; + cout << "If the fclist does not contain MLS ranges and -m was given," << endl; + cout << "then the search will return nothing." << endl; +} + +static int print_entry(sefs_fclist * fclist, const sefs_entry * e, void *arg __attribute__ ((unused))) +{ + char *str = e->toString(); + cout << str << endl; + free(str); + return 0; +} + +int main(int argc, char *argv[]) +{ + int optc; + sefs_query *query = new sefs_query(); + + apol_context_t *context = NULL; + try + { + while ((optc = getopt_long(argc, argv, "t:u:r:m:p:c:RhV", longopts, NULL)) != -1) + { + switch (optc) + { + case 't': + if (context == NULL) + { + query->type(optarg, false); + } + break; + case 'u': + if (context == NULL) + { + query->user(optarg); + } + break; + case 'r': + if (context == NULL) + { + query->role(optarg); + } + break; + case 'm': + if (context == NULL) + { + query->range(optarg, APOL_QUERY_EXACT); + } + break; + case OPTION_CONTEXT: + if ((context = apol_context_create_from_literal(optarg)) == NULL) + { + cerr << "Could not create a context." << endl; + throw runtime_error(strerror(errno)); + } + break; + case 'p': + query->path(optarg); + break; + case 'c': + query->objectClass(optarg); + break; + case 'R': + query->regex(true); + break; + case 'h': // help + usage(argv[0], false); + exit(0); + case 'V': // version + cout << "findcon " << VERSION << endl << COPYRIGHT_INFO << endl; + exit(0); + default: + usage(argv[0], true); + exit(1); + } + } + if (context != NULL) + { + query->user(apol_context_get_user(context)); + query->role(apol_context_get_role(context)); + query->type(apol_context_get_type(context), false); + if (apol_context_get_range(context) != NULL) + { + char *r = apol_mls_range_render(NULL, apol_context_get_range(context)); + query->range(r, APOL_QUERY_EXACT); + free(r); + } + else + { + query->range(NULL, APOL_QUERY_EXACT); + } + } + } + catch(...) + { + cerr << strerror(errno) << endl; + apol_context_destroy(&context); + delete query; + exit(-1); + } + apol_context_destroy(&context); + + if (optind + 1 != argc) + { + usage(argv[0], 1); + delete query; + exit(-1); + } + + // try to autodetect the type of thing being searched + struct stat sb; + if (stat(argv[optind], &sb) != 0) + { + cerr << "Could not open " << argv[optind] << ": " << strerror(errno) << endl; + delete query; + exit(-1); + } + + sefs_fclist *fclist = NULL; + try + { + if (S_ISDIR(sb.st_mode)) + { + fclist = new sefs_filesystem(argv[optind], NULL, NULL); + } + else if (sefs_db::isDB(argv[optind])) + { + fclist = new sefs_db(argv[optind], NULL, NULL); + } + else + { + fclist = new sefs_fcfile(argv[optind], NULL, NULL); + } + + if (fclist->runQueryMap(query, print_entry, NULL) < 0) + { + throw runtime_error(strerror(errno)); + } + } + catch(...) + { + delete query; + delete fclist; + exit(-1); + } + + delete query; + delete fclist; + return 0; +} diff --git a/secmds/indexcon.cc b/secmds/indexcon.cc new file mode 100644 index 0000000..dbbb4af --- /dev/null +++ b/secmds/indexcon.cc @@ -0,0 +1,121 @@ +/** + * @file + * + * Command-line program that builds a libsefs database of file + * contexts. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * indexcon: a tool for indexing the security contexts of filesystem entities + */ + +#include <config.h> + +#include <sefs/db.hh> +#include <sefs/filesystem.hh> + +using namespace std; + +#include <iostream> +#include <getopt.h> + +#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC" + +static struct option const longopts[] = { + {"directory", required_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +static void usage(const char *program_name, bool brief) +{ + cout << "Usage: " << program_name << " FILE [OPTIONS]" << endl << endl; + if (brief) + { + cout << "\tTry " << program_name << " --help for more help." << endl << endl; + return; + } + cout << "Index SELinux contexts on the filesystem." << endl; + cout << endl; + cout << " -d DIR, --directory=DIR start scanning at directory DIR (default \"/\")" << endl; + cout << " -h, --help print this help text and exit" << endl; + cout << " -V, --version print version information and exit" << endl; +} + +int main(int argc, char *argv[]) +{ + int optc; + + char *outfilename = NULL, *dir = "/"; + + while ((optc = getopt_long(argc, argv, "d:hV", longopts, NULL)) != -1) + { + switch (optc) + { + case 'd': // starting directory + dir = optarg; + break; + case 'h': + usage(argv[0], false); + exit(0); + case 'V': + cout << "indexcon " << VERSION << endl << COPYRIGHT_INFO << endl; + exit(0); + default: + usage(argv[0], true); + exit(1); + } + } + if (argc - optind > 1 || argc - optind < 1) + { + usage(argv[0], true); + exit(1); + } + else + { + outfilename = argv[optind]; + } + + if (outfilename == NULL) + { + usage(argv[0], true); + exit(1); + } + + sefs_filesystem *fs = NULL; + sefs_db *db = NULL; + try + { + fs = new sefs_filesystem(dir, NULL, NULL); + db = new sefs_db(fs, NULL, NULL); + db->save(outfilename); + } + catch(...) + { + delete fs; + delete db; + exit(2); + } + + delete fs; + delete db; + + return 0; +} diff --git a/secmds/replcon.cc b/secmds/replcon.cc new file mode 100644 index 0000000..34f7c1a --- /dev/null +++ b/secmds/replcon.cc @@ -0,0 +1,358 @@ +/** + * @file + * + * A tool for replacing file contexts in SELinux. + * + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Jason Tang jtang@tresys.com + * + * Copyright (C) 2003-2007 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +#include <sefs/filesystem.hh> +#include <sefs/query.hh> +#include <selinux/selinux.h> +#include <apol/util.h> + +using namespace std; + +#include <assert.h> +#include <errno.h> +#include <getopt.h> +#include <iostream> +#include <stdlib.h> + +#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC" + +enum OPTIONS +{ + OPTION_CONTEXT = 256 +}; + +static struct option const longopts[] = { + {"class", required_argument, NULL, 'c'}, + {"type", required_argument, NULL, 't'}, + {"user", required_argument, NULL, 'u'}, + {"role", required_argument, NULL, 'r'}, + {"mls-range", required_argument, NULL, 'm'}, + {"path", required_argument, NULL, 'p'}, + {"regex", no_argument, NULL, 'R'}, + {"context", required_argument, NULL, OPTION_CONTEXT}, + {"verbose", no_argument, NULL, 'v'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +extern int lsetfilecon_raw(const char *, security_context_t) __attribute__ ((weak)); + +/** + * As that setools must work with older libselinux versions that may + * not have the _raw() functions, declare them as weak. If libselinux + * does indeed have the new functions then use them; otherwise + * fallback to the originals. + */ +static int replcon_lsetfilecon(const char *path, security_context_t context) +{ + if (lsetfilecon_raw != NULL) + { + return lsetfilecon_raw(path, context); + } + else + { + return lsetfilecon(path, context); + } +} + +struct replcon_info +{ + bool verbose, mls; + apol_context_t *replcon; +}; + +static void usage(const char *program_name, bool brief) +{ + cout << "Usage: " << program_name << " NEW_CONTEXT DIR [OPTIONS] [EXPRESSION]" << endl << endl; + if (brief) + { + cout << "\tTry " << program_name << " --help for more help." << endl << endl; + return; + } + + cout << "Replace SELinux file contexts for files matching a given context." << endl << endl; + + cout << "REQUIRED ARGUMENTS :" << endl; + cout << " NEW_CONTEXT partial or full context to relabel" << endl; + cout << " DIR starting directory to replace" << endl; + cout << endl; + cout << "EXPRESSION:" << endl; + cout << " -t TYPE, --type=TYPE find contexts with type TYPE" << endl; + cout << " -u USER, --user=USER find contexts with user USER" << endl; + cout << " -r ROLE, --role=ROLE find contexts with role ROLE" << endl; + cout << " -m RANGE, --mls-range=RANGE find contexts with MLS range RANGE" << endl; + cout << " --context=CONTEXT partial or full context to find" << endl; + cout << " (overrides expression options above)" << endl; + cout << " -p PATH, --path=PATH find files in PATH" << endl; + cout << " -c CLASS, --class=CLASS find files of object class CLASS" << endl; + cout << endl; + + cout << "OPTIONS:" << endl; + cout << " -R, --regex enable regular expressions" << endl; + cout << " -v, --verbose show context of matching files" << endl; + cout << " -h, --help print this help text and exit" << endl; + cout << " -V, --version print version information and exit" << endl; + cout << endl; + cout << "If the fclist does not contain MLS ranges and -m was given," << endl; + cout << "then the search will return nothing." << endl; + cout << endl; + cout << "NEW_CONTEXT is as a colon separated list of user, role, type, and MLS range" << endl; + cout << "such as follows: user_u:object_r:user_t:s0. If a field is not specified," << endl; + cout << "that portion of the context will not be replaced." << endl; + cout << "Examples:" << endl; + cout << " replcon ::type_t: ." << endl; + cout << " Replace all files and subdirectories in current directory with" << endl; + cout << " type type_t, recursing within the directory." << endl; + cout << " replcon -u user_u *:role_r:* ." << endl; + cout << " Replace files that contain user_u with role role_r." << endl; + cout << " replcon --context ::type_t:so :::s0:c0 /tmp" << endl; + cout << " Replace files with type type_t and level s0 in /tmp with MLS" << endl; + cout << " range s0:c0." << endl; +} + +static int replace_entry(sefs_fclist * fclist, const sefs_entry * e, void *arg) +{ + struct replcon_info *r = static_cast < struct replcon_info *>(arg); + const apol_context_t *scon = e->context(); + const char *user, *role, *type; + char *con_str = NULL; + size_t len = 0; + + // determine what the new context should be + if ((user = apol_context_get_user(r->replcon)) == NULL) + { + user = apol_context_get_user(scon); + } + if ((role = apol_context_get_role(r->replcon)) == NULL) + { + role = apol_context_get_role(scon); + } + if ((type = apol_context_get_type(r->replcon)) == NULL) + { + type = apol_context_get_type(scon); + } + if (apol_str_appendf(&con_str, &len, "%s:%s:%s", user, role, type) < 0) + { + return -1; + } + if (r->mls) + { + const apol_mls_range_t *apol_range = NULL; + char *range = NULL; + if ((apol_range = apol_context_get_range(r->replcon)) == NULL) + { + apol_range = apol_context_get_range(scon); + } + if ((range = apol_mls_range_render(NULL, apol_range)) == NULL || apol_str_appendf(&con_str, &len, ":%s", range) < 0) + { + free(range); + free(con_str); + return -1; + } + free(range); + } + + if (r->verbose) + { + char *lcon = NULL, *rcon = NULL; + if (r->mls) + { + lcon = apol_context_render(NULL, r->replcon); + rcon = apol_context_render(NULL, scon); + } + else + { + if (asprintf(&lcon, "%s:%s:%s", + apol_context_get_user(r->replcon), + apol_context_get_role(r->replcon), apol_context_get_type(r->replcon)) < 0) + { + lcon = NULL; + } + if (asprintf(&rcon, "%s:%s:%s", + apol_context_get_user(scon), apol_context_get_role(scon), apol_context_get_type(scon)) < 0) + { + rcon = NULL; + } + } + if (lcon == NULL || rcon == NULL) + { + free(lcon); + free(rcon); + return -1; + } + printf("%s: %s --> %s\n", e->path(), lcon, rcon); + free(lcon); + free(rcon); + } + + // until there is a way to create a security_context_t from a + // char *, simply perform the implicit cast below + if (replcon_lsetfilecon(e->path(), con_str) != 0) + { + cerr << "Could not set context " << con_str << " for file " << e->path() << "." << endl; + free(con_str); + return -1; + } + + free(con_str); + return 0; +} + +int main(int argc, char *argv[]) +{ + int optc; + struct replcon_info r; + + r.verbose = false; + r.replcon = NULL; + sefs_query *query = new sefs_query(); + + apol_context_t *context = NULL; + try + { + while ((optc = getopt_long(argc, argv, "t:u:r:m:p:c:RvhV", longopts, NULL)) != -1) + { + switch (optc) + { + case 't': + if (context == NULL) + { + query->type(optarg, false); + } + break; + case 'u': + if (context == NULL) + { + query->user(optarg); + } + break; + case 'r': + if (context == NULL) + { + query->role(optarg); + } + break; + case 'm': + if (context == NULL) + { + query->range(optarg, APOL_QUERY_EXACT); + } + break; + case OPTION_CONTEXT: + if ((context = apol_context_create_from_literal(optarg)) == NULL) + { + cerr << "Could not create source context." << endl; + throw runtime_error(strerror(errno)); + } + break; + case 'p': + query->path(optarg); + break; + case 'c': + query->objectClass(optarg); + break; + case 'R': + query->regex(true); + break; + case 'v': + r.verbose = true; + break; + case 'h': // help + usage(argv[0], false); + exit(0); + case 'V': // version + cout << "replcon " << VERSION << endl << COPYRIGHT_INFO << endl; + exit(0); + default: + usage(argv[0], true); + exit(1); + } + if (context != NULL) + { + query->user(apol_context_get_user(context)); + query->role(apol_context_get_role(context)); + query->type(apol_context_get_type(context), false); + if (apol_context_get_range(context) != NULL) + { + char *rng = apol_mls_range_render(NULL, apol_context_get_range(context)); + query->range(rng, APOL_QUERY_EXACT); + free(rng); + } + else + { + query->range(NULL, APOL_QUERY_EXACT); + } + } + } + } + catch(bad_alloc) + { + cerr << strerror(errno) << endl; + apol_context_destroy(&context); + delete query; + exit(-1); + } + apol_context_destroy(&context); + + if (optind + 2 != argc) + { + usage(argv[0], 1); + delete query; + exit(-1); + } + + sefs_fclist *fclist = NULL; + try + { + fclist = new sefs_filesystem(argv[optind + 1], NULL, NULL); + r.mls = fclist->isMLS(); + + if ((r.replcon = apol_context_create_from_literal(argv[optind])) == NULL) + { + cerr << "Could not create replacement context." << endl; + throw runtime_error(strerror(errno)); + } + + if (fclist->runQueryMap(query, replace_entry, &r) < 0) + { + throw runtime_error(strerror(errno)); + } + } + catch(...) + { + delete query; + delete fclist; + apol_context_destroy(&(r.replcon)); + exit(-1); + } + + delete query; + delete fclist; + apol_context_destroy(&(r.replcon)); + return 0; +} diff --git a/secmds/seinfo.c b/secmds/seinfo.c new file mode 100644 index 0000000..fdf23e9 --- /dev/null +++ b/secmds/seinfo.c @@ -0,0 +1,2337 @@ +/** + * @file + * + * Command line tool for looking at a SELinux policy + * and getting various component elements and statistics. + * + * @author Frank Mayer mayerf@tresys.com + * @author Jeremy A. Mowery jmowery@tresys.com + * @author David Windsor dwindsor@tresys.com + * @author Steve Lawrence slawrence@tresys.com + * + * Copyright (C) 2003-2008 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +/* libapol */ +#include <apol/policy.h> +#include <apol/policy-query.h> +#include <apol/render.h> +#include <apol/util.h> +#include <apol/vector.h> + +/* libqpol */ +#include <qpol/policy.h> +#include <qpol/util.h> + +/* other */ +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <getopt.h> + +#define COPYRIGHT_INFO "Copyright (C) 2003-2007 Tresys Technology, LLC" + +/* placeholder for empty set in constraint statements */ +#define CONSTRAIN_NULL_SET "<empty set>" + +static char *policy_file = NULL; + +static void print_type_attrs(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand); +static void print_attr_types(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand); +static void print_user_roles(FILE * fp, const qpol_user_t * user_datum, const apol_policy_t * policydb, const int expand); +static void print_role_types(FILE * fp, const qpol_role_t * role_datum, const apol_policy_t * policydb, const int expand); +static void print_bool_state(FILE * fp, const qpol_bool_t * bool_datum, const apol_policy_t * policydb, const int expand); +static void print_class_perms(FILE * fp, const qpol_class_t * class_datum, const apol_policy_t * policydb, const int expand); +static void print_cat_sens(FILE * fp, const qpol_cat_t * cat_datum, const apol_policy_t * policydb, const int expand); +static int qpol_cat_datum_compare(const void *datum1, const void *datum2, void *data); +static int qpol_level_datum_compare(const void *datum1, const void *datum2, void *data); + +enum opt_values +{ + OPT_SENSITIVITY = 256, OPT_CATEGORY, + OPT_INITIALSID, OPT_FS_USE, OPT_GENFSCON, + OPT_NETIFCON, OPT_NODECON, OPT_PORTCON, OPT_PROTOCOL, + OPT_PERMISSIVE, OPT_POLCAP, + OPT_ALL, OPT_STATS, OPT_CONSTRAIN +}; + +static struct option const longopts[] = { + {"class", optional_argument, NULL, 'c'}, + {"sensitivity", optional_argument, NULL, OPT_SENSITIVITY}, + {"category", optional_argument, NULL, OPT_CATEGORY}, + {"type", optional_argument, NULL, 't'}, + {"attribute", optional_argument, NULL, 'a'}, + {"role", optional_argument, NULL, 'r'}, + {"user", optional_argument, NULL, 'u'}, + {"bool", optional_argument, NULL, 'b'}, + {"constrain", no_argument, NULL, OPT_CONSTRAIN}, + {"initialsid", optional_argument, NULL, OPT_INITIALSID}, + {"fs_use", optional_argument, NULL, OPT_FS_USE}, + {"genfscon", optional_argument, NULL, OPT_GENFSCON}, + {"netifcon", optional_argument, NULL, OPT_NETIFCON}, + {"nodecon", optional_argument, NULL, OPT_NODECON}, + {"permissive", optional_argument, NULL, OPT_PERMISSIVE}, + {"polcap", optional_argument, NULL, OPT_POLCAP}, + {"portcon", optional_argument, NULL, OPT_PORTCON}, + {"protocol", required_argument, NULL, OPT_PROTOCOL}, + {"stats", no_argument, NULL, OPT_STATS}, + {"all", no_argument, NULL, OPT_ALL}, + {"line-breaks", no_argument, NULL, 'l'}, + {"expand", no_argument, NULL, 'x'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +/** + * Prints a message specifying program options and usage. + * + * @param program_name Name of the program + * @param brief Flag indicating whether brief usage + * information should be displayed + */ +void usage(const char *program_name, int brief) +{ + printf("Usage: %s [OPTIONS] [EXPRESSION] [POLICY ...]\n\n", program_name); + if (brief) { + printf("\tTry %s --help for more help.\n\n", program_name); + return; + } + printf("Print information about the components of a SELinux policy.\n\n"); + printf("EXPRESSIONS:\n"); + printf(" -c[NAME], --class[=NAME] print object classes\n"); + printf(" --sensitivity[=NAME] print sensitivities\n"); + printf(" --category[=NAME] print categories\n"); + printf(" -t[NAME], --type[=NAME] print types (no aliases or attributes)\n"); + printf(" -a[NAME], --attribute[=NAME] print type attributes\n"); + printf(" -r[NAME], --role[=NAME] print roles\n"); + printf(" -u[NAME], --user[=NAME] print users\n"); + printf(" -b[NAME], --bool[=NAME] print conditional booleans\n"); + printf(" --constrain print constrain statements\n"); + printf(" --initialsid[=NAME] print initial SIDs\n"); + printf(" --fs_use[=TYPE] print fs_use statements\n"); + printf(" --genfscon[=TYPE] print genfscon statements\n"); + printf(" --netifcon[=NAME] print netif contexts\n"); + printf(" --nodecon[=ADDR] print node contexts\n"); + printf(" --permissive print permissive types\n"); + printf(" --polcap print policy capabilities\n"); + printf(" --portcon[=PORT] print port contexts\n"); + printf(" --protocol=PROTO specify a protocol for portcons\n"); + printf(" --all print all of the above\n"); + printf("OPTIONS:\n"); + printf(" -x, --expand show more info for specified components\n"); + printf(" --stats print useful policy statistics\n"); + printf(" -l, --line-breaks print line breaks in constrain statements\n"); + printf(" -h, --help print this help text and exit\n"); + printf(" -V, --version print version information and exit\n"); + printf("\n"); + printf("For component options, if NAME is provided, then only show info for\n"); + printf("NAME. Specifying a name is most useful when used with the -x option.\n"); + printf("If no option is provided, display useful policy statistics (-s).\n"); + printf("\n"); + printf("The default source policy, or if that is unavailable the default binary\n"); + printf("policy, will be opened if no policy is provided.\n\n"); +} + +/** + * Prints statistics regarding a policy's components. + * + * @param fp Reference to a file to which to print + * policy statistics + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_stats(FILE * fp, const apol_policy_t * policydb) +{ + int retval = -1; + unsigned int n_perms = 0; + qpol_iterator_t *iter = NULL; + apol_type_query_t *type_query = NULL; + apol_attr_query_t *attr_query = NULL; + apol_perm_query_t *perm_query = NULL; + apol_vector_t *perms = NULL, *v = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + char *str = NULL; + size_t n_types = 0, n_attrs = 0; + size_t n_classes = 0, n_users = 0, n_roles = 0, n_bools = 0, n_conds = 0, n_levels = 0, + n_cats = 0, n_portcons = 0, n_netifcons = 0, n_nodecons = 0, n_fsuses = 0, + n_genfscons = 0, n_allows = 0, n_neverallows = 0, n_auditallows = 0, n_dontaudits = 0, + n_typetrans = 0, n_typechanges = 0, n_typemembers = 0, n_isids = 0, n_roleallows = 0, + n_roletrans = 0, n_rangetrans = 0, n_constr = 0, n_vtrans = 0, n_permissives = 0, + n_polcaps = 0; + + assert(policydb != NULL); + + fprintf(fp, "\nStatistics for policy file: %s\n", policy_file); + + if (!(str = apol_policy_get_version_type_mls_str(policydb))) + goto cleanup; + + fprintf(fp, "Policy Version & Type: "); + fprintf(fp, "%s\n", str); + free(str); + + if (qpol_policy_get_class_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_classes)) + goto cleanup; + qpol_iterator_destroy(&iter); + + perm_query = apol_perm_query_create(); + if (!perm_query) + goto cleanup; + + /* Match all perms */ + if (apol_perm_get_by_query(policydb, perm_query, &perms)) + goto cleanup; + + n_perms = apol_vector_get_size(perms); + apol_perm_query_destroy(&perm_query); + apol_vector_destroy(&perms); + fprintf(fp, "\n Classes: %7zd Permissions: %7d\n", n_classes, n_perms); + + /* sensitivities/categories */ + if (qpol_policy_get_level_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_levels)) + goto cleanup; + + qpol_iterator_destroy(&iter); + if (qpol_policy_get_cat_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_cats)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Sensitivities: %7zd Categories: %7zd\n", n_levels, n_cats); + + /* types */ + type_query = apol_type_query_create(); + if (!type_query) + goto cleanup; + if (apol_type_get_by_query(policydb, type_query, &v) < 0) + goto cleanup; + + n_types = apol_vector_get_size(v); + apol_type_query_destroy(&type_query); + apol_vector_destroy(&v); + + attr_query = apol_attr_query_create(); + if (!attr_query) + goto cleanup; + if (apol_attr_get_by_query(policydb, attr_query, &v) < 0) + goto cleanup; + + n_attrs = apol_vector_get_size(v); + apol_attr_query_destroy(&attr_query); + apol_vector_destroy(&v); + + fprintf(fp, " Types: %7zd Attributes: %7zd\n", n_types, n_attrs); + qpol_iterator_destroy(&iter); + + /* users/roles */ + if (qpol_policy_get_user_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_users)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_role_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_roles)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Users: %7zd Roles: %7zd\n", n_users, n_roles); + + /* booleans/cond. exprs. */ + if (qpol_policy_get_bool_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_bools)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_cond_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_conds)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Booleans: %7zd Cond. Expr.: %7zd\n", n_bools, n_conds); + + /* allow/neverallow */ + if (qpol_policy_get_avrule_iter(q, QPOL_RULE_ALLOW, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_allows)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_has_capability(q, QPOL_CAP_NEVERALLOW)) { + if (qpol_policy_get_avrule_iter(q, QPOL_RULE_NEVERALLOW, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_neverallows)) + goto cleanup; + } else { + n_neverallows = 0; + } + qpol_iterator_destroy(&iter); + fprintf(fp, " Allow: %7zd Neverallow: %7zd\n", n_allows, n_neverallows); + + /* auditallow/dontaudit */ + if (qpol_policy_get_avrule_iter(q, QPOL_RULE_AUDITALLOW, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_auditallows)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_avrule_iter(q, QPOL_RULE_DONTAUDIT, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_dontaudits)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Auditallow: %7zd Dontaudit: %7zd\n", n_auditallows, n_dontaudits); + + /* type_transition/type_change */ + if (qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_TRANS, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_typetrans)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_CHANGE, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_typechanges)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Type_trans: %7zd Type_change: %7zd\n", n_typetrans, n_typechanges); + + /* type_member/role allow */ + if (qpol_policy_get_terule_iter(q, QPOL_RULE_TYPE_MEMBER, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_typemembers)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_role_allow_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_roleallows)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Type_member: %7zd Role allow: %7zd\n", n_typemembers, n_roleallows); + + /* role_trans/range_trans */ + if (qpol_policy_get_role_trans_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_roletrans)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_range_trans_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_rangetrans)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Role_trans: %7zd Range_trans: %7zd\n", n_roletrans, n_rangetrans); + + /* constraints/validatetrans */ + if (qpol_policy_get_constraint_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_constr)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_validatetrans_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_vtrans)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Constraints: %7zd Validatetrans: %7zd\n", n_constr, n_vtrans); + + /* isids/fs_use */ + if (qpol_policy_get_isid_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_isids)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_fs_use_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_fsuses)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Initial SIDs: %7zd Fs_use: %7zd\n", n_isids, n_fsuses); + + /* genfscon/portcon */ + if (qpol_policy_get_genfscon_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_genfscons)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_portcon_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_portcons)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Genfscon: %7zd Portcon: %7zd\n", n_genfscons, n_portcons); + + /* netifcon/nodecon */ + if (qpol_policy_get_netifcon_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_netifcons)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_nodecon_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_nodecons)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Netifcon: %7zd Nodecon: %7zd\n", n_netifcons, n_nodecons); + + /* permissives/polcaps */ + if (qpol_policy_get_permissive_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_permissives)) + goto cleanup; + qpol_iterator_destroy(&iter); + if (qpol_policy_get_polcap_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_polcaps)) + goto cleanup; + qpol_iterator_destroy(&iter); + fprintf(fp, " Permissives: %7zd Polcap: %7zd\n", n_permissives, n_polcaps); + + fprintf(fp, "\n"); + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + apol_type_query_destroy(&type_query); + apol_attr_query_destroy(&attr_query); + apol_perm_query_destroy(&perm_query); + apol_vector_destroy(&v); + apol_vector_destroy(&perms); + return retval; +} + +/** + * Prints statistics regarding a policy's object classes. + * If this function is given a name, it will attempt to + * print statistics about a particular object class; otherwise + * the function prints statistics about all of the policy's object + * classes. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to an object class' name; if NULL, + * all object classes will be considered + * @param expand Flag indicating whether to print object class + * permissions + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_classes(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + qpol_iterator_t *iter = NULL; + size_t n_classes = 0; + const qpol_class_t *class_datum = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + if (name != NULL) { + if (qpol_policy_get_class_by_name(q, name, &class_datum)) + goto cleanup; + print_class_perms(fp, class_datum, policydb, expand); + } else { + if (qpol_policy_get_class_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_classes)) + goto cleanup; + fprintf(fp, "Object classes: %d\n", (int)n_classes); + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&class_datum)) + goto cleanup; + print_class_perms(fp, class_datum, policydb, expand); + } + qpol_iterator_destroy(&iter); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's types. + * If this function is given a name, it will attempt to + * print statistics about a particular type; otherwise + * the function prints statistics about all of the policy's types. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a type's name; if NULL, + * all object classes will be considered + * @param expand Flag indicating whether to print each type's attributes + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_types(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + const qpol_type_t *type_datum = NULL; + qpol_iterator_t *iter = NULL; + apol_vector_t *type_vector = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t vector_sz; + + if (name != NULL) { + if (qpol_policy_get_type_by_name(q, name, &type_datum)) + goto cleanup; + } + + /* Find the number of types in the policy */ + if (apol_type_get_by_query(policydb, NULL, &type_vector)) + goto cleanup; + vector_sz = apol_vector_get_size(type_vector); + apol_vector_destroy(&type_vector); + + if (name == NULL) { + fprintf(fp, "\nTypes: %zd\n", vector_sz); + } + + /* if name was provided, only print that name */ + if (name != NULL) { + if (qpol_policy_get_type_by_name(q, name, &type_datum)) + goto cleanup; + print_type_attrs(fp, type_datum, policydb, expand); + } else { + if (qpol_policy_get_type_iter(q, &iter)) + goto cleanup; + /* Print all type names */ + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type_datum)) + goto cleanup; + print_type_attrs(fp, type_datum, policydb, expand); + } + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's attributes. + * If this function is given a name, it will attempt to + * print statistics about a particular attribute; otherwise + * the function prints statistics about all of the policy's + * attributes. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to an attribute's name; if NULL, + * all object classes will be considered + * @param expand Flag indicating whether to print each attribute's + * allowed types + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_attribs(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + apol_attr_query_t *attr_query = NULL; + apol_vector_t *v = NULL; + const qpol_type_t *type_datum = NULL; + size_t n_attrs, i; + + /* we are only printing information about 1 attribute */ + if (name != NULL) { + attr_query = apol_attr_query_create(); + if (!attr_query) + goto cleanup; + if (apol_attr_query_set_attr(policydb, attr_query, name)) + goto cleanup; + if (apol_attr_get_by_query(policydb, attr_query, &v)) + goto cleanup; + apol_attr_query_destroy(&attr_query); + if (apol_vector_get_size(v) == 0) { + apol_vector_destroy(&v); + ERR(policydb, "Provided attribute (%s) is not a valid attribute name.", name); + goto cleanup; + } + + type_datum = apol_vector_get_element(v, (size_t) 0); + print_attr_types(fp, type_datum, policydb, expand); + } else { + attr_query = apol_attr_query_create(); + if (!attr_query) + goto cleanup; + if (apol_attr_get_by_query(policydb, attr_query, &v)) + goto cleanup; + apol_attr_query_destroy(&attr_query); + n_attrs = apol_vector_get_size(v); + + fprintf(fp, "\nAttributes: %zd\n", n_attrs); + for (i = 0; i < n_attrs; i++) { + /* get qpol_type_t* item from vector */ + type_datum = (qpol_type_t *) apol_vector_get_element(v, (size_t) i); + if (!type_datum) + goto cleanup; + print_attr_types(fp, type_datum, policydb, expand); + } + } + apol_vector_destroy(&v); + + retval = 0; + cleanup: + apol_attr_query_destroy(&attr_query); + apol_vector_destroy(&v); + return retval; +} + +/** + * Prints statistics regarding a policy's roles. + * If this function is given a name, it will attempt to + * print statistics about a particular role; otherwise + * the function prints statistics about all of the policy's roles. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to an role's name; if NULL, + * all roles will be considered + * @param expand Flag indicating whether to print valid users + * for each role + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_roles(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + const qpol_role_t *role_datum = NULL; + qpol_iterator_t *iter = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_roles = 0; + + if (name != NULL) { + if (qpol_policy_get_role_by_name(q, name, &role_datum)) + goto cleanup; + print_role_types(fp, role_datum, policydb, expand); + } else { + if (qpol_policy_get_role_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_roles)) + goto cleanup; + fprintf(fp, "\nRoles: %d\n", (int)n_roles); + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&role_datum)) + goto cleanup; + print_role_types(fp, role_datum, policydb, expand); + } + qpol_iterator_destroy(&iter); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's booleans. + * If this function is given a name, it will attempt to + * print statistics about a particular boolean; otherwise + * the function prints statistics about all of the policy's booleans. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a boolean's name; if NULL, + * all booleans will be considered + * @param expand Flag indicating whether to print each + * boolean's default state + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_booleans(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + qpol_bool_t *bool_datum = NULL; + qpol_iterator_t *iter = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_bools = 0; + + if (name != NULL) { + if (qpol_policy_get_bool_by_name(q, name, &bool_datum)) + goto cleanup; + print_bool_state(fp, bool_datum, policydb, expand); + } else { + if (qpol_policy_get_bool_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_bools)) + goto cleanup; + fprintf(fp, "\nConditional Booleans: %zd\n", n_bools); + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&bool_datum)) + goto cleanup; + print_bool_state(fp, bool_datum, policydb, expand); + } + qpol_iterator_destroy(&iter); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's users. + * If this function is given a name, it will attempt to + * print statistics about a particular user; otherwise + * the function prints statistics about all of the policy's + * users. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a user's name; if NULL, + * all users will be considered + * @param expand Flag indicating whether to print each user's + * roles + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_users(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + qpol_iterator_t *iter = NULL; + const qpol_user_t *user_datum = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_users = 0; + + if (name != NULL) { + if (qpol_policy_get_user_by_name(q, name, &user_datum)) + goto cleanup; + print_user_roles(fp, user_datum, policydb, expand); + } else { + if (qpol_policy_get_user_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_users)) + goto cleanup; + fprintf(fp, "\nUsers: %d\n", (int)n_users); + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&user_datum)) + goto cleanup; + print_user_roles(fp, user_datum, policydb, expand); + } + qpol_iterator_destroy(&iter); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's MLS sensitivities. + * If this function is given a name, it will attempt to + * print statistics about a particular sensitivity; otherwise + * the function prints statistics about all of the policy's + * sensitivities. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a sensitivity's name; if NULL, + * all sensitivities will be considered + * @param expand Flag indicating whether to print each + * sensitivity's categories + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_sens(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + size_t i; + char *tmp = NULL; + const char *lvl_name = NULL; + apol_level_query_t *query = NULL; + apol_vector_t *v = NULL; + const qpol_level_t *level = NULL; + apol_mls_level_t *ap_mls_lvl = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + + query = apol_level_query_create(); + if (!query) { + ERR(policydb, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_level_query_set_sens(policydb, query, name)) + goto cleanup; + if (apol_level_get_by_query(policydb, query, &v)) + goto cleanup; + + if (!name) + fprintf(fp, "\nSensitivities: %zd\n", apol_vector_get_size(v)); + for (i = 0; i < apol_vector_get_size(v); i++) { + level = apol_vector_get_element(v, i); + if (qpol_level_get_name(q, level, &lvl_name)) + goto cleanup; + fprintf(fp, " %s\n", lvl_name); + if (expand) { + ap_mls_lvl = (apol_mls_level_t *) apol_mls_level_create_from_qpol_level_datum(policydb, level); + tmp = apol_mls_level_render(policydb, ap_mls_lvl); + apol_mls_level_destroy(&ap_mls_lvl); + if (!tmp) + goto cleanup; + fprintf(fp, " level %s\n", tmp); + free(tmp); + } + } + + if (name && !apol_vector_get_size(v)) { + ERR(policydb, "Provided sensitivity (%s) is not a valid sensitivity name.", name); + goto cleanup; + } + + retval = 0; + cleanup: + apol_level_query_destroy(&query); + apol_vector_destroy(&v); + return retval; +} + +/** + * Prints statistics regarding a policy's MLS categories. + * If this function is given a name, it will attempt to + * print statistics about a particular category; otherwise + * the function prints statistics about all of the policy's + * categories. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a MLS category's name; if NULL, + * all categories will be considered + * @param expand Flag indicating whether to print each + * category's sensitivities + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_cats(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = 0; + apol_cat_query_t *query = NULL; + apol_vector_t *v = NULL; + const qpol_cat_t *cat_datum = NULL; + size_t i, n_cats; + + query = apol_cat_query_create(); + if (!query) { + ERR(policydb, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_cat_query_set_cat(policydb, query, name)) + goto cleanup; + if (apol_cat_get_by_query(policydb, query, &v)) + goto cleanup; + n_cats = apol_vector_get_size(v); + apol_vector_sort(v, &qpol_cat_datum_compare, (void *)policydb); + + if (!name) + fprintf(fp, "Categories: %zd\n", n_cats); + for (i = 0; i < n_cats; i++) { + cat_datum = apol_vector_get_element(v, i); + if (!cat_datum) + goto cleanup; + print_cat_sens(fp, cat_datum, policydb, expand); + + } + + if (name && !n_cats) { + ERR(policydb, "Provided category (%s) is not a valid category name.", name); + goto cleanup; + } + + retval = 0; + cleanup: + apol_cat_query_destroy(&query); + apol_vector_destroy(&v); + return retval; +} + +/** + * Prints statistics regarding a policy's fs_use statements. + * If this function is given a name, it will attempt to + * print statistics about a particular filesystem; otherwise + * the function prints statistics about all of the policy's + * fs_use statements. + * + * @param fp Reference to a file to which to print statistics + * @param type Reference to the name of a file system type; if NULL, + * all file system types will be considered + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_fsuse(FILE * fp, const char *type, const apol_policy_t * policydb) +{ + int retval = -1; + char *tmp = NULL; + apol_fs_use_query_t *query = NULL; + apol_vector_t *v = NULL; + const qpol_fs_use_t *fs_use = NULL; + size_t i; + + query = apol_fs_use_query_create(); + if (!query) { + ERR(policydb, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_fs_use_query_set_filesystem(policydb, query, type)) + goto cleanup; + if (apol_fs_use_get_by_query(policydb, query, &v)) + goto cleanup; + + if (!type) + fprintf(fp, "\nFs_use: %zd\n", apol_vector_get_size(v)); + + for (i = 0; i < apol_vector_get_size(v); i++) { + fs_use = apol_vector_get_element(v, i); + tmp = apol_fs_use_render(policydb, fs_use); + if (!tmp) + goto cleanup; + fprintf(fp, " %s\n", tmp); + free(tmp); + } + if (type && !apol_vector_get_size(v)) + ERR(policydb, "No fs_use statement for filesystem of type %s.", type); + + retval = 0; + cleanup: + apol_fs_use_query_destroy(&query); + apol_vector_destroy(&v); + return retval; +} + +/** + * Prints statistics regarding a policy's genfscons. + * If this function is given a name, it will attempt to + * print statistics about a particular genfscon; otherwise + * the function prints statistics about all of the policy's + * genfscons. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a genfscon's type; if NULL, + * all genfscons will be considered + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_genfscon(FILE * fp, const char *type, const apol_policy_t * policydb) +{ + int retval = -1; + size_t i; + char *tmp = NULL; + apol_genfscon_query_t *query = NULL; + apol_vector_t *v = NULL; + qpol_genfscon_t *genfscon = NULL; + + query = apol_genfscon_query_create(); + if (!query) { + ERR(policydb, "%s", strerror(ENOMEM)); + goto cleanup; + } + + if (apol_genfscon_query_set_filesystem(policydb, query, type)) + goto cleanup; + if (apol_genfscon_get_by_query(policydb, query, &v)) + goto cleanup; + + if (!type) + fprintf(fp, "\nGenfscon: %zd\n", apol_vector_get_size(v)); + + for (i = 0; i < apol_vector_get_size(v); i++) { + genfscon = (qpol_genfscon_t *) apol_vector_get_element(v, i); + tmp = apol_genfscon_render(policydb, genfscon); + if (!tmp) + goto cleanup; + fprintf(fp, " %s\n", tmp); + free(tmp); + } + + if (type && !apol_vector_get_size(v)) + ERR(policydb, "No genfscon statement for filesystem of type %s.", type); + + retval = 0; + cleanup: + apol_genfscon_query_destroy(&query); + apol_vector_destroy(&v); + + return retval; +} + +/** + * Prints statistics regarding a policy's netifcons. + * If this function is given a name, it will attempt to + * print statistics about a particular netifcon; otherwise + * the function prints statistics about all of the policy's + * netifcons. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a network interface's name; if NULL, + * all netifcons will be considered + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_netifcon(FILE * fp, const char *name, const apol_policy_t * policydb) +{ + int retval = -1; + char *tmp; + const qpol_netifcon_t *netifcon = NULL; + qpol_iterator_t *iter = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_netifcons = 0; + + if (name != NULL) { + if (qpol_policy_get_netifcon_by_name(q, name, &netifcon)) + goto cleanup; + tmp = apol_netifcon_render(policydb, netifcon); + if (!tmp) + goto cleanup; + fprintf(fp, " %s\n", tmp); + free(tmp); + } else { + if (qpol_policy_get_netifcon_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_netifcons)) + goto cleanup; + fprintf(fp, "\nNetifcon: %zd\n", n_netifcons); + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&netifcon)) + goto cleanup; + tmp = apol_netifcon_render(policydb, netifcon); + if (!tmp) + goto cleanup; + fprintf(fp, " %s\n", tmp); + free(tmp); + } + qpol_iterator_destroy(&iter); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's nodecons. + * If this function is given a name, it will attempt to + * print statistics about a particular nodecon; otherwise + * the function prints statistics about all of the policy's + * nodecons. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a textually represented IP address; + * if NULL, all nodecons will be considered + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_nodecon(FILE * fp, const char *addr, const apol_policy_t * policydb) +{ + int retval = -1, protocol; + char *tmp = NULL; + uint32_t address[4] = { 0, 0, 0, 0 }; + apol_nodecon_query_t *query = NULL; + apol_vector_t *v = NULL; + qpol_nodecon_t *nodecon = NULL; + size_t n_nodecons = 0, i; + + query = apol_nodecon_query_create(); + if (!query) { + ERR(policydb, "%s", strerror(ENOMEM)); + goto cleanup; + } + + /* address needs to be in a libapol-understandable format */ + if (addr) { + protocol = apol_str_to_internal_ip(addr, address); + if (protocol < 0) { + ERR(policydb, "%s", "Unable to parse IP address"); + goto cleanup; + } + if (apol_nodecon_query_set_addr(policydb, query, address, protocol)) + goto cleanup; + if (apol_nodecon_query_set_protocol(policydb, query, protocol)) + goto cleanup; + } + + if (apol_nodecon_get_by_query(policydb, query, &v)) + goto cleanup; + + n_nodecons = apol_vector_get_size(v); + + if (!addr) { + fprintf(fp, "Nodecon: %zd\n", n_nodecons); + } else if (!n_nodecons) { + ERR(policydb, "No matching nodecon for address %s.", addr); + retval = 1; + goto cleanup; + } + + for (i = 0; i < n_nodecons; i++) { + nodecon = apol_vector_get_element(v, i); + tmp = apol_nodecon_render(policydb, nodecon); + if (!tmp) + goto cleanup; + fprintf(fp, " %s\n", tmp); + free(tmp); + } + + retval = 0; + cleanup: + apol_nodecon_query_destroy(&query); + apol_vector_destroy(&v); + return retval; +} + +/** + * Prints statistics regarding a policy's portcons. + * If this function is given a name, it will attempt to + * print statistics about a particular portcon; otherwise + * the function prints statistics about all of the policy's + * portcons. + * + * @param fp Reference to a file to which to print statistics + * @param num Reference to a port number; if NULL, + * all ports will be considered + * @param protocol Reference to the name of a ISO 7498-1 + * transport layer protocol used to communicate over a port + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_portcon(FILE * fp, const char *num, const char *protocol, const apol_policy_t * policydb) +{ + int retval = -1; + const qpol_portcon_t *portcon = NULL; + qpol_iterator_t *iter = NULL; + uint16_t low_port, high_port; + uint8_t ocon_proto, proto = 0; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_portcons; + char *tmp = NULL; + + if (protocol) { + if (!strcmp(protocol, "tcp")) + proto = IPPROTO_TCP; + else if (!strcmp(protocol, "udp")) + proto = IPPROTO_UDP; + else { + ERR(policydb, "Unable to get portcon by protocol: bad protocol %s.", protocol); + goto cleanup; + } + } + if (qpol_policy_get_portcon_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_portcons)) + goto cleanup; + if (!num) + fprintf(fp, "\nPortcon: %zd\n", n_portcons); + + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&portcon)) + goto cleanup; + if (qpol_portcon_get_low_port(q, portcon, &low_port)) + goto cleanup; + if (qpol_portcon_get_high_port(q, portcon, &high_port)) + goto cleanup; + if (qpol_portcon_get_protocol(q, portcon, &ocon_proto)) + goto cleanup; + if (num) { + if (atoi(num) < low_port || atoi(num) > high_port) + continue; + } + if (protocol) { + if (ocon_proto != proto) + continue; + } + fprintf(fp, " %s\n", (tmp = apol_portcon_render(policydb, portcon))); + free(tmp); + tmp = NULL; + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's initial SIDs. + * If this function is given a name, it will attempt to + * print statistics about a particular initial SID; otherwise + * the function prints statistics about all of the policy's + * initial SIDs. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a SID name; if NULL, + * all initial SIDs will be considered + * @param expand Flag indicating whether to print each + * initial SID's security context + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_isids(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + apol_isid_query_t *query = NULL; + apol_vector_t *v = NULL; + const qpol_isid_t *isid = NULL; + const qpol_context_t *ctxt = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t i, n_isids = 0; + char *tmp = NULL; + const char *isid_name = NULL; + + query = apol_isid_query_create(); + if (!query) { + ERR(policydb, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_isid_query_set_name(policydb, query, name)) + goto cleanup; + if (apol_isid_get_by_query(policydb, query, &v)) + goto cleanup; + n_isids = apol_vector_get_size(v); + + if (!name) + fprintf(fp, "\nInitial SID: %zd\n", n_isids); + + for (i = 0; i < n_isids; i++) { + isid = apol_vector_get_element(v, i); + if (qpol_isid_get_name(q, isid, &isid_name)) + goto cleanup; + if (!expand) { + fprintf(fp, " %s\n", isid_name); + } else { + if (qpol_isid_get_context(q, isid, &ctxt)) + goto cleanup; + tmp = apol_qpol_context_render(policydb, ctxt); + if (!tmp) + goto cleanup; + fprintf(fp, "%20s: %s\n", isid_name, tmp); + free(tmp); + } + } + + if (name && !n_isids) { + ERR(policydb, "Provided initial SID name (%s) is not a valid name.", name); + goto cleanup; + } + + retval = 0; + cleanup: + apol_isid_query_destroy(&query); + apol_vector_destroy(&v); + return retval; +} + +/** + * Prints statistics regarding a policy's permissives. + * If this function is given a name, it will attempt to + * print statistics about a particular permissive; otherwise + * the function prints statistics about all of the policy's permissives. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a permissives name; if NULL, + * all permissives will be considered + * @param expand Flag indicating whether to print each + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ +static int print_permissives(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + const char *tmp = NULL; + const qpol_permissive_t *permissive_datum = NULL; + qpol_iterator_t *iter = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_permissives = 0; + + if (qpol_policy_get_permissive_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_permissives)) + goto cleanup; + fprintf(fp, "\nPermissive Types: %zd\n", n_permissives); + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&permissive_datum)) + goto cleanup; + if (qpol_permissive_get_name(q, permissive_datum, &tmp)) + goto cleanup; + if (!tmp) + goto cleanup; + fprintf(fp, " %s\n", tmp); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +/** + * Prints statistics regarding a policy's capabilities. + * If this function is given a name, it will attempt to + * print statistics about a particular capability; otherwise + * the function prints statistics about all of the policy's capabilities. + * + * @param fp Reference to a file to which to print statistics + * @param name Reference to a policy capability name; if NULL, + * all capabilities will be considered + * @param expand Flag indicating whether to print each + * @param policydb Reference to a policy + * + * @return 0 on success, < 0 on error. + */ + +static int print_polcaps(FILE * fp, const char *name, int expand, const apol_policy_t * policydb) +{ + int retval = -1; + const char *tmp = NULL; + qpol_polcap_t *polcap_datum = NULL; + qpol_iterator_t *iter = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_polcaps = 0; + + if (qpol_policy_get_polcap_iter(q, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_polcaps)) + goto cleanup; + fprintf(fp, "\nPolicy Capabilities: %zd\n", n_polcaps); + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&polcap_datum)) + goto cleanup; + if (qpol_polcap_get_name(q, polcap_datum, &tmp)) + goto cleanup; + if (!tmp) + goto cleanup; + fprintf(fp, " %s\n", tmp); + } + + retval = 0; + cleanup: + qpol_iterator_destroy(&iter); + return retval; +} + +static const char *get_attr_string(int attr) +{ + const char *string = ""; + switch (attr) + { + case QPOL_CEXPR_SYM_USER: + string = "u1"; + break; + case QPOL_CEXPR_SYM_ROLE: + string = "r1"; + break; + case QPOL_CEXPR_SYM_TYPE: + string = "t1"; + break; + + case QPOL_CEXPR_SYM_USER+QPOL_CEXPR_SYM_TARGET: + string = "u2"; + break; + case QPOL_CEXPR_SYM_ROLE+QPOL_CEXPR_SYM_TARGET: + string = "r2"; + break; + case QPOL_CEXPR_SYM_TYPE+QPOL_CEXPR_SYM_TARGET: + string = "t2"; + break; + + case QPOL_CEXPR_SYM_USER+QPOL_CEXPR_SYM_XTARGET: + string = "u3"; + break; + case QPOL_CEXPR_SYM_ROLE+QPOL_CEXPR_SYM_XTARGET: + string = "r3"; + break; + case QPOL_CEXPR_SYM_TYPE+QPOL_CEXPR_SYM_XTARGET: + string = "t3"; + break; + + case QPOL_CEXPR_SYM_L1L2: + string = "l1 l2"; + break; + case QPOL_CEXPR_SYM_L1H2: + string = "l1 h2"; + break; + case QPOL_CEXPR_SYM_H1L2: + string = "h1 l2"; + break; + case QPOL_CEXPR_SYM_H1H2: + string = "h1 h2"; + break; + case QPOL_CEXPR_SYM_L1H1: + string = "l1 h1"; + break; + case QPOL_CEXPR_SYM_L2H2: + string = "l2 h2"; + break; + } + + return string; +} + +static const char *get_op_string(int op) +{ + char *string = ""; + + switch (op) + { + case QPOL_CEXPR_OP_EQ: + string = "=="; + break; + case QPOL_CEXPR_OP_NEQ: + string = "!="; + break; + case QPOL_CEXPR_OP_DOM: + string = "dom"; + break; + case QPOL_CEXPR_OP_DOMBY: + string = "domby"; + break; + case QPOL_CEXPR_OP_INCOMP: + string = "incomp"; + break; + } + + return string; +} + +static int print_constraints(FILE * fp, int expand, const apol_policy_t * policydb, int linebreaks) +{ + int retval = -1; + const char *class_name = NULL; + char *constrain_type; + char *perm_list = "No Perms Extracted"; + const qpol_constraint_expr_node_t *expr = NULL; + qpol_iterator_t *policy_iter = NULL; // Iterates over all constraints in a policy + qpol_iterator_t *perm_iter = NULL; // Iterates over permissions in a constraint + qpol_iterator_t *expr_iter = NULL; // Iterates over expression in a constraint + qpol_policy_t *q = apol_policy_get_qpol(policydb); + qpol_constraint_t *constraint = NULL; + const qpol_class_t *class; + size_t n_constraints = 0; + int expr_type = 0; + int sym_type = 0; // 'attr' in struct constraint_expr + int op = 0; + + if (qpol_policy_get_constraint_iter(q, &policy_iter) != 0) + { + ERR (policydb, "%s", "Policy constraint iterator not accessible"); + return retval; + } + if (qpol_iterator_get_size(policy_iter, &n_constraints) != 0) + { + ERR(policydb, "%s", "Policy size computation failed"); + goto cleanup; + } + + fprintf(fp, "\nConstraints: %zd\n", n_constraints); + + // Iterate through constraints + for (; qpol_iterator_end(policy_iter) == 0; qpol_iterator_next(policy_iter)) + { + constrain_type = ""; + if (qpol_iterator_get_item(policy_iter, (void **)&constraint) != 0) + { + ERR(policydb, "%s", "Can't get constraint from iterator\n"); + goto cleanup; + } + + if (qpol_constraint_get_class(q, constraint, &class) != 0) + { + ERR(policydb, "%s", "Can't get class from constraint\n"); + goto cleanup; + } + + if (qpol_class_get_name(q, class, &class_name) != 0) + { + ERR(policydb, "%s", "Can't get class name from constraint\n"); + goto cleanup; + } + + // Get expression, we need to look into it. + if (qpol_constraint_get_expr_iter (q, constraint, &expr_iter) != 0) + { + ERR(policydb, "%s", "Can't get expression from constraint\n"); + goto cleanup; + } + // Traverse the iterator to see if this is mlsconstrain + for (; qpol_iterator_end(expr_iter) == 0; qpol_iterator_next(expr_iter)) + { + if (qpol_iterator_get_item(expr_iter, (void **)&expr) != 0) + { + ERR(policydb, "%s", "Can't get expression from iterator\n"); + goto cleanup; + } + + if (qpol_constraint_expr_node_get_sym_type(q, expr, &sym_type) != 0) + { + ERR(policydb, "%s", "Can't get sym_type from expression\n"); + goto cleanup; + } + + if (sym_type >= QPOL_CEXPR_SYM_L1L2) + { + constrain_type = "mls"; + break; + } + } + + // print permissions + fprintf (fp, "%sconstrain { %s } { ", constrain_type, class_name); + + if (qpol_constraint_get_perm_iter (q, constraint, &perm_iter) != 0) + { + ERR(policydb, "%s", "Can't get permissions from constraint\n"); + goto cleanup; + } + + for (; qpol_iterator_end(perm_iter) == 0; qpol_iterator_next(perm_iter)) + { + if (qpol_iterator_get_item(perm_iter, (void **)&perm_list) != 0) + { + ERR(policydb, "%s", "Can't get permissions from iterator\n"); + goto cleanup; + } + + fprintf (fp, "%s ", perm_list); + free (perm_list); // Strdup created the string. + } + fprintf (fp, " } "); + + // dump RPN expressions + if (qpol_constraint_get_expr_iter (q, constraint, &expr_iter) != 0) + { + ERR(policydb, "%s", "Can't get expression from constraint\n"); + goto cleanup; + } + + fprintf (fp, "\n( "); + for (; qpol_iterator_end(expr_iter) == 0; qpol_iterator_next(expr_iter)) + { + qpol_iterator_t *names_iter = NULL; + + if (qpol_iterator_get_item(expr_iter, (void **)&expr) != 0) + { + ERR(policydb, "%s", "Can't get expression from iterator\n"); + goto cleanup; + } + + if (qpol_constraint_expr_node_get_op (q, expr, &op) != 0) + { + ERR(policydb, "%s", "Can't get op from expression\n"); + goto cleanup; + } + + if (qpol_constraint_expr_node_get_sym_type(q, expr, &sym_type) != 0) + { + ERR(policydb, "%s", "Can't get sym_type from expression\n"); + goto cleanup; + } + + if (qpol_constraint_expr_node_get_expr_type(q, expr, &expr_type) != 0) + { + ERR(policydb, "%s", "Can't get expr_type from expression\n"); + goto cleanup; + } + + if (linebreaks) + fprintf (fp, "\n\t"); + + if (expr_type == QPOL_CEXPR_TYPE_NOT) + { + fprintf (fp, " ! "); + } + if (expr_type == QPOL_CEXPR_TYPE_AND) + { + fprintf (fp, " && "); + } + if (expr_type == QPOL_CEXPR_TYPE_OR) + { + fprintf (fp, " || "); + } + if (expr_type == QPOL_CEXPR_TYPE_ATTR) + { + fprintf (fp, " %s ", get_attr_string(sym_type)); + fprintf (fp, "%s ", get_attr_string(sym_type | QPOL_CEXPR_SYM_TARGET)); + fprintf (fp, "%s ", get_op_string(op)); + } + if (expr_type == QPOL_CEXPR_TYPE_NAMES) + { + size_t name_size=0; + + fprintf (fp, " %s ", get_attr_string(sym_type)); + + if (qpol_constraint_expr_node_get_names_iter (q, expr, &names_iter) != 0) + { + ERR(policydb, "%s", "Can't get names iterator from expression\n"); + goto cleanup; + } + + if (qpol_iterator_get_size(names_iter, &name_size) != 0) + { + ERR(policydb, "%s", "Can't get size from names iterator\n"); + goto cleanup; + } + if (name_size > 0) + { + if (name_size > 1) + fprintf (fp, "{ "); + + for (; qpol_iterator_end(names_iter) == 0; qpol_iterator_next(names_iter)) + { + char *lname = NULL; + + if (qpol_iterator_get_item (names_iter, (void **)&lname) != 0) + { + ERR(policydb, "%s", "Can't get names from iterator\n"); + goto cleanup; + } + + fprintf (fp, "%s ", lname); + free (lname); + + } + if (name_size > 1) + fprintf (fp, "} "); + } else { + fprintf (fp, "%s ", CONSTRAIN_NULL_SET); + } + + fprintf (fp, "%s ", get_op_string(op)); + } + } + if (linebreaks) + fprintf (fp, "\n);\n\n"); + else + fprintf (fp, ");\n\n"); + } + + retval = 0; + +cleanup: // close and destroy iterators etc. + if (policy_iter != NULL) qpol_iterator_destroy(&policy_iter); + if (perm_iter != NULL) qpol_iterator_destroy(&perm_iter); + if (expr_iter != NULL) qpol_iterator_destroy(&expr_iter); + + return retval; +} + + +int main(int argc, char **argv) +{ + int classes, types, attribs, roles, users, all, expand, stats, rt, optc, isids, bools, sens, cats, fsuse, genfs, netif, + node, port, permissives, polcaps, constrain, linebreaks; + apol_policy_t *policydb = NULL; + apol_policy_path_t *pol_path = NULL; + apol_vector_t *mod_paths = NULL; + apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + + char *class_name, *type_name, *attrib_name, *role_name, *user_name, *isid_name, *bool_name, *sens_name, *cat_name, + *fsuse_type, *genfs_type, *netif_name, *node_addr, *permissive_name, *polcap_name, *port_num = NULL, *protocol = NULL; + + class_name = type_name = attrib_name = role_name = user_name = isid_name = bool_name = sens_name = cat_name = fsuse_type = + genfs_type = netif_name = node_addr = port_num = permissive_name = polcap_name = NULL; + classes = types = attribs = roles = users = all = expand = stats = isids = bools = sens = cats = fsuse = genfs = netif = + node = port = permissives = polcaps = constrain = linebreaks = 0; + while ((optc = getopt_long(argc, argv, "c::t::a::r::u::b::lxhV", longopts, NULL)) != -1) { + switch (optc) { + case 0: + break; + case 'c': /* classes */ + classes = 1; + if (optarg != 0) + class_name = optarg; + break; + case OPT_SENSITIVITY: + sens = 1; + if (optarg != 0) + sens_name = optarg; + break; + case OPT_CATEGORY: + cats = 1; + if (optarg != 0) + cat_name = optarg; + break; + case 't': /* types */ + types = 1; + if (optarg != 0) + type_name = optarg; + break; + case 'a': /* attributes */ + attribs = 1; + if (optarg != 0) + attrib_name = optarg; + break; + case 'r': /* roles */ + roles = 1; + if (optarg != 0) + role_name = optarg; + break; + case 'u': /* users */ + users = 1; + if (optarg != 0) + user_name = optarg; + break; + case 'b': /* conditional booleans */ + bools = 1; + if (optarg != 0) + bool_name = optarg; + break; + case OPT_INITIALSID: + isids = 1; + if (optarg != 0) + isid_name = optarg; + break; + case OPT_FS_USE: + fsuse = 1; + if (optarg != 0) + fsuse_type = optarg; + break; + case OPT_GENFSCON: + genfs = 1; + if (optarg != 0) + genfs_type = optarg; + break; + case OPT_NETIFCON: + netif = 1; + if (optarg != 0) + netif_name = optarg; + break; + case OPT_NODECON: + node = 1; + if (optarg != 0) + node_addr = optarg; + break; + case OPT_PERMISSIVE: + permissives = 1; + if (optarg != 0) + permissive_name = optarg; + break; + case OPT_POLCAP: + polcaps = 1; + if (optarg != 0) + polcap_name = optarg; + break; + case OPT_PORTCON: + port = 1; + if (optarg != 0) + port_num = optarg; + break; + case OPT_PROTOCOL: + if (optarg != 0) + protocol = optarg; + break; + case OPT_ALL: + all = 1; + break; + case 'x': /* expand */ + expand = 1; + break; + case 'l': /* Print line breaks in constraints */ + linebreaks=1; + break; + case OPT_CONSTRAIN: /* Print constraints */ + constrain=1; + break; + case OPT_STATS: + stats = 1; + break; + case 'h': /* help */ + usage(argv[0], 0); + exit(0); + case 'V': /* version */ + printf("seinfo %s\n%s\n", VERSION, COPYRIGHT_INFO); + exit(0); + default: + usage(argv[0], 1); + exit(1); + } + } + + /* check for naked -l */ + if (protocol && !(port || all)) { + fprintf(stderr, "The --protocol flag requires either --portcon or --ALL.\n"); + exit(1); + } + + /* if no options, then show stats */ + if (classes + types + attribs + roles + users + isids + bools + sens + cats + fsuse + genfs + netif + node + port + permissives + polcaps + constrain + all < 1) { + stats = 1; + } + + int policy_load_options = ((stats || all) ? 0 : QPOL_POLICY_OPTION_NO_RULES); + + if (argc - optind < 1) { + rt = qpol_default_policy_find(&policy_file); + if (rt < 0) { + fprintf(stderr, "Default policy search failed: %s\n", strerror(errno)); + exit(1); + } else if (rt != 0) { + fprintf(stderr, "No default policy found.\n"); + exit(1); + } + policy_load_options |= QPOL_POLICY_OPTION_MATCH_SYSTEM; + } else { + policy_file = strdup(argv[optind]); + if (!policy_file) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + optind++; + } + + if (argc - optind > 0) { + path_type = APOL_POLICY_PATH_TYPE_MODULAR; + if (!(mod_paths = apol_vector_create(NULL))) { + ERR(policydb, "%s", strerror(ENOMEM)); + free(policy_file); + exit(1); + } + for (; argc - optind; optind++) { + if (apol_vector_append(mod_paths, (void *)argv[optind])) { + ERR(policydb, "Error loading module %s", argv[optind]); + free(policy_file); + apol_vector_destroy(&mod_paths); + exit(1); + } + } + } else if (apol_file_is_policy_path_list(policy_file) > 0) { + pol_path = apol_policy_path_create_from_file(policy_file); + if (!pol_path) { + ERR(policydb, "%s", "invalid policy list"); + free(policy_file); + exit(1); + } + } + + if (!pol_path) + pol_path = apol_policy_path_create(path_type, policy_file, mod_paths); + if (!pol_path) { + ERR(policydb, "%s", strerror(ENOMEM)); + free(policy_file); + apol_vector_destroy(&mod_paths); + exit(1); + } + apol_vector_destroy(&mod_paths); + + policydb = apol_policy_create_from_policy_path(pol_path, policy_load_options, NULL, NULL); + if (!policydb) { + ERR(policydb, "%s", strerror(errno)); + free(policy_file); + apol_policy_path_destroy(&pol_path); + exit(1); + } + + /* display requested info */ + if (stats || all) + print_stats(stdout, policydb); + if (classes || all) + print_classes(stdout, class_name, expand, policydb); + if (types || all) + print_types(stdout, type_name, expand, policydb); + if (attribs || all) + print_attribs(stdout, attrib_name, expand, policydb); + if (roles || all) + print_roles(stdout, role_name, expand, policydb); + if (users || all) + print_users(stdout, user_name, expand, policydb); + if (bools || all) + print_booleans(stdout, bool_name, expand, policydb); + if (sens || all) + print_sens(stdout, sens_name, expand, policydb); + if (cats || all) + print_cats(stdout, cat_name, expand, policydb); + if (fsuse || all) + print_fsuse(stdout, fsuse_type, policydb); + if (genfs || all) + print_genfscon(stdout, genfs_type, policydb); + if (netif || all) + print_netifcon(stdout, netif_name, policydb); + if (node || all) + print_nodecon(stdout, node_addr, policydb); + if (port || all) + print_portcon(stdout, port_num, protocol, policydb); + if (isids || all) + print_isids(stdout, isid_name, expand, policydb); + if (permissives || all) + print_permissives(stdout, permissive_name, expand, policydb); + if (polcaps || all) + print_polcaps(stdout, polcap_name, expand, policydb); + if (constrain || all) + print_constraints(stdout, expand, policydb, linebreaks); + + apol_policy_destroy(&policydb); + apol_policy_path_destroy(&pol_path); + free(policy_file); + exit(0); +} + +/** + * Prints a textual representation of a type, and possibly + * all of that type's attributes. + * + * @param fp Reference to a file to which to print type information + * @param type_datum Reference to sepol type_datum + * @param policydb Reference to a policy + * @param expand Flag indicating whether to print each type's + * attributes + */ +static void print_type_attrs(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand) +{ + qpol_iterator_t *iter = NULL; + unsigned char isattr, isalias; + const char *type_name = NULL, *attr_name = NULL; + const qpol_type_t *attr_datum = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + + if (qpol_type_get_name(q, type_datum, &type_name)) + goto cleanup; + if (qpol_type_get_isattr(q, type_datum, &isattr)) + goto cleanup; + if (qpol_type_get_isalias(q, type_datum, &isalias)) + goto cleanup; + + if (!isattr && !isalias) { + fprintf(fp, " %s\n", type_name); + if (expand) { /* Print this type's attributes */ + if (qpol_type_get_attr_iter(q, type_datum, &iter)) + goto cleanup; + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&attr_datum)) + goto cleanup; + if (qpol_type_get_name(q, attr_datum, &attr_name)) + goto cleanup; + fprintf(fp, " %s\n", attr_name); + } + } + } + + cleanup: + qpol_iterator_destroy(&iter); + return; +} + +/** + * Prints a textual representation of an attribute, and possibly + * all of that attribute's types. + * + * @param fp Reference to a file to which to print attribute information + * @param type_datum Reference to sepol type_datum + * @param policydb Reference to a policy + * @param expand Flag indicating whether to print each attribute's + * types + */ +static void print_attr_types(FILE * fp, const qpol_type_t * type_datum, const apol_policy_t * policydb, const int expand) +{ + const qpol_type_t *attr_datum = NULL; + qpol_iterator_t *iter = NULL; + const char *attr_name = NULL, *type_name = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + unsigned char isattr; + + if (qpol_type_get_name(q, type_datum, &attr_name)) + goto cleanup; + fprintf(fp, " %s\n", attr_name); + + if (expand) { + /* get an iterator over all types this attribute has */ + if (qpol_type_get_isattr(q, type_datum, &isattr)) + goto cleanup; + if (isattr) { /* sanity check */ + if (qpol_type_get_type_iter(q, type_datum, &iter)) + goto cleanup; + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&attr_datum)) + goto cleanup; + if (qpol_type_get_name(q, attr_datum, &type_name)) + goto cleanup; + fprintf(fp, " %s\n", type_name); + } + qpol_iterator_destroy(&iter); + + } else /* this should never happen */ + goto cleanup; + + } + + cleanup: + qpol_iterator_destroy(&iter); + return; +} + +/** + * Prints a textual representation of a user, and possibly + * all of that user's roles. + * + * @param fp Reference to a file to which to print user information + * @param type_datum Reference to sepol type_datum + * @param policydb Reference to a policy + * @param expand Flag indicating whether to print each user's + * roles + */ +static void print_user_roles(FILE * fp, const qpol_user_t * user_datum, const apol_policy_t * policydb, const int expand) +{ + const qpol_role_t *role_datum = NULL; + qpol_iterator_t *iter = NULL; + const qpol_mls_range_t *range = NULL; + const qpol_mls_level_t *dflt_level = NULL; + apol_mls_level_t *ap_lvl = NULL; + apol_mls_range_t *ap_range = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + char *tmp; + const char *user_name, *role_name; + + if (qpol_user_get_name(q, user_datum, &user_name)) + goto cleanup; + fprintf(fp, " %s\n", user_name); + + if (expand) { + if (qpol_policy_has_capability(q, QPOL_CAP_MLS)) { + /* print default level */ + if (qpol_user_get_dfltlevel(q, user_datum, &dflt_level)) + goto cleanup; + ap_lvl = apol_mls_level_create_from_qpol_mls_level(policydb, dflt_level); + tmp = apol_mls_level_render(policydb, ap_lvl); + if (!tmp) + goto cleanup; + fprintf(fp, " default level: %s\n", tmp); + free(tmp); + /* print default range */ + if (qpol_user_get_range(q, user_datum, &range)) + goto cleanup; + ap_range = apol_mls_range_create_from_qpol_mls_range(policydb, range); + tmp = apol_mls_range_render(policydb, ap_range); + if (!tmp) + goto cleanup; + fprintf(fp, " range: %s\n", tmp); + free(tmp); + } + + fprintf(fp, " roles:\n"); + if (qpol_user_get_role_iter(q, user_datum, &iter)) + goto cleanup; + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&role_datum)) + goto cleanup; + if (qpol_role_get_name(q, role_datum, &role_name)) + goto cleanup; + fprintf(fp, " %s\n", role_name); + } + } + + cleanup: + qpol_iterator_destroy(&iter); + apol_mls_level_destroy(&ap_lvl); + apol_mls_range_destroy(&ap_range); + return; +} + +/** + * Prints a textual representation of a role, and possibly + * all of that role's types. + * + * @param fp Reference to a file to which to print role information + * @param type_datum Reference to sepol type_datum + * @param policydb Reference to a policy + * @param expand Flag indicating whether to print each role's + * types + */ +static void print_role_types(FILE * fp, const qpol_role_t * role_datum, const apol_policy_t * policydb, const int expand) +{ + const char *role_name = NULL, *type_name = NULL; + const qpol_role_t *dom_datum = NULL; + const qpol_type_t *type_datum = NULL; + qpol_iterator_t *iter = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t n_dom = 0, n_types = 0; + + if (qpol_role_get_name(q, role_datum, &role_name)) + goto cleanup; + fprintf(fp, " %s\n", role_name); + + if (expand) { + if (qpol_role_get_dominate_iter(q, role_datum, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_dom)) + goto cleanup; + if ((int)n_dom > 0) { + fprintf(fp, " Dominated Roles:\n"); + /* print dominated roles */ + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&dom_datum)) + goto cleanup; + if (qpol_role_get_name(q, dom_datum, &role_name)) + goto cleanup; + fprintf(fp, " %s\n", role_name); + } + } + qpol_iterator_destroy(&iter); + + if (qpol_role_get_type_iter(q, role_datum, &iter)) + goto cleanup; + if (qpol_iterator_get_size(iter, &n_types)) + goto cleanup; + if ((int)n_types > 0) { + fprintf(fp, " Types:\n"); + /* print types */ + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&type_datum)) + goto cleanup; + if (qpol_type_get_name(q, type_datum, &type_name)) + goto cleanup; + fprintf(fp, " %s\n", type_name); + } + } + } + + cleanup: + qpol_iterator_destroy(&iter); + return; +} + +/** + * Prints a textual representation of a boolean value, and possibly + * all of that boolean's initial state. + * + * @param fp Reference to a file to which to print boolean information + * @param type_datum Reference to sepol type_datum + * @param policydb Reference to a policy + * @param expand Flag indicating whether to print each boolean's + * initial state + */ +static void print_bool_state(FILE * fp, const qpol_bool_t * bool_datum, const apol_policy_t * policydb, const int expand) +{ + const char *bool_name = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + int state; + + if (qpol_bool_get_name(q, bool_datum, &bool_name)) + return; + fprintf(fp, " %s", bool_name); + + if (expand) { + if (qpol_bool_get_state(q, bool_datum, &state)) + return; + fprintf(fp, ": %s", state ? "TRUE" : "FALSE"); + } + fprintf(fp, "\n"); +} + +/** + * Prints a textual representation of an object class and possibly + * all of that object class' permissions. + * + * @param fp Reference to a file to which to print object class information + * @param type_datum Reference to sepol type_datum + * @param policydb Reference to a policy + * @param expand Flag indicating whether to print each object class' + * permissions + */ +static void print_class_perms(FILE * fp, const qpol_class_t * class_datum, const apol_policy_t * policydb, const int expand) +{ + const char *class_name = NULL, *perm_name = NULL; + qpol_iterator_t *iter = NULL; + const qpol_common_t *common_datum = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + + if (!class_datum) + goto cleanup; + + if (qpol_class_get_name(q, class_datum, &class_name)) + goto cleanup; + fprintf(fp, " %s\n", class_name); + + if (expand) { + /* get commons for this class */ + if (qpol_class_get_common(q, class_datum, &common_datum)) + goto cleanup; + if (common_datum) { + if (qpol_common_get_perm_iter(q, common_datum, &iter)) + goto cleanup; + /* print perms for the common */ + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&perm_name)) + goto cleanup; + fprintf(fp, " %s\n", perm_name); + } + qpol_iterator_destroy(&iter); + } + /* print unique perms for this class */ + if (qpol_class_get_perm_iter(q, class_datum, &iter)) + goto cleanup; + for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) { + if (qpol_iterator_get_item(iter, (void **)&perm_name)) + goto cleanup; + fprintf(fp, " %s\n", perm_name); + } + qpol_iterator_destroy(&iter); + } + + cleanup: + qpol_iterator_destroy(&iter); + return; +} + +/** + * Prints a textual representation of a MLS category and possibly + * all of that category's sensitivies. + * + * @param fp Reference to a file to which to print category information + * @param type_datum Reference to sepol type_datum + * @param policydb Reference to a policy + * @param expand Flag indicating whether to print each category's + * sensitivities + */ +static void print_cat_sens(FILE * fp, const qpol_cat_t * cat_datum, const apol_policy_t * policydb, const int expand) +{ + const char *cat_name, *lvl_name; + apol_level_query_t *query = NULL; + apol_vector_t *v = NULL; + const qpol_level_t *lvl_datum = NULL; + qpol_policy_t *q = apol_policy_get_qpol(policydb); + size_t i, n_sens = 0; + + if (!fp || !cat_datum || !policydb) + goto cleanup; + + /* get category name for apol query */ + if (qpol_cat_get_name(q, cat_datum, &cat_name)) + goto cleanup; + + query = apol_level_query_create(); + if (!query) { + ERR(policydb, "%s", strerror(ENOMEM)); + goto cleanup; + } + if (apol_level_query_set_cat(policydb, query, cat_name)) + goto cleanup; + if (apol_level_get_by_query(policydb, query, &v)) + goto cleanup; + fprintf(fp, " %s\n", cat_name); + + if (expand) { + fprintf(fp, " Sensitivities:\n"); + apol_vector_sort(v, &qpol_level_datum_compare, (void *)policydb); + n_sens = apol_vector_get_size(v); + for (i = 0; i < n_sens; i++) { + lvl_datum = (qpol_level_t *) apol_vector_get_element(v, i); + if (!lvl_datum) + goto cleanup; + if (qpol_level_get_name(q, lvl_datum, &lvl_name)) + goto cleanup; + fprintf(fp, " %s\n", lvl_name); + } + } + + cleanup: + apol_level_query_destroy(&query); + apol_vector_destroy(&v); + return; +} + +/** + * Compare two qpol_cat_datum_t objects. + * This function is meant to be passed to apol_vector_compare + * as the callback for performing comparisons. + * + * @param datum1 Reference to a qpol_type_datum_t object + * @param datum2 Reference to a qpol_type_datum_t object + * @param data Reference to a policy + * @return Greater than 0 if the first argument is less than the second argument, + * less than 0 if the first argument is greater than the second argument, + * 0 if the arguments are equal + */ +static int qpol_cat_datum_compare(const void *datum1, const void *datum2, void *data) +{ + const qpol_cat_t *cat_datum1 = NULL, *cat_datum2 = NULL; + apol_policy_t *policydb = NULL; + qpol_policy_t *q; + uint32_t val1, val2; + + policydb = (apol_policy_t *) data; + q = apol_policy_get_qpol(policydb); + assert(policydb); + + if (!datum1 || !datum2) + goto exit_err; + cat_datum1 = datum1; + cat_datum2 = datum2; + + if (qpol_cat_get_value(q, cat_datum1, &val1)) + goto exit_err; + if (qpol_cat_get_value(q, cat_datum2, &val2)) + goto exit_err; + + return (val1 > val2) ? 1 : ((val1 == val2) ? 0 : -1); + + exit_err: + assert(0); + return 0; +} + +/** + * Compare two qpol_level_datum_t objects. + * This function is meant to be passed to apol_vector_compare + * as the callback for performing comparisons. + * + * @param datum1 Reference to a qpol_level_datum_t object + * @param datum2 Reference to a qpol_level_datum_t object + * @param data Reference to a policy + * @return Greater than 0 if the first argument is less than the second argument, + * less than 0 if the first argument is greater than the second argument, + * 0 if the arguments are equal + */ +static int qpol_level_datum_compare(const void *datum1, const void *datum2, void *data) +{ + const qpol_level_t *lvl_datum1 = NULL, *lvl_datum2 = NULL; + apol_policy_t *policydb = NULL; + qpol_policy_t *q; + uint32_t val1, val2; + + policydb = (apol_policy_t *) data; + assert(policydb); + q = apol_policy_get_qpol(policydb); + + if (!datum1 || !datum2) + goto exit_err; + lvl_datum1 = datum1; + lvl_datum2 = datum2; + + if (qpol_level_get_value(q, lvl_datum1, &val1)) + goto exit_err; + if (qpol_level_get_value(q, lvl_datum2, &val2)) + goto exit_err; + + return (val1 > val2) ? 1 : ((val1 == val2) ? 0 : -1); + + exit_err: + assert(0); + return 0; +} diff --git a/secmds/sesearch.c b/secmds/sesearch.c new file mode 100644 index 0000000..ec0315f --- /dev/null +++ b/secmds/sesearch.c @@ -0,0 +1,1173 @@ +/** + * @file + * Command line tool to search TE rules. + * + * @author Frank Mayer mayerf@tresys.com + * @author Jeremy A. Mowery jmowery@tresys.com + * @author Paul Rosenfeld prosenfeld@tresys.com + * + * Copyright (C) 2003-2009 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> + +/* libapol */ +#include <apol/policy.h> +#include <apol/policy-query.h> +#include <apol/render.h> +#include <apol/util.h> +#include <apol/vector.h> + +/* libqpol*/ +#include <qpol/policy.h> +#include <qpol/policy_extend.h> +#include <qpol/syn_rule_query.h> +#include <qpol/util.h> + +/* other */ +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <getopt.h> +#include <string.h> +#include <stdbool.h> + +#define COPYRIGHT_INFO "Copyright (C) 2003-2009 Tresys Technology, LLC" + +static char *policy_file = NULL; + +enum opt_values +{ + RULE_NEVERALLOW = 256, RULE_AUDIT, RULE_AUDITALLOW, RULE_DONTAUDIT, + RULE_ROLE_ALLOW, RULE_ROLE_TRANS, RULE_RANGE_TRANS, RULE_ALL, + EXPR_ROLE_SOURCE, EXPR_ROLE_TARGET +}; + +static struct option const longopts[] = { + {"allow", no_argument, NULL, 'A'}, + {"neverallow", no_argument, NULL, RULE_NEVERALLOW}, + {"audit", no_argument, NULL, RULE_AUDIT}, + {"auditallow", no_argument, NULL, RULE_AUDITALLOW}, + {"dontaudit", no_argument, NULL, RULE_DONTAUDIT}, + {"type", no_argument, NULL, 'T'}, + {"role_allow", no_argument, NULL, RULE_ROLE_ALLOW}, + {"role_trans", no_argument, NULL, RULE_ROLE_TRANS}, + {"range_trans", no_argument, NULL, RULE_RANGE_TRANS}, + {"all", no_argument, NULL, RULE_ALL}, + + {"source", required_argument, NULL, 's'}, + {"target", required_argument, NULL, 't'}, + {"role_source", required_argument, NULL, EXPR_ROLE_SOURCE}, + {"role_target", required_argument, NULL, EXPR_ROLE_TARGET}, + {"class", required_argument, NULL, 'c'}, + {"perm", required_argument, NULL, 'p'}, + {"bool", required_argument, NULL, 'b'}, + + {"direct", no_argument, NULL, 'd'}, + {"regex", no_argument, NULL, 'R'}, + {"linenum", no_argument, NULL, 'n'}, + {"semantic", no_argument, NULL, 'S'}, + {"show_cond", no_argument, NULL, 'C'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'}, + {NULL, 0, NULL, 0} +}; + +typedef struct options +{ + char *src_name; + char *tgt_name; + char *src_role_name; + char *tgt_role_name; + char *class_name; + char *permlist; + char *bool_name; + apol_vector_t *class_vector; + bool all; + bool lineno; + bool semantic; + bool indirect; + bool allow; + bool nallow; + bool auditallow; + bool dontaudit; + bool type; + bool rtrans; + bool role_allow; + bool role_trans; + bool useregex; + bool show_cond; + apol_vector_t *perm_vector; +} options_t; + +void usage(const char *program_name, int brief) +{ + printf("Usage: %s [OPTIONS] RULE_TYPE [RULE_TYPE ...] [EXPESSION] [POLICY ...]\n\n", program_name); + if (brief) { + printf("\tTry %s --help for more help.\n\n", program_name); + return; + } + printf("Search the rules in a SELinux policy.\n\n"); + printf("RULE_TYPES:\n"); + printf(" -A, --allow allow rules\n"); + printf(" --neverallow neverallow rules\n"); + printf(" --auditallow auditallow rules\n"); + printf(" --dontaudit dontaudit rules\n"); + printf(" -T, --type type_trans, type_member, and type_change\n"); + printf(" --role_allow role allow rules\n"); + printf(" --role_trans role_transition rules\n"); + printf(" --range_trans range_transition rules\n"); + printf(" --all all rules regardless of type, class, or perms\n"); + printf("EXPRESSIONS:\n"); + printf(" -s NAME, --source=NAME rules with type/attribute NAME as source\n"); + printf(" -t NAME, --target=NAME rules with type/attribute NAME as target\n"); + printf(" --role_source=NAME rules with role NAME as source\n"); + printf(" --role_target=NAME rules with role NAME as target\n"); + printf(" -c NAME, --class=NAME rules with class NAME as the object class\n"); + printf(" -p P1[,P2,...], --perm=P1[,P2...]\n"); + printf(" rules with the specified permission\n"); + printf(" -b NAME, --bool=NAME conditional rules with NAME in the expression\n"); + printf("OPTIONS:\n"); + printf(" -d, --direct do not search for type's attributes\n"); + printf(" -R, --regex use regular expression matching\n"); + printf(" -n, --linenum show line number for each rule if available\n"); + printf(" -S, --semantic search rules semantically instead of syntactically\n"); + printf(" -C, --show_cond show conditional expression for conditional rules\n"); + printf(" -h, --help print this help text and exit\n"); + printf(" -V, --version print version information and exit\n"); + printf("\n"); + printf("If no expression is specified, then all rules are shown.\n"); + printf("\n"); + printf("The default source policy, or if that is unavailable the default binary\n"); + printf("policy, will be opened if no policy is provided.\n\n"); +} + +static int perform_av_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v) +{ + apol_avrule_query_t *avq = NULL; + unsigned int rules = 0; + int error = 0; + char *tmp = NULL, *tok = NULL, *s = NULL; + + if (!policy || !opt || !v) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!opt->all && !opt->allow && !opt->nallow && !opt->auditallow && !opt->dontaudit) { + *v = NULL; + return 0; /* no search to do */ + } + + avq = apol_avrule_query_create(); + if (!avq) { + ERR(policy, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return -1; + } + + if (opt->allow || opt->all) + rules |= QPOL_RULE_ALLOW; + if (opt->nallow || opt->all) // Add this regardless of policy capabilities + rules |= QPOL_RULE_NEVERALLOW; + if (opt->auditallow || opt->all) + rules |= QPOL_RULE_AUDITALLOW; + if (opt->dontaudit || opt->all) + rules |= QPOL_RULE_DONTAUDIT; + if (rules != 0) // Setting rules = 0 means you want all the rules + apol_avrule_query_set_rules(policy, avq, rules); + apol_avrule_query_set_regex(policy, avq, opt->useregex); + if (opt->src_name) + apol_avrule_query_set_source(policy, avq, opt->src_name, opt->indirect); + if (opt->tgt_name) + apol_avrule_query_set_target(policy, avq, opt->tgt_name, opt->indirect); + if (opt->bool_name) + apol_avrule_query_set_bool(policy, avq, opt->bool_name); + if (opt->class_name) { + if (opt->class_vector == NULL) { + if (apol_avrule_query_append_class(policy, avq, opt->class_name)) { + error = errno; + goto err; + } + } else { + for (size_t i = 0; i < apol_vector_get_size(opt->class_vector); ++i) { + char *class_name; + class_name = apol_vector_get_element(opt->class_vector, i); + if (!class_name) + continue; + if (apol_avrule_query_append_class(policy, avq, class_name)) { + error = errno; + goto err; + } + } + } + } + + if (opt->permlist) { + tmp = strdup(opt->permlist); + for (tok = strtok(tmp, ","); tok; tok = strtok(NULL, ",")) { + if (apol_avrule_query_append_perm(policy, avq, tok)) { + error = errno; + goto err; + } + if ((s = strdup(tok)) == NULL || apol_vector_append(opt->perm_vector, s) < 0) { + error = errno; + goto err; + } + s = NULL; + } + free(tmp); + } + + if (!(opt->semantic) && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) { + if (apol_syn_avrule_get_by_query(policy, avq, v)) { + error = errno; + goto err; + } + } else { + if (apol_avrule_get_by_query(policy, avq, v)) { + error = errno; + goto err; + } + } + + apol_avrule_query_destroy(&avq); + return 0; + + err: + apol_vector_destroy(v); + apol_avrule_query_destroy(&avq); + free(tmp); + free(s); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; +} + +static void print_syn_av_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v) +{ + qpol_policy_t *q = apol_policy_get_qpol(policy); + size_t i, num_rules = 0; + const apol_vector_t *syn_list = NULL; + const qpol_syn_avrule_t *rule = NULL; + char *tmp = NULL, *rule_str = NULL, *expr = NULL; + char enable_char = ' ', branch_char = ' '; + const qpol_cond_t *cond = NULL; + uint32_t enabled = 0, is_true = 0; + unsigned long lineno = 0; + + if (!policy || !v) + return; + + syn_list = v; + if (!(num_rules = apol_vector_get_size(syn_list))) + goto cleanup; + + fprintf(stdout, "Found %zd syntactic av rules:\n", num_rules); + + for (i = 0; i < num_rules; i++) { + rule = apol_vector_get_element(syn_list, i); + enable_char = branch_char = ' '; + if (opt->show_cond) { + if (qpol_syn_avrule_get_cond(q, rule, &cond)) + goto cleanup; + if (cond) { + if (qpol_syn_avrule_get_is_enabled(q, rule, &enabled) < 0 || qpol_cond_eval(q, cond, &is_true) < 0) + goto cleanup; + tmp = apol_cond_expr_render(policy, cond); + enable_char = (enabled ? 'E' : 'D'); + branch_char = ((is_true && enabled) || (!is_true && !enabled) ? 'T' : 'F'); + asprintf(&expr, "[ %s ]", tmp); + free(tmp); + tmp = NULL; + if (!expr) + goto cleanup; + } + } + if (!(rule_str = apol_syn_avrule_render(policy, rule))) + goto cleanup; + if (opt->lineno) { + if (qpol_syn_avrule_get_lineno(q, rule, &lineno)) + goto cleanup; + fprintf(stdout, "%c%c [%7lu] %s %s\n", enable_char, branch_char, lineno, rule_str, expr ? expr : ""); + } else { + fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : ""); + } + free(rule_str); + rule_str = NULL; + free(expr); + expr = NULL; + } + + cleanup: + free(tmp); + free(rule_str); + free(expr); +} + +static void print_av_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v) +{ + qpol_policy_t *q = apol_policy_get_qpol(policy); + size_t i, num_rules = 0; + const qpol_avrule_t *rule = NULL; + char *tmp = NULL, *rule_str = NULL, *expr = NULL; + char enable_char = ' ', branch_char = ' '; + qpol_iterator_t *iter = NULL; + const qpol_cond_t *cond = NULL; + uint32_t enabled = 0, list = 0; + + if (!policy || !v) + return; + + if (!(num_rules = apol_vector_get_size(v))) + return; + + fprintf(stdout, "Found %zd semantic av rules:\n", num_rules); + + for (i = 0; i < num_rules; i++) { + enable_char = branch_char = ' '; + if (!(rule = apol_vector_get_element(v, i))) + goto cleanup; + if (opt->show_cond) { + if (qpol_avrule_get_cond(q, rule, &cond)) + goto cleanup; + if (qpol_avrule_get_is_enabled(q, rule, &enabled)) + goto cleanup; + if (cond) { + if (qpol_avrule_get_which_list(q, rule, &list)) + goto cleanup; + tmp = apol_cond_expr_render(policy, cond); + qpol_iterator_destroy(&iter); + enable_char = (enabled ? 'E' : 'D'); + branch_char = (list ? 'T' : 'F'); + asprintf(&expr, "[ %s ]", tmp); + free(tmp); + tmp = NULL; + if (!expr) + goto cleanup; + } + } + if (!(rule_str = apol_avrule_render(policy, rule))) + goto cleanup; + fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : ""); + free(rule_str); + rule_str = NULL; + free(expr); + expr = NULL; + } + + cleanup: + free(tmp); + free(rule_str); + free(expr); +} + +static int perform_te_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v) +{ + apol_terule_query_t *teq = NULL; + unsigned int rules = 0; + int error = 0; + + if (!policy || !opt || !v) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (opt->all || opt->type) { + rules = (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER); + } else { + *v = NULL; + return 0; /* no search to do */ + } + + teq = apol_terule_query_create(); + if (!teq) { + ERR(policy, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return -1; + } + + apol_terule_query_set_rules(policy, teq, rules); + apol_terule_query_set_regex(policy, teq, opt->useregex); + if (opt->src_name) + apol_terule_query_set_source(policy, teq, opt->src_name, opt->indirect); + if (opt->tgt_name) + apol_terule_query_set_target(policy, teq, opt->tgt_name, opt->indirect); + if (opt->bool_name) + apol_terule_query_set_bool(policy, teq, opt->bool_name); + if (opt->class_name) { + if (opt->class_vector == NULL) { + if (apol_terule_query_append_class(policy, teq, opt->class_name)) { + error = errno; + goto err; + } + } else { + for (size_t i = 0; i < apol_vector_get_size(opt->class_vector); ++i) { + char *class_name; + class_name = apol_vector_get_element(opt->class_vector, i); + if (!class_name) + continue; + if (apol_terule_query_append_class(policy, teq, class_name)) { + error = errno; + goto err; + } + } + } + } + + if (!(opt->semantic) && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) { + if (apol_syn_terule_get_by_query(policy, teq, v)) { + error = errno; + goto err; + } + } else { + if (apol_terule_get_by_query(policy, teq, v)) { + error = errno; + goto err; + } + } + + apol_terule_query_destroy(&teq); + return 0; + + err: + apol_vector_destroy(v); + apol_terule_query_destroy(&teq); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; +} + +static void print_syn_te_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v) +{ + qpol_policy_t *q = apol_policy_get_qpol(policy); + size_t i, num_rules = 0; + const apol_vector_t *syn_list = NULL; + const qpol_syn_terule_t *rule = NULL; + char *tmp = NULL, *rule_str = NULL, *expr = NULL; + char enable_char = ' ', branch_char = ' '; + const qpol_cond_t *cond = NULL; + uint32_t enabled = 0, is_true = 0; + unsigned long lineno = 0; + + if (!policy || !v) + return; + + syn_list = v; + if (!(num_rules = apol_vector_get_size(syn_list))) + goto cleanup; + + fprintf(stdout, "Found %zd syntactic te rules:\n", num_rules); + + for (i = 0; i < num_rules; i++) { + rule = apol_vector_get_element(syn_list, i); + enable_char = branch_char = ' '; + if (opt->show_cond) { + if (qpol_syn_terule_get_cond(q, rule, &cond)) + goto cleanup; + if (cond) { + if (qpol_syn_terule_get_is_enabled(q, rule, &enabled) < 0 || qpol_cond_eval(q, cond, &is_true) < 0) + goto cleanup; + tmp = apol_cond_expr_render(policy, cond); + enable_char = (enabled ? 'E' : 'D'); + branch_char = ((is_true && enabled) || (!is_true && !enabled) ? 'T' : 'F'); + asprintf(&expr, "[ %s ]", tmp); + free(tmp); + tmp = NULL; + if (!expr) + break; + } + } + if (!(rule_str = apol_syn_terule_render(policy, rule))) + goto cleanup; + if (opt->lineno) { + if (qpol_syn_terule_get_lineno(q, rule, &lineno)) + goto cleanup; + fprintf(stdout, "%c%c [%7lu] %s %s\n", enable_char, branch_char, lineno, rule_str, expr ? expr : ""); + } else { + fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : ""); + } + free(rule_str); + rule_str = NULL; + free(expr); + expr = NULL; + } + + cleanup: + free(tmp); + free(rule_str); + free(expr); +} + +static void print_te_results(const apol_policy_t * policy, const options_t * opt, const apol_vector_t * v) +{ + qpol_policy_t *q = apol_policy_get_qpol(policy); + size_t i, num_rules = 0; + const qpol_terule_t *rule = NULL; + char *tmp = NULL, *rule_str = NULL, *expr = NULL; + char enable_char = ' ', branch_char = ' '; + qpol_iterator_t *iter = NULL; + const qpol_cond_t *cond = NULL; + uint32_t enabled = 0, list = 0; + + if (!policy || !v) + goto cleanup; + + if (!(num_rules = apol_vector_get_size(v))) + goto cleanup; + + fprintf(stdout, "Found %zd semantic te rules:\n", num_rules); + + for (i = 0; i < num_rules; i++) { + enable_char = branch_char = ' '; + if (!(rule = apol_vector_get_element(v, i))) + goto cleanup; + if (opt->show_cond) { + if (qpol_terule_get_cond(q, rule, &cond)) + goto cleanup; + if (qpol_terule_get_is_enabled(q, rule, &enabled)) + goto cleanup; + if (cond) { + if (qpol_terule_get_which_list(q, rule, &list)) + goto cleanup; + if (qpol_cond_get_expr_node_iter(q, cond, &iter)) + goto cleanup; + tmp = apol_cond_expr_render(policy, cond); + qpol_iterator_destroy(&iter); + enable_char = (enabled ? 'E' : 'D'); + branch_char = (list ? 'T' : 'F'); + asprintf(&expr, "[ %s ]", tmp); + free(tmp); + tmp = NULL; + if (!expr) + goto cleanup; + } + } + if (!(rule_str = apol_terule_render(policy, rule))) + goto cleanup; + fprintf(stdout, "%c%c %s %s\n", enable_char, branch_char, rule_str, expr ? expr : ""); + free(rule_str); + rule_str = NULL; + free(expr); + expr = NULL; + } + + cleanup: + free(tmp); + free(rule_str); + free(expr); +} + +static int perform_ra_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v) +{ + apol_role_allow_query_t *raq = NULL; + int error = 0; + + if (!policy || !opt || !v) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!opt->role_allow && !opt->all) { + *v = NULL; + return 0; /* no search to do */ + } + + raq = apol_role_allow_query_create(); + if (!raq) { + ERR(policy, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return -1; + } + + apol_role_allow_query_set_regex(policy, raq, opt->useregex); + if (opt->src_role_name) { + if (apol_role_allow_query_set_source(policy, raq, opt->src_role_name)) { + error = errno; + goto err; + } + } + if (opt->tgt_role_name) + if (apol_role_allow_query_set_target(policy, raq, opt->tgt_role_name)) { + error = errno; + goto err; + } + + if (apol_role_allow_get_by_query(policy, raq, v)) { + error = errno; + goto err; + } + + apol_role_allow_query_destroy(&raq); + return 0; + + err: + apol_vector_destroy(v); + apol_role_allow_query_destroy(&raq); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; +} + +static void print_ra_results(const apol_policy_t * policy, const options_t * opt __attribute__ ((unused)), const apol_vector_t * v) +{ + size_t i, num_rules = 0; + const qpol_role_allow_t *rule = NULL; + char *tmp = NULL; + + if (!policy || !v) + return; + + if (!(num_rules = apol_vector_get_size(v))) + return; + + fprintf(stdout, "Found %zd role allow rules:\n", num_rules); + + for (i = 0; i < num_rules; i++) { + if (!(rule = apol_vector_get_element(v, i))) + break; + if (!(tmp = apol_role_allow_render(policy, rule))) + break; + fprintf(stdout, " %s\n", tmp); + free(tmp); + tmp = NULL; + } +} + +static int perform_rt_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v) +{ + apol_role_trans_query_t *rtq = NULL; + int error = 0; + + if (!policy || !opt || !v) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!opt->role_trans && !opt->all) { + *v = NULL; + return 0; /* no search to do */ + } + + rtq = apol_role_trans_query_create(); + if (!rtq) { + ERR(policy, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return -1; + } + + apol_role_trans_query_set_regex(policy, rtq, opt->useregex); + if (opt->src_role_name) { + if (apol_role_trans_query_set_source(policy, rtq, opt->src_role_name)) { + error = errno; + goto err; + } + } + if (opt->tgt_name) { + if (apol_role_trans_query_set_target(policy, rtq, opt->tgt_name, opt->indirect)) { + error = errno; + goto err; + } + } + + if (apol_role_trans_get_by_query(policy, rtq, v)) { + error = errno; + goto err; + } + + apol_role_trans_query_destroy(&rtq); + return 0; + + err: + apol_vector_destroy(v); + apol_role_trans_query_destroy(&rtq); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; +} + +static void print_rt_results(const apol_policy_t * policy, const options_t * opt __attribute__ ((unused)), const apol_vector_t * v) +{ + size_t i, num_rules = 0; + const qpol_role_trans_t *rule = NULL; + char *tmp = NULL; + + if (!policy || !v) + return; + + if (!(num_rules = apol_vector_get_size(v))) + return; + + fprintf(stdout, "Found %zd role_transition rules:\n", num_rules); + + for (i = 0; i < num_rules; i++) { + if (!(rule = apol_vector_get_element(v, i))) + break; + if (!(tmp = apol_role_trans_render(policy, rule))) + break; + fprintf(stdout, " %s\n", tmp); + free(tmp); + tmp = NULL; + } +} + +static int perform_range_query(const apol_policy_t * policy, const options_t * opt, apol_vector_t ** v) +{ + apol_range_trans_query_t *rtq = NULL; + int error = 0; + + if (!policy || !opt || !v) { + ERR(policy, "%s", strerror(EINVAL)); + errno = EINVAL; + return -1; + } + + if (!opt->rtrans && !opt->all) { + *v = NULL; + return 0; /* no search to do */ + } + + rtq = apol_range_trans_query_create(); + if (!rtq) { + ERR(policy, "%s", strerror(ENOMEM)); + errno = ENOMEM; + return -1; + } + + apol_range_trans_query_set_regex(policy, rtq, opt->useregex); + if (opt->src_name) { + if (apol_range_trans_query_set_source(policy, rtq, opt->src_name, opt->indirect)) { + error = errno; + goto err; + } + } + if (opt->tgt_name) { + if (apol_range_trans_query_set_target(policy, rtq, opt->tgt_name, opt->indirect)) { + error = errno; + goto err; + } + } + if (opt->class_name) { + if (opt->class_vector == NULL) { + if (apol_range_trans_query_append_class(policy, rtq, opt->class_name)) { + error = errno; + goto err; + } + } else { + for (size_t i = 0; i < apol_vector_get_size(opt->class_vector); ++i) { + char *class_name; + class_name = apol_vector_get_element(opt->class_vector, i); + if (!class_name) + continue; + if (apol_range_trans_query_append_class(policy, rtq, class_name)) { + error = errno; + goto err; + } + } + } + } + + if (apol_range_trans_get_by_query(policy, rtq, v)) { + error = errno; + goto err; + } + + apol_range_trans_query_destroy(&rtq); + return 0; + + err: + apol_vector_destroy(v); + apol_range_trans_query_destroy(&rtq); + ERR(policy, "%s", strerror(error)); + errno = error; + return -1; +} + +static void print_range_results(const apol_policy_t * policy, const options_t * opt + __attribute__ ((unused)), const apol_vector_t * v) +{ + size_t i, num_rules = 0; + const qpol_range_trans_t *rule = NULL; + char *tmp = NULL; + + if (!policy || !v) + return; + + if (!(num_rules = apol_vector_get_size(v))) + return; + + fprintf(stdout, "Found %zd range_transition rules:\n", num_rules); + + for (i = 0; i < num_rules; i++) { + if (!(rule = apol_vector_get_element(v, i))) + break; + if (!(tmp = apol_range_trans_render(policy, rule))) + break; + fprintf(stdout, " %s\n", tmp); + free(tmp); + tmp = NULL; + } +} + +int main(int argc, char **argv) +{ + options_t cmd_opts; + int optc, rt = -1; + + apol_policy_t *policy = NULL; + apol_vector_t *v = NULL; + apol_policy_path_t *pol_path = NULL; + apol_vector_t *mod_paths = NULL; + apol_policy_path_type_e path_type = APOL_POLICY_PATH_TYPE_MONOLITHIC; + + memset(&cmd_opts, 0, sizeof(cmd_opts)); + cmd_opts.indirect = true; + while ((optc = getopt_long(argc, argv, "ATs:t:c:p:b:dRnSChV", longopts, NULL)) != -1) { + switch (optc) { + case 0: + break; + case 's': /* source */ + if (optarg == 0) { + usage(argv[0], 1); + printf("Missing source type/attribute for -s (--source)\n"); + exit(1); + } + cmd_opts.src_name = strdup(optarg); + if (!cmd_opts.src_name) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + break; + case 't': /* target */ + if (optarg == 0) { + usage(argv[0], 1); + printf("Missing target type/attribute for -t (--target)\n"); + exit(1); + } + cmd_opts.tgt_name = strdup(optarg); + if (!cmd_opts.tgt_name) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + break; + case EXPR_ROLE_SOURCE: + if (optarg == 0) { + usage(argv[0], 1); + printf("Missing source role for --role_source\n"); + exit(1); + } + cmd_opts.src_role_name = strdup(optarg); + if (!cmd_opts.src_role_name) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + break; + case EXPR_ROLE_TARGET: + if (optarg == 0) { + usage(argv[0], 1); + printf("Missing target role for --role_target\n"); + exit(1); + } + cmd_opts.tgt_role_name = strdup(optarg); + if (!cmd_opts.tgt_role_name) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + break; + case 'c': /* class */ + if (optarg == 0) { + usage(argv[0], 1); + printf("Missing object class for -c (--class)\n"); + exit(1); + } + cmd_opts.class_name = strdup(optarg); + if (!cmd_opts.class_name) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + break; + case 'p': /* permission */ + if (optarg == 0) { + usage(argv[0], 1); + printf("Missing permissions for -p (--perm)\n"); + exit(1); + } + if ((cmd_opts.permlist = strdup(optarg)) == NULL + || (cmd_opts.perm_vector = apol_vector_create(free)) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + break; + case 'b': + if (optarg == 0) { + usage(argv[0], 1); + printf("Missing boolean for -b (--bool)\n"); + exit(1); + } + cmd_opts.bool_name = strdup(optarg); + if (!cmd_opts.bool_name) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + break; + case 'd': /* direct search */ + cmd_opts.indirect = false; + break; + case 'R': /* use regex */ + cmd_opts.useregex = true; + break; + case 'A': /* allow */ + cmd_opts.allow = true; + break; + case RULE_NEVERALLOW: /* neverallow */ + cmd_opts.nallow = true; + break; + case RULE_AUDIT: /* audit */ + cmd_opts.auditallow = true; + cmd_opts.dontaudit = true; + fprintf(stderr, "Use of --audit is deprecated; use --auditallow and --dontaudit instead.\n"); + break; + case RULE_AUDITALLOW: + cmd_opts.auditallow = true; + break; + case RULE_DONTAUDIT: + cmd_opts.dontaudit = true; + break; + case 'T': /* type */ + cmd_opts.type = true; + break; + case RULE_ROLE_ALLOW: + cmd_opts.role_allow = true; + break; + case RULE_ROLE_TRANS: + cmd_opts.role_trans = true; + break; + case RULE_RANGE_TRANS: /* range transition */ + cmd_opts.rtrans = true; + break; + case RULE_ALL: /* all */ + cmd_opts.all = true; + break; + case 'n': /* lineno */ + cmd_opts.lineno = true; + break; + case 'S': /* semantic */ + cmd_opts.semantic = true; + break; + case 'C': + cmd_opts.show_cond = true; + break; + case 'h': /* help */ + usage(argv[0], 0); + exit(0); + case 'V': /* version */ + printf("sesearch %s\n%s\n", VERSION, COPYRIGHT_INFO); + exit(0); + default: + usage(argv[0], 1); + exit(1); + } + } + + if (!(cmd_opts.allow || cmd_opts.nallow || cmd_opts.auditallow || cmd_opts.dontaudit || cmd_opts.role_allow || + cmd_opts.type || cmd_opts.rtrans || cmd_opts.role_trans || cmd_opts.all)) { + usage(argv[0], 1); + fprintf(stderr, "One of --all, --allow, --neverallow, --auditallow, --dontaudit,\n" + "--range_trans, --type, --role_allow, or --role_trans must be specified.\n"); + exit(1); + } + + int pol_opt = 0; + if (!(cmd_opts.nallow || cmd_opts.all)) + pol_opt |= QPOL_POLICY_OPTION_NO_NEVERALLOWS; + + if (argc - optind < 1) { + rt = qpol_default_policy_find(&policy_file); + if (rt < 0) { + fprintf(stderr, "Default policy search failed: %s\n", strerror(errno)); + exit(1); + } else if (rt != 0) { + fprintf(stderr, "No default policy found.\n"); + exit(1); + } + pol_opt |= QPOL_POLICY_OPTION_MATCH_SYSTEM; + } else { + if ((policy_file = strdup(argv[optind])) == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + optind++; + } + + if (argc - optind > 0) { + path_type = APOL_POLICY_PATH_TYPE_MODULAR; + if (!(mod_paths = apol_vector_create(NULL))) { + ERR(policy, "%s", strerror(ENOMEM)); + exit(1); + } + for (; argc - optind; optind++) { + if (apol_vector_append(mod_paths, (void *)argv[optind])) { + ERR(policy, "Error loading module %s", argv[optind]); + apol_vector_destroy(&mod_paths); + free(policy_file); + exit(1); + } + } + } else if (apol_file_is_policy_path_list(policy_file) > 0) { + pol_path = apol_policy_path_create_from_file(policy_file); + if (!pol_path) { + ERR(policy, "%s", "invalid policy list"); + free(policy_file); + exit(1); + } + } + + if (!pol_path) + pol_path = apol_policy_path_create(path_type, policy_file, mod_paths); + if (!pol_path) { + ERR(policy, "%s", strerror(ENOMEM)); + free(policy_file); + apol_vector_destroy(&mod_paths); + exit(1); + } + free(policy_file); + apol_vector_destroy(&mod_paths); + + policy = apol_policy_create_from_policy_path(pol_path, pol_opt, NULL, NULL); + if (!policy) { + ERR(policy, "%s", strerror(errno)); + apol_policy_path_destroy(&pol_path); + exit(1); + } + /* handle regex for class name */ + if (cmd_opts.useregex && cmd_opts.class_name != NULL) { + cmd_opts.class_vector = apol_vector_create(NULL); + apol_vector_t *qpol_matching_classes = NULL; + apol_class_query_t *regex_match_query = apol_class_query_create(); + apol_class_query_set_regex(policy, regex_match_query, 1); + apol_class_query_set_class(policy, regex_match_query, cmd_opts.class_name); + if (apol_class_get_by_query(policy, regex_match_query, &qpol_matching_classes)) { + apol_class_query_destroy(®ex_match_query); + goto cleanup; + } + const qpol_class_t *class = NULL; + for (size_t i = 0; i < apol_vector_get_size(qpol_matching_classes); ++i) { + const char *class_name; + class = apol_vector_get_element(qpol_matching_classes, i); + if (!class) + break; + qpol_class_get_name(apol_policy_get_qpol(policy), class, &class_name); + apol_vector_append(cmd_opts.class_vector, (void *)class_name); + } + if (!apol_vector_get_size(qpol_matching_classes)) { + apol_vector_destroy(&qpol_matching_classes); + apol_class_query_destroy(®ex_match_query); + ERR(policy, "No classes match expression %s", cmd_opts.class_name); + goto cleanup; + } + apol_vector_destroy(&qpol_matching_classes); + apol_class_query_destroy(®ex_match_query); + } + + if (!cmd_opts.semantic && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) { + if (qpol_policy_build_syn_rule_table(apol_policy_get_qpol(policy))) { + apol_policy_destroy(&policy); + exit(1); + } + } + + /* if syntactic rules are not available always do semantic search */ + if (!qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) { + cmd_opts.semantic = 1; + } + + /* supress line numbers if doing semantic search or not available */ + if (cmd_opts.semantic || !qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_LINE_NUMBERS)) { + cmd_opts.lineno = 0; + } + + if (perform_av_query(policy, &cmd_opts, &v)) { + rt = 1; + goto cleanup; + } + if (v) { + if (!cmd_opts.semantic && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) + print_syn_av_results(policy, &cmd_opts, v); + else + print_av_results(policy, &cmd_opts, v); + fprintf(stdout, "\n"); + } + apol_vector_destroy(&v); + if (perform_te_query(policy, &cmd_opts, &v)) { + rt = 1; + goto cleanup; + } + if (v) { + if (!cmd_opts.semantic && qpol_policy_has_capability(apol_policy_get_qpol(policy), QPOL_CAP_SYN_RULES)) + print_syn_te_results(policy, &cmd_opts, v); + else + print_te_results(policy, &cmd_opts, v); + fprintf(stdout, "\n"); + } + apol_vector_destroy(&v); + if (perform_ra_query(policy, &cmd_opts, &v)) { + rt = 1; + goto cleanup; + } + if (v) { + print_ra_results(policy, &cmd_opts, v); + fprintf(stdout, "\n"); + } + apol_vector_destroy(&v); + if (perform_rt_query(policy, &cmd_opts, &v)) { + rt = 1; + goto cleanup; + } + if (v) { + print_rt_results(policy, &cmd_opts, v); + fprintf(stdout, "\n"); + } + apol_vector_destroy(&v); + if (perform_range_query(policy, &cmd_opts, &v)) { + rt = 1; + goto cleanup; + } + if (v) { + print_range_results(policy, &cmd_opts, v); + fprintf(stdout, "\n"); + } + apol_vector_destroy(&v); + rt = 0; + cleanup: + apol_policy_destroy(&policy); + apol_policy_path_destroy(&pol_path); + free(cmd_opts.src_name); + free(cmd_opts.tgt_name); + free(cmd_opts.class_name); + free(cmd_opts.permlist); + free(cmd_opts.bool_name); + free(cmd_opts.src_role_name); + free(cmd_opts.tgt_role_name); + apol_vector_destroy(&cmd_opts.perm_vector); + apol_vector_destroy(&cmd_opts.class_vector); + exit(rt); +} |