2006-04-11 21:52:54 +00:00
|
|
|
/*
|
2007-04-11 08:12:51 +00:00
|
|
|
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
|
|
|
* NOVELL (All rights reserved)
|
2006-04-11 21:52:54 +00:00
|
|
|
*
|
2013-10-15 16:46:18 -07:00
|
|
|
* Copyright (c) 2010 - 2013
|
2012-02-24 04:21:59 -08:00
|
|
|
* Canonical Ltd. (All rights reserved)
|
2010-03-12 15:26:32 -08:00
|
|
|
*
|
2006-04-11 21:52:54 +00:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of version 2 of the GNU General Public
|
|
|
|
* License published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* 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
|
2010-03-12 15:26:32 -08:00
|
|
|
* along with this program; if not, contact Novell, Inc. or Canonical,
|
|
|
|
* Ltd.
|
2006-04-11 21:52:54 +00:00
|
|
|
*/
|
|
|
|
|
2011-08-09 06:52:43 -07:00
|
|
|
#include <ctype.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
#include <stdarg.h>
|
2012-08-13 16:58:33 -07:00
|
|
|
#include <stddef.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
#include <getopt.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2012-02-24 04:18:45 -08:00
|
|
|
#include <dirent.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
/* enable the following line to get voluminous debug info */
|
|
|
|
/* #define DEBUG */
|
|
|
|
|
|
|
|
#include <unistd.h>
|
2011-05-02 13:34:58 -07:00
|
|
|
#include <limits.h>
|
2010-07-26 09:22:45 -07:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2014-01-06 14:46:10 -08:00
|
|
|
#include <sys/apparmor.h>
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2012-08-16 16:26:03 -07:00
|
|
|
#include "lib.h"
|
2015-03-25 17:09:25 -05:00
|
|
|
#include "features.h"
|
2015-03-25 17:09:26 -05:00
|
|
|
#include "kernel_interface.h"
|
2006-04-11 21:52:54 +00:00
|
|
|
#include "parser.h"
|
|
|
|
#include "parser_version.h"
|
|
|
|
#include "parser_include.h"
|
2014-04-23 11:10:41 -07:00
|
|
|
#include "common_optarg.h"
|
2015-03-25 17:09:25 -05:00
|
|
|
#include "policy_cache.h"
|
2010-01-07 16:21:02 -08:00
|
|
|
#include "libapparmor_re/apparmor_re.h"
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
#define OLD_MODULE_NAME "subdomain"
|
|
|
|
#define PROC_MODULES "/proc/modules"
|
2014-04-23 10:53:46 -07:00
|
|
|
#define MATCH_FILE "/sys/kernel/security/" MODULE_NAME "/matching"
|
2006-04-11 21:52:54 +00:00
|
|
|
#define MOUNTED_FS "/proc/mounts"
|
2007-02-27 02:29:16 +00:00
|
|
|
#define AADFA "pattern=aadfa"
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2010-09-14 12:45:34 -07:00
|
|
|
#define PRIVILEGED_OPS (kernel_load)
|
2010-01-07 18:20:19 -08:00
|
|
|
#define UNPRIVILEGED_OPS (!(PRIVILEGED_OPS))
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2011-02-22 14:58:49 -08:00
|
|
|
const char *parser_title = "AppArmor parser";
|
2012-02-24 04:21:59 -08:00
|
|
|
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2012 Canonical Ltd.";
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2009-07-24 07:35:39 +00:00
|
|
|
int opt_force_complain = 0;
|
2008-06-09 10:00:28 +00:00
|
|
|
int binary_input = 0;
|
2006-04-11 21:52:54 +00:00
|
|
|
int dump_vars = 0;
|
|
|
|
int dump_expanded_vars = 0;
|
2009-07-24 07:36:09 +00:00
|
|
|
int show_cache = 0;
|
2009-08-19 14:44:40 +00:00
|
|
|
int skip_cache = 0;
|
|
|
|
int skip_read_cache = 0;
|
2009-07-24 07:36:09 +00:00
|
|
|
int write_cache = 0;
|
2012-08-13 16:58:33 -07:00
|
|
|
int cond_clear_cache = 1; /* only applies if write is set */
|
|
|
|
int force_clear_cache = 0; /* force clearing regargless of state */
|
2013-09-29 02:04:55 -07:00
|
|
|
int create_cache_dir = 0; /* create the cache dir if missing? */
|
2010-06-26 13:13:52 -07:00
|
|
|
int preprocess_only = 0;
|
2010-07-10 17:46:06 -07:00
|
|
|
int skip_mode_force = 0;
|
2014-09-23 14:24:40 -07:00
|
|
|
int abort_on_error = 0; /* stop processing profiles if error */
|
2014-09-23 14:33:54 -07:00
|
|
|
int skip_bad_cache_rebuild = 0;
|
2015-03-25 17:09:25 -05:00
|
|
|
int mru_skip_cache = 1;
|
|
|
|
int debug_cache = 0;
|
2010-09-14 12:22:02 -07:00
|
|
|
struct timespec mru_tstamp;
|
2009-06-10 15:37:27 +00:00
|
|
|
|
2012-01-11 17:25:18 +01:00
|
|
|
char *cacheloc = NULL;
|
2008-09-10 08:42:49 +00:00
|
|
|
|
2014-02-05 09:10:53 -05:00
|
|
|
/* Make sure to update BOTH the short and long_options */
|
2014-04-23 10:53:46 -07:00
|
|
|
static const char *short_options = "adf:h::rRVvI:b:BCD:NSm:M:qQn:XKTWkL:O:po:";
|
2006-04-11 21:52:54 +00:00
|
|
|
struct option long_options[] = {
|
|
|
|
{"add", 0, 0, 'a'},
|
2008-06-09 10:00:28 +00:00
|
|
|
{"binary", 0, 0, 'B'},
|
2006-04-11 21:52:54 +00:00
|
|
|
{"base", 1, 0, 'b'},
|
2013-06-26 11:26:43 -07:00
|
|
|
{"subdomainfs", 1, 0, 'f'},
|
2010-01-07 16:21:02 -08:00
|
|
|
{"help", 2, 0, 'h'},
|
2006-04-11 21:52:54 +00:00
|
|
|
{"replace", 0, 0, 'r'},
|
|
|
|
{"reload", 0, 0, 'r'}, /* undocumented reload option == replace */
|
2009-11-11 10:56:04 -08:00
|
|
|
{"version", 0, 0, 'V'},
|
2006-04-11 21:52:54 +00:00
|
|
|
{"complain", 0, 0, 'C'},
|
2009-03-12 15:21:46 +00:00
|
|
|
{"Complain", 0, 0, 'C'}, /* Erk, apparently documented as --Complain */
|
2006-04-11 21:52:54 +00:00
|
|
|
{"Include", 1, 0, 'I'},
|
|
|
|
{"remove", 0, 0, 'R'},
|
2008-06-09 22:15:28 +00:00
|
|
|
{"names", 0, 0, 'N'},
|
2006-04-11 21:52:54 +00:00
|
|
|
{"stdout", 0, 0, 'S'},
|
2010-06-26 13:13:52 -07:00
|
|
|
{"ofile", 1, 0, 'o'},
|
2006-04-18 17:15:05 +00:00
|
|
|
{"match-string", 1, 0, 'm'},
|
2014-04-23 10:53:46 -07:00
|
|
|
{"features-file", 1, 0, 'M'},
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
{"quiet", 0, 0, 'q'},
|
2009-11-11 10:56:04 -08:00
|
|
|
{"skip-kernel-load", 0, 0, 'Q'},
|
|
|
|
{"verbose", 0, 0, 'v'},
|
2007-11-16 09:18:48 +00:00
|
|
|
{"namespace", 1, 0, 'n'},
|
2009-06-10 15:37:27 +00:00
|
|
|
{"readimpliesX", 0, 0, 'X'},
|
2009-07-24 07:36:09 +00:00
|
|
|
{"skip-cache", 0, 0, 'K'},
|
2009-08-19 14:44:40 +00:00
|
|
|
{"skip-read-cache", 0, 0, 'T'},
|
2009-07-24 07:36:09 +00:00
|
|
|
{"write-cache", 0, 0, 'W'},
|
|
|
|
{"show-cache", 0, 0, 'k'},
|
2012-08-13 16:58:33 -07:00
|
|
|
{"skip-bad-cache", 0, 0, 129}, /* no short option */
|
|
|
|
{"purge-cache", 0, 0, 130}, /* no short option */
|
2013-09-29 02:04:55 -07:00
|
|
|
{"create-cache-dir", 0, 0, 131}, /* no short option */
|
2012-01-11 17:25:18 +01:00
|
|
|
{"cache-loc", 1, 0, 'L'},
|
2010-01-07 16:21:02 -08:00
|
|
|
{"debug", 0, 0, 'd'},
|
|
|
|
{"dump", 1, 0, 'D'},
|
|
|
|
{"Dump", 1, 0, 'D'},
|
2010-01-08 14:33:35 -08:00
|
|
|
{"optimize", 1, 0, 'O'},
|
|
|
|
{"Optimize", 1, 0, 'O'},
|
2010-06-26 13:13:52 -07:00
|
|
|
{"preprocess", 0, 0, 'p'},
|
2014-09-23 14:24:40 -07:00
|
|
|
{"abort-on-error", 0, 0, 132}, /* no short option */
|
2014-09-23 14:33:54 -07:00
|
|
|
{"skip-bad-cache-rebuild", 0, 0, 133}, /* no short option */
|
2014-10-08 13:20:20 -07:00
|
|
|
{"warn", 1, 0, 134}, /* no short option */
|
2015-03-25 17:09:25 -05:00
|
|
|
{"debug-cache", 0, 0, 135}, /* no short option */
|
2006-04-11 21:52:54 +00:00
|
|
|
{NULL, 0, 0, 0},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int debug = 0;
|
|
|
|
|
2014-04-23 11:10:41 -07:00
|
|
|
void display_version(void)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
|
|
|
printf("%s version " PARSER_VERSION "\n%s\n", parser_title,
|
|
|
|
parser_copyright);
|
|
|
|
}
|
|
|
|
|
2013-10-01 10:59:04 -07:00
|
|
|
static void display_usage(const char *command)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
|
|
|
display_version();
|
|
|
|
printf("\nUsage: %s [options] [profile]\n\n"
|
|
|
|
"Options:\n"
|
|
|
|
"--------\n"
|
|
|
|
"-a, --add Add apparmor definitions [default]\n"
|
|
|
|
"-r, --replace Replace apparmor definitions\n"
|
|
|
|
"-R, --remove Remove apparmor definitions\n"
|
|
|
|
"-C, --Complain Force the profile into complain mode\n"
|
2008-06-09 22:15:28 +00:00
|
|
|
"-B, --binary Input is precompiled profile\n"
|
|
|
|
"-N, --names Dump names of profiles in input.\n"
|
|
|
|
"-S, --stdout Dump compiled profile to stdout\n"
|
2010-06-26 13:13:52 -07:00
|
|
|
"-o n, --ofile n Write output to file n\n"
|
2006-04-11 21:52:54 +00:00
|
|
|
"-b n, --base n Set base dir and cwd\n"
|
2008-06-09 22:15:28 +00:00
|
|
|
"-I n, --Include n Add n to the search path\n"
|
2006-04-11 21:52:54 +00:00
|
|
|
"-f n, --subdomainfs n Set location of apparmor filesystem\n"
|
2014-04-23 10:53:46 -07:00
|
|
|
"-m n, --match-string n Use only features n\n"
|
|
|
|
"-M n, --features-file n Use only features in file n\n"
|
2007-11-16 09:18:48 +00:00
|
|
|
"-n n, --namespace n Set Namespace for the profile\n"
|
2009-06-10 15:37:27 +00:00
|
|
|
"-X, --readimpliesX Map profile read permissions to mr\n"
|
2009-07-24 07:36:09 +00:00
|
|
|
"-k, --show-cache Report cache hit/miss details\n"
|
|
|
|
"-K, --skip-cache Do not attempt to load or save cached profiles\n"
|
2009-08-19 14:44:40 +00:00
|
|
|
"-T, --skip-read-cache Do not attempt to load cached profiles\n"
|
|
|
|
"-W, --write-cache Save cached profile (force with -T)\n"
|
2012-08-13 16:58:33 -07:00
|
|
|
" --skip-bad-cache Don't clear cache if out of sync\n"
|
|
|
|
" --purge-cache Clear cache regardless of its state\n"
|
2013-10-26 00:15:13 -07:00
|
|
|
" --create-cache-dir Create the cache dir if missing\n"
|
2015-03-25 17:09:25 -05:00
|
|
|
" --debug-cache Debug cache file checks\n"
|
2012-01-11 17:25:18 +01:00
|
|
|
"-L, --cache-loc n Set the location of the profile cache\n"
|
2008-06-11 20:19:36 +00:00
|
|
|
"-q, --quiet Don't emit warnings\n"
|
2010-11-04 14:27:30 -07:00
|
|
|
"-v, --verbose Show profile names as they load\n"
|
2009-11-11 10:56:04 -08:00
|
|
|
"-Q, --skip-kernel-load Do everything except loading into kernel\n"
|
|
|
|
"-V, --version Display version info and exit\n"
|
2008-06-09 22:15:28 +00:00
|
|
|
"-d, --debug Debug apparmor definitions\n"
|
2010-06-26 13:13:52 -07:00
|
|
|
"-p, --preprocess Dump preprocessed profile\n"
|
2010-01-07 16:21:02 -08:00
|
|
|
"-D [n], --dump Dump internal info for debugging\n"
|
2010-01-08 04:30:56 -08:00
|
|
|
"-O [n], --Optimize Control dfa optimizations\n"
|
2010-06-26 13:13:52 -07:00
|
|
|
"-h [cmd], --help[=cmd] Display this text or info about cmd\n"
|
2014-09-23 14:24:40 -07:00
|
|
|
"--abort-on-error Abort processing of profiles on first error\n"
|
2014-09-23 14:33:54 -07:00
|
|
|
"--skip-bad-cache-rebuild Do not try rebuilding the cache if it is rejected by the kernel\n"
|
2014-10-08 13:20:20 -07:00
|
|
|
"--warn n Enable warnings (see --help=warn)\n"
|
2010-01-07 16:21:02 -08:00
|
|
|
,command);
|
|
|
|
}
|
|
|
|
|
2014-10-08 13:20:20 -07:00
|
|
|
optflag_table_t warnflag_table[] = {
|
|
|
|
{ 0, "rule-not-enforced", "warn if a rule is not enforced", WARN_RULE_NOT_ENFORCED },
|
|
|
|
{ 0, "rule-downgraded", "warn if a rule is downgraded to a lesser but still enforcing rule", WARN_RULE_DOWNGRADED },
|
|
|
|
{ 0, NULL, NULL, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
void display_warn(const char *command)
|
|
|
|
{
|
|
|
|
display_version();
|
|
|
|
printf("\n%s: --warn [Option]\n\n"
|
|
|
|
"Options:\n"
|
|
|
|
"--------\n"
|
|
|
|
,command);
|
|
|
|
print_flag_table(warnflag_table);
|
|
|
|
}
|
2011-08-09 06:52:43 -07:00
|
|
|
|
|
|
|
/* Treat conf file like options passed on command line
|
|
|
|
*/
|
|
|
|
static int getopt_long_file(FILE *f, const struct option *longopts,
|
|
|
|
char **optarg, int *longindex)
|
|
|
|
{
|
|
|
|
static char line[256];
|
|
|
|
char *pos, *opt, *save;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
if (!fgets(line, 256, f))
|
|
|
|
return -1;
|
|
|
|
pos = line;
|
|
|
|
while (isblank(*pos))
|
|
|
|
pos++;
|
|
|
|
if (*pos == '#')
|
|
|
|
continue;
|
|
|
|
opt = strtok_r(pos, " \t\r\n=", &save);
|
|
|
|
/* blank line */
|
|
|
|
if (!opt)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (i = 0; longopts[i].name &&
|
|
|
|
strcmp(longopts[i].name, opt) != 0; i++) ;
|
|
|
|
if (!longopts[i].name) {
|
|
|
|
PERROR("%s: unknown option (%s) in config file.\n",
|
|
|
|
progname, opt);
|
|
|
|
/* skip it */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (longindex)
|
|
|
|
*longindex = i;
|
|
|
|
|
|
|
|
if (*save) {
|
|
|
|
int len;
|
|
|
|
while(isblank(*save))
|
|
|
|
save++;
|
|
|
|
len = strlen(save) - 1;
|
|
|
|
if (save[len] == '\n')
|
|
|
|
save[len] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (longopts[i].has_arg) {
|
|
|
|
case 0:
|
|
|
|
*optarg = NULL;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (!strlen(save)) {
|
|
|
|
*optarg = NULL;
|
|
|
|
return '?';
|
|
|
|
}
|
|
|
|
*optarg = save;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
*optarg = save;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
PERROR("%s: internal error bad longopt value\n", progname);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (longopts[i].flag == NULL)
|
|
|
|
return longopts[i].val;
|
|
|
|
else
|
|
|
|
*longopts[i].flag = longopts[i].val;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* process a single argment from getopt_long
|
|
|
|
* Returns: 1 if an action arg, else 0
|
|
|
|
*/
|
|
|
|
static int process_arg(int c, char *optarg)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case 0:
|
|
|
|
PERROR("Assert, in getopt_long handling\n");
|
|
|
|
display_usage(progname);
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
count++;
|
|
|
|
option = OPTION_ADD;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
debug++;
|
|
|
|
skip_read_cache = 1;
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
if (!optarg) {
|
|
|
|
display_usage(progname);
|
|
|
|
} else if (strcmp(optarg, "Dump") == 0 ||
|
|
|
|
strcmp(optarg, "dump") == 0 ||
|
|
|
|
strcmp(optarg, "D") == 0) {
|
|
|
|
display_dump(progname);
|
|
|
|
} else if (strcmp(optarg, "Optimize") == 0 ||
|
|
|
|
strcmp(optarg, "optimize") == 0 ||
|
|
|
|
strcmp(optarg, "O") == 0) {
|
|
|
|
display_optimize(progname);
|
2014-10-08 13:20:20 -07:00
|
|
|
} else if (strcmp(optarg, "warn") == 0) {
|
|
|
|
display_warn(progname);
|
2011-08-09 06:52:43 -07:00
|
|
|
} else {
|
|
|
|
PERROR("%s: Invalid --help option %s\n",
|
|
|
|
progname, optarg);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
count++;
|
|
|
|
option = OPTION_REPLACE;
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
count++;
|
|
|
|
option = OPTION_REMOVE;
|
|
|
|
skip_cache = 1;
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
display_version();
|
|
|
|
exit(0);
|
|
|
|
break;
|
|
|
|
case 'I':
|
|
|
|
add_search_dir(optarg);
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
set_base_dir(optarg);
|
|
|
|
break;
|
|
|
|
case 'B':
|
|
|
|
binary_input = 1;
|
|
|
|
skip_cache = 1;
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
opt_force_complain = 1;
|
|
|
|
skip_cache = 1;
|
|
|
|
break;
|
|
|
|
case 'N':
|
2012-08-13 16:59:00 -07:00
|
|
|
count++;
|
2011-08-09 06:52:43 -07:00
|
|
|
names_only = 1;
|
|
|
|
skip_cache = 1;
|
2012-08-13 16:59:00 -07:00
|
|
|
kernel_load = 0;
|
2011-08-09 06:52:43 -07:00
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
count++;
|
|
|
|
option = OPTION_STDOUT;
|
|
|
|
skip_read_cache = 1;
|
|
|
|
kernel_load = 0;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
count++;
|
|
|
|
option = OPTION_OFILE;
|
|
|
|
skip_read_cache = 1;
|
|
|
|
kernel_load = 0;
|
|
|
|
ofile = fopen(optarg, "w");
|
|
|
|
if (!ofile) {
|
|
|
|
PERROR("%s: Could not open file %s\n",
|
|
|
|
progname, optarg);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
subdomainbase = strndup(optarg, PATH_MAX);
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
skip_read_cache = 1;
|
|
|
|
if (!optarg) {
|
|
|
|
dump_vars = 1;
|
|
|
|
} else if (strcmp(optarg, "variables") == 0) {
|
|
|
|
dump_vars = 1;
|
|
|
|
} else if (strcmp(optarg, "expanded-variables") == 0) {
|
|
|
|
dump_expanded_vars = 1;
|
|
|
|
} else if (!handle_flag_table(dumpflag_table, optarg,
|
|
|
|
&dfaflags)) {
|
|
|
|
PERROR("%s: Invalid --Dump option %s\n",
|
|
|
|
progname, optarg);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'O':
|
|
|
|
skip_read_cache = 1;
|
|
|
|
|
|
|
|
if (!handle_flag_table(optflag_table, optarg,
|
|
|
|
&dfaflags)) {
|
|
|
|
PERROR("%s: Invalid --Optimize option %s\n",
|
|
|
|
progname, optarg);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'm':
|
2014-04-23 10:53:46 -07:00
|
|
|
features_string = strdup(optarg);
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
if (load_features(optarg) == -1) {
|
|
|
|
fprintf(stderr, "Failed to load features from '%s'\n",
|
|
|
|
optarg);
|
|
|
|
exit(1);
|
|
|
|
}
|
2011-08-09 06:52:43 -07:00
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
conf_verbose = 0;
|
|
|
|
conf_quiet = 1;
|
2014-10-08 13:20:20 -07:00
|
|
|
warnflags = 0;
|
2011-08-09 06:52:43 -07:00
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
conf_verbose = 1;
|
|
|
|
conf_quiet = 0;
|
|
|
|
break;
|
|
|
|
case 'n':
|
2013-09-27 16:13:22 -07:00
|
|
|
profile_ns = strdup(optarg);
|
2011-08-09 06:52:43 -07:00
|
|
|
break;
|
|
|
|
case 'X':
|
|
|
|
read_implies_exec = 1;
|
|
|
|
break;
|
|
|
|
case 'K':
|
|
|
|
skip_cache = 1;
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
show_cache = 1;
|
|
|
|
break;
|
|
|
|
case 'W':
|
|
|
|
write_cache = 1;
|
|
|
|
break;
|
|
|
|
case 'T':
|
|
|
|
skip_read_cache = 1;
|
|
|
|
break;
|
2012-08-13 16:58:33 -07:00
|
|
|
case 129:
|
|
|
|
cond_clear_cache = 0;
|
|
|
|
break;
|
|
|
|
case 130:
|
|
|
|
force_clear_cache = 1;
|
|
|
|
break;
|
2013-09-29 02:04:55 -07:00
|
|
|
case 131:
|
|
|
|
create_cache_dir = 1;
|
|
|
|
break;
|
2014-09-23 14:24:40 -07:00
|
|
|
case 132:
|
|
|
|
abort_on_error = 1;
|
|
|
|
break;
|
2014-09-23 14:33:54 -07:00
|
|
|
case 133:
|
|
|
|
skip_bad_cache_rebuild = 1;
|
|
|
|
break;
|
2012-01-11 17:25:18 +01:00
|
|
|
case 'L':
|
|
|
|
cacheloc = strdup(optarg);
|
|
|
|
break;
|
2011-08-09 06:52:43 -07:00
|
|
|
case 'Q':
|
|
|
|
kernel_load = 0;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
count++;
|
|
|
|
kernel_load = 0;
|
|
|
|
skip_cache = 1;
|
|
|
|
preprocess_only = 1;
|
|
|
|
skip_mode_force = 1;
|
|
|
|
break;
|
2014-10-08 13:20:20 -07:00
|
|
|
case 134:
|
|
|
|
if (!handle_flag_table(warnflag_table, optarg,
|
|
|
|
&warnflags)) {
|
|
|
|
PERROR("%s: Invalid --warn option %s\n",
|
|
|
|
progname, optarg);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
2015-03-25 17:09:25 -05:00
|
|
|
case 135:
|
|
|
|
debug_cache = 1;
|
|
|
|
break;
|
2011-08-09 06:52:43 -07:00
|
|
|
default:
|
|
|
|
display_usage(progname);
|
2014-02-20 16:53:18 -08:00
|
|
|
exit(1);
|
2011-08-09 06:52:43 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
static int process_args(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int c, o;
|
|
|
|
int count = 0;
|
[https://bugzilla.novell.com/show_bug.cgi?id=172061]
This (updated) patch to trunk adds support for Px and Ux (toggle
bprm_secure on exec) in the parser, As requested, lowercase p and u
corresponds to an unfiltered environmnet on exec, uppercase will filter
the environment. It applies after the 'm' patch.
As a side effect, I tried to reduce the use of hardcoded characters in
the debugging statements -- there are still a few warnings that have
hard coded letters in them; not sure I can fix them all.
This version issues a warning for every unsafe ux and issues a single
warning for the first 'R', 'W', 'X', 'L', and 'I' it encounters,
except when the "-q" or "--quiet" flag , "--remove" profile flag, or
"-N" report names flags are passed. Unfortunately, it made the logic
somewhat more convoluted. Wordsmithing improvements welcome.
2006-08-04 17:14:49 +00:00
|
|
|
option = OPTION_ADD;
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2014-02-05 09:10:53 -05:00
|
|
|
while ((c = getopt_long(argc, argv, short_options, long_options, &o)) != -1)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
2011-08-09 06:52:43 -07:00
|
|
|
count += process_arg(c, optarg);
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count > 1) {
|
2009-07-24 07:35:39 +00:00
|
|
|
PERROR("%s: Too many actions given on the command line.\n",
|
2006-04-11 21:52:54 +00:00
|
|
|
progname);
|
2009-07-24 07:35:39 +00:00
|
|
|
display_usage(progname);
|
|
|
|
exit(1);
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PDEBUG("optind = %d argc = %d\n", optind, argc);
|
2009-07-24 07:35:39 +00:00
|
|
|
return optind;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
2011-08-09 06:52:43 -07:00
|
|
|
static int process_config_file(const char *name)
|
|
|
|
{
|
|
|
|
char *optarg;
|
2015-03-25 17:09:26 -05:00
|
|
|
autofclose FILE *f = NULL;
|
2011-08-09 06:52:43 -07:00
|
|
|
int c, o;
|
|
|
|
|
|
|
|
f = fopen(name, "r");
|
|
|
|
if (!f)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while ((c = getopt_long_file(f, long_options, &optarg, &o)) != -1)
|
|
|
|
process_arg(c, optarg);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2007-02-27 02:29:16 +00:00
|
|
|
int find_subdomainfs_mountpoint(void)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
2015-03-25 17:09:26 -05:00
|
|
|
if (aa_find_iface_dir(&subdomainbase) == -1) {
|
2007-02-27 02:29:16 +00:00
|
|
|
PERROR(_("Warning: unable to find a suitable fs in %s, is it "
|
|
|
|
"mounted?\nUse --subdomainfs to override.\n"),
|
|
|
|
MOUNTED_FS);
|
2015-03-25 17:09:26 -05:00
|
|
|
return false;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
return true;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int have_enough_privilege(void)
|
|
|
|
{
|
|
|
|
uid_t uid, euid;
|
|
|
|
|
|
|
|
uid = getuid();
|
|
|
|
euid = geteuid();
|
|
|
|
|
|
|
|
if (uid != 0 && euid != 0) {
|
|
|
|
PERROR(_("%s: Sorry. You need root privileges to run this program.\n\n"),
|
|
|
|
progname);
|
|
|
|
display_usage(progname);
|
|
|
|
return EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uid != 0 && euid == 0) {
|
|
|
|
PERROR(_("%s: Warning! You've set this program setuid root.\n"
|
|
|
|
"Anybody who can run this program can update "
|
|
|
|
"your AppArmor profiles.\n\n"), progname);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-04-23 10:53:46 -07:00
|
|
|
static void set_features_by_match_file(void)
|
|
|
|
{
|
2015-03-25 17:09:26 -05:00
|
|
|
autofclose FILE *ms = fopen(MATCH_FILE, "r");
|
2014-04-23 10:53:46 -07:00
|
|
|
if (ms) {
|
2015-03-25 17:09:26 -05:00
|
|
|
autofree char *match_string = (char *) malloc(1000);
|
2014-04-23 10:53:46 -07:00
|
|
|
if (!match_string)
|
|
|
|
goto no_match;
|
2015-03-25 17:09:26 -05:00
|
|
|
if (!fgets(match_string, 1000, ms))
|
2014-04-23 10:53:46 -07:00
|
|
|
goto no_match;
|
2009-11-11 10:56:04 -08:00
|
|
|
if (strstr(match_string, " perms=c"))
|
|
|
|
perms_create = 1;
|
2014-04-23 10:55:46 -07:00
|
|
|
kernel_supports_network = 1;
|
2015-03-25 17:09:26 -05:00
|
|
|
return;
|
2007-02-27 02:29:16 +00:00
|
|
|
}
|
2014-04-23 10:53:46 -07:00
|
|
|
no_match:
|
|
|
|
perms_create = 1;
|
2006-04-18 17:15:05 +00:00
|
|
|
}
|
|
|
|
|
2014-04-23 10:53:46 -07:00
|
|
|
static void set_supported_features(void) {
|
2008-09-10 08:42:49 +00:00
|
|
|
|
2014-04-23 10:53:46 -07:00
|
|
|
/* has process_args() already assigned a match string? */
|
|
|
|
if (!features_string) {
|
|
|
|
if (load_features(FEATURES_FILE) == -1) {
|
|
|
|
set_features_by_match_file();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2008-09-10 08:42:49 +00:00
|
|
|
|
2014-04-23 10:53:46 -07:00
|
|
|
perms_create = 1;
|
2008-09-10 08:42:49 +00:00
|
|
|
|
2014-04-23 10:53:46 -07:00
|
|
|
/* TODO: make this real parsing and config setting */
|
2014-04-23 10:59:07 -07:00
|
|
|
if (strstr(features_string, "file {")) /* pre policydb is file= */
|
|
|
|
kernel_supports_policydb = 1;
|
|
|
|
if (strstr(features_string, "v6"))
|
2014-04-23 11:00:32 -07:00
|
|
|
kernel_abi_version = 6;
|
parser: first step implementing fine grained mediation for unix domain sockets
This patch implements parsing of fine grained mediation for unix domain
sockets, that have abstract and anonymous paths. Sockets with file
system paths are handled by regular file access rules.
The unix network rules follow the general fine grained network
rule pattern of
[<qualifiers>] af_name [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
specifically for af_unix this is
[<qualifiers>] 'unix' [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
<qualifiers> = [ 'audit' ] [ 'allow' | 'deny' ]
<access expr> = ( <access> | <access list> )
<access> = ( 'server' | 'create' | 'bind' | 'listen' | 'accept' |
'connect' | 'shutdown' | 'getattr' | 'setattr' |
'getopt' | 'setopt' |
'send' | 'receive' | 'r' | 'w' | 'rw' )
(some access modes are incompatible with some rules or require additional
parameters)
<access list> = '(' <access> ( [','] <WS> <access> )* ')'
<WS> = white space
<rule conds> = ( <type cond> | <protocol cond> )*
each cond can appear at most once
<type cond> = 'type' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<protocol cond> = 'protocol' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<local expr> = ( <path cond> | <attr cond> | <opt cond> )*
each cond can appear at most once
<peer expr> = 'peer' '=' ( <path cond> | <label cond> )+
each cond can appear at most once
<path cond> = 'path' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<label cond> = 'label' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')')
<attr cond> = 'attr' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<opt cond> = 'opt' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<AARE> = ?*[]{}^ ( see man page )
unix domain socket rules are accumulated so that the granted unix
socket permissions are the union of all the listed unix rule permissions.
unix domain socket rules are broad and general and become more restrictive
as further information is specified. Policy may be specified down to
the path and label level. The content of the communication is not
examined.
Some permissions are not compatible with all unix rules.
unix socket rule permissions are implied when a rule does not explicitly
state an access list. By default if a rule does not have an access list
all permissions that are compatible with the specified set of local
and peer conditionals are implied.
The 'server', 'r', 'w' and 'rw' permissions are aliases for other permissions.
server = (create, bind, listen, accept)
r = (receive, getattr, getopt)
w = (create, connect, send, setattr, setopt)
In addition it supports the v7 kernel abi semantics around generic
network rules. The v7 abi removes the masking unix and netlink
address families from the generic masking and uses fine grained
mediation for an address type if supplied.
This means that the rules
network unix,
network netlink,
are now enforced instead of ignored. The parser previously could accept
these but the kernel would ignore anything written to them. If a network
rule is supplied it takes precedence over the finer grained mediation
rule. If permission is not granted via a broad network access rule
fine grained mediation is applied.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-09-03 13:22:26 -07:00
|
|
|
if (strstr(features_string, "v7"))
|
|
|
|
kernel_abi_version = 7;
|
2014-05-08 09:03:13 -07:00
|
|
|
if (strstr(features_string, "set_load"))
|
|
|
|
kernel_supports_setload = 1;
|
2014-04-23 10:53:46 -07:00
|
|
|
if (strstr(features_string, "network"))
|
|
|
|
kernel_supports_network = 1;
|
parser: first step implementing fine grained mediation for unix domain sockets
This patch implements parsing of fine grained mediation for unix domain
sockets, that have abstract and anonymous paths. Sockets with file
system paths are handled by regular file access rules.
The unix network rules follow the general fine grained network
rule pattern of
[<qualifiers>] af_name [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
specifically for af_unix this is
[<qualifiers>] 'unix' [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
<qualifiers> = [ 'audit' ] [ 'allow' | 'deny' ]
<access expr> = ( <access> | <access list> )
<access> = ( 'server' | 'create' | 'bind' | 'listen' | 'accept' |
'connect' | 'shutdown' | 'getattr' | 'setattr' |
'getopt' | 'setopt' |
'send' | 'receive' | 'r' | 'w' | 'rw' )
(some access modes are incompatible with some rules or require additional
parameters)
<access list> = '(' <access> ( [','] <WS> <access> )* ')'
<WS> = white space
<rule conds> = ( <type cond> | <protocol cond> )*
each cond can appear at most once
<type cond> = 'type' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<protocol cond> = 'protocol' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<local expr> = ( <path cond> | <attr cond> | <opt cond> )*
each cond can appear at most once
<peer expr> = 'peer' '=' ( <path cond> | <label cond> )+
each cond can appear at most once
<path cond> = 'path' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<label cond> = 'label' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')')
<attr cond> = 'attr' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<opt cond> = 'opt' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<AARE> = ?*[]{}^ ( see man page )
unix domain socket rules are accumulated so that the granted unix
socket permissions are the union of all the listed unix rule permissions.
unix domain socket rules are broad and general and become more restrictive
as further information is specified. Policy may be specified down to
the path and label level. The content of the communication is not
examined.
Some permissions are not compatible with all unix rules.
unix socket rule permissions are implied when a rule does not explicitly
state an access list. By default if a rule does not have an access list
all permissions that are compatible with the specified set of local
and peer conditionals are implied.
The 'server', 'r', 'w' and 'rw' permissions are aliases for other permissions.
server = (create, bind, listen, accept)
r = (receive, getattr, getopt)
w = (create, connect, send, setattr, setopt)
In addition it supports the v7 kernel abi semantics around generic
network rules. The v7 abi removes the masking unix and netlink
address families from the generic masking and uses fine grained
mediation for an address type if supplied.
This means that the rules
network unix,
network netlink,
are now enforced instead of ignored. The parser previously could accept
these but the kernel would ignore anything written to them. If a network
rule is supplied it takes precedence over the finer grained mediation
rule. If permission is not granted via a broad network access rule
fine grained mediation is applied.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-09-03 13:22:26 -07:00
|
|
|
if (strstr(features_string, "af_unix"))
|
|
|
|
kernel_supports_unix = 1;
|
2014-04-23 10:53:46 -07:00
|
|
|
if (strstr(features_string, "mount"))
|
|
|
|
kernel_supports_mount = 1;
|
|
|
|
if (strstr(features_string, "dbus"))
|
|
|
|
kernel_supports_dbus = 1;
|
2014-04-23 11:35:29 -07:00
|
|
|
if (strstr(features_string, "signal"))
|
|
|
|
kernel_supports_signal = 1;
|
2014-04-23 11:38:04 -07:00
|
|
|
if (strstr(features_string, "ptrace {"))
|
|
|
|
kernel_supports_ptrace = 1;
|
2014-04-23 11:05:58 -07:00
|
|
|
if (strstr(features_string, "diff_encode"))
|
|
|
|
kernel_supports_diff_encode = 1;
|
|
|
|
else if (dfaflags & DFA_CONTROL_DIFF_ENCODE)
|
|
|
|
/* clear diff_encode because it is not supported */
|
|
|
|
dfaflags &= ~DFA_CONTROL_DIFF_ENCODE;
|
2008-09-10 08:42:49 +00:00
|
|
|
}
|
|
|
|
|
2013-10-26 00:15:13 -07:00
|
|
|
int process_binary(int option, const char *profilename)
|
2008-06-09 10:00:28 +00:00
|
|
|
{
|
2015-03-25 17:09:26 -05:00
|
|
|
autofree char *buffer = NULL;
|
2008-06-09 10:00:28 +00:00
|
|
|
int retval = 0, size = 0, asize = 0, rsize;
|
|
|
|
int chunksize = 1 << 14;
|
2015-03-25 17:09:26 -05:00
|
|
|
autoclose int fd = -1;
|
2008-06-09 10:00:28 +00:00
|
|
|
|
|
|
|
if (profilename) {
|
|
|
|
fd = open(profilename, O_RDONLY);
|
|
|
|
if (fd == -1) {
|
2014-09-23 14:24:40 -07:00
|
|
|
retval = errno;
|
2014-06-17 10:58:09 -07:00
|
|
|
PERROR(_("Error: Could not read binary profile or cache file %s: %s.\n"),
|
2008-06-09 10:00:28 +00:00
|
|
|
profilename, strerror(errno));
|
2014-09-23 14:24:40 -07:00
|
|
|
return retval;
|
2008-06-09 10:00:28 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fd = dup(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (asize - size == 0) {
|
2015-03-25 17:09:26 -05:00
|
|
|
buffer = (char *) realloc(buffer, chunksize);
|
2008-06-09 10:00:28 +00:00
|
|
|
asize = chunksize;
|
|
|
|
chunksize <<= 1;
|
|
|
|
if (!buffer) {
|
|
|
|
PERROR(_("Memory allocation error."));
|
2014-09-23 14:24:40 -07:00
|
|
|
return ENOMEM;
|
2008-06-09 10:00:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rsize = read(fd, buffer + size, asize - size);
|
|
|
|
if (rsize)
|
|
|
|
size += rsize;
|
|
|
|
} while (rsize > 0);
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
if (rsize == 0) {
|
|
|
|
retval = aa_load_buffer(option, buffer, size);
|
|
|
|
if (retval == -1)
|
|
|
|
retval = -errno;
|
|
|
|
} else
|
2008-06-09 10:00:28 +00:00
|
|
|
retval = rsize;
|
|
|
|
|
2009-11-11 10:56:04 -08:00
|
|
|
if (conf_verbose) {
|
2009-07-24 07:35:39 +00:00
|
|
|
switch (option) {
|
|
|
|
case OPTION_ADD:
|
|
|
|
printf(_("Cached load succeeded for \"%s\".\n"),
|
|
|
|
profilename ? profilename : "stdin");
|
|
|
|
break;
|
|
|
|
case OPTION_REPLACE:
|
|
|
|
printf(_("Cached reload succeeded for \"%s\".\n"),
|
|
|
|
profilename ? profilename : "stdin");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-06-09 10:00:28 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-10-26 00:15:13 -07:00
|
|
|
void reset_parser(const char *filename)
|
2009-07-24 07:35:39 +00:00
|
|
|
{
|
2010-09-14 12:22:02 -07:00
|
|
|
memset(&mru_tstamp, 0, sizeof(mru_tstamp));
|
2015-03-25 17:09:25 -05:00
|
|
|
mru_skip_cache = 1;
|
2009-07-24 07:35:39 +00:00
|
|
|
free_aliases();
|
|
|
|
free_symtabs();
|
|
|
|
free_policies();
|
|
|
|
reset_regex();
|
2010-06-04 18:47:44 -07:00
|
|
|
reset_include_stack(filename);
|
2009-07-24 07:35:39 +00:00
|
|
|
}
|
|
|
|
|
2010-06-26 13:14:56 -07:00
|
|
|
int test_for_dir_mode(const char *basename, const char *linkdir)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
2010-07-10 17:46:06 -07:00
|
|
|
if (!skip_mode_force) {
|
2015-03-25 17:09:26 -05:00
|
|
|
autofree char *target = NULL;
|
2010-07-10 17:46:06 -07:00
|
|
|
if (asprintf(&target, "%s/%s/%s", basedir, linkdir, basename) < 0) {
|
|
|
|
perror("asprintf");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access(target, R_OK) == 0)
|
|
|
|
rc = 1;
|
2010-06-26 13:14:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2013-10-26 00:15:13 -07:00
|
|
|
int process_profile(int option, const char *profilename)
|
2006-04-11 21:52:54 +00:00
|
|
|
{
|
|
|
|
int retval = 0;
|
2015-03-25 17:09:26 -05:00
|
|
|
autofree const char *cachename = NULL;
|
|
|
|
autofree const char *cachetmpname = NULL;
|
|
|
|
autoclose int cachetmp = -1;
|
2013-10-26 00:15:13 -07:00
|
|
|
const char *basename = NULL;
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2009-07-24 07:35:39 +00:00
|
|
|
/* per-profile states */
|
|
|
|
force_complain = opt_force_complain;
|
|
|
|
|
2010-01-07 14:44:42 -08:00
|
|
|
if (profilename) {
|
2009-07-24 07:35:39 +00:00
|
|
|
if ( !(yyin = fopen(profilename, "r")) ) {
|
|
|
|
PERROR(_("Error: Could not read profile %s: %s.\n"),
|
|
|
|
profilename, strerror(errno));
|
2014-09-23 14:24:40 -07:00
|
|
|
return errno;
|
2009-07-24 07:35:39 +00:00
|
|
|
}
|
2015-03-25 17:09:25 -05:00
|
|
|
} else {
|
2010-08-04 09:52:54 -07:00
|
|
|
pwarn("%s: cannot use or update cache, disable, or force-complain via stdin\n", progname);
|
2009-07-24 07:35:39 +00:00
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:25 -05:00
|
|
|
reset_parser(profilename);
|
|
|
|
|
2010-01-07 14:44:42 -08:00
|
|
|
if (profilename && option != OPTION_REMOVE) {
|
2009-07-24 07:35:39 +00:00
|
|
|
/* make decisions about disabled or complain-mode profiles */
|
2010-09-14 12:22:02 -07:00
|
|
|
basename = strrchr(profilename, '/');
|
2010-01-07 14:44:42 -08:00
|
|
|
if (basename)
|
|
|
|
basename++;
|
|
|
|
else
|
|
|
|
basename = profilename;
|
2009-07-24 07:35:39 +00:00
|
|
|
|
2010-06-26 13:14:56 -07:00
|
|
|
if (test_for_dir_mode(basename, "disable")) {
|
|
|
|
if (!conf_quiet)
|
|
|
|
PERROR("Skipping profile in %s/disable: %s\n", basedir, basename);
|
2009-07-24 07:35:39 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-06-26 13:14:56 -07:00
|
|
|
if (test_for_dir_mode(basename, "force-complain")) {
|
|
|
|
PERROR("Warning: found %s in %s/force-complain, forcing complain mode\n", basename, basedir);
|
|
|
|
force_complain = 1;
|
|
|
|
}
|
2009-07-24 07:36:09 +00:00
|
|
|
|
2015-03-25 17:09:25 -05:00
|
|
|
/* setup cachename and tstamp */
|
|
|
|
if (!force_complain && !skip_cache) {
|
2015-03-25 17:09:26 -05:00
|
|
|
cachename = cache_filename(cacheloc, basename);
|
|
|
|
valid_read_cache(cachename);
|
2015-03-25 17:09:25 -05:00
|
|
|
}
|
|
|
|
|
2010-09-14 12:22:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (yyin) {
|
|
|
|
yyrestart(yyin);
|
2015-03-25 17:09:25 -05:00
|
|
|
update_mru_tstamp(yyin, profilename ? profilename : "stdin");
|
2010-09-14 12:22:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
retval = yyparse();
|
|
|
|
if (retval != 0)
|
|
|
|
goto out;
|
|
|
|
|
2012-01-11 17:26:51 +01:00
|
|
|
/* Test to see if profile is for another namespace, if so disable
|
|
|
|
* caching for now
|
|
|
|
* TODO: Add support for caching profiles in an alternate namespace
|
|
|
|
* TODO: Add support for embedded namespace defines if they aren't
|
|
|
|
* removed from the language.
|
2015-03-25 17:09:26 -05:00
|
|
|
* TODO: test profile->ns NOT profile_ns (must be after parse)
|
2012-01-11 17:26:51 +01:00
|
|
|
*/
|
2013-09-27 16:13:22 -07:00
|
|
|
if (profile_ns)
|
2012-01-11 17:26:51 +01:00
|
|
|
skip_cache = 1;
|
|
|
|
|
2015-03-25 17:09:25 -05:00
|
|
|
if (cachename) {
|
2010-09-14 12:22:02 -07:00
|
|
|
/* Load a binary cache if it exists and is newest */
|
2015-03-25 17:09:26 -05:00
|
|
|
if (cache_hit(cachename)) {
|
2010-09-14 12:22:02 -07:00
|
|
|
retval = process_binary(option, cachename);
|
2014-09-23 14:33:54 -07:00
|
|
|
if (!retval || skip_bad_cache_rebuild)
|
2015-03-25 17:09:26 -05:00
|
|
|
return retval;
|
2010-09-14 12:22:02 -07:00
|
|
|
}
|
2015-03-25 17:09:26 -05:00
|
|
|
|
|
|
|
cachetmp = setup_cache_tmp(&cachetmpname, cachename);
|
2009-07-24 07:35:39 +00:00
|
|
|
}
|
|
|
|
|
2009-07-24 07:36:09 +00:00
|
|
|
if (show_cache)
|
|
|
|
PERROR("Cache miss: %s\n", profilename ? profilename : "stdin");
|
|
|
|
|
2010-06-26 13:13:52 -07:00
|
|
|
if (preprocess_only)
|
|
|
|
goto out;
|
|
|
|
|
2008-06-09 21:15:17 +00:00
|
|
|
if (names_only) {
|
|
|
|
dump_policy_names();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
if (dump_vars) {
|
|
|
|
dump_symtab();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-03-12 15:26:32 -08:00
|
|
|
retval = post_process_policy(debug);
|
2010-01-07 14:44:42 -08:00
|
|
|
if (retval != 0) {
|
|
|
|
PERROR(_("%s: Errors found in file. Aborting.\n"), progname);
|
|
|
|
goto out;
|
|
|
|
}
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
if (dump_expanded_vars) {
|
|
|
|
dump_expanded_symtab();
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug > 0) {
|
2009-03-12 16:05:19 +00:00
|
|
|
printf("----- Debugging built structures -----\n");
|
|
|
|
dump_policy();
|
2006-04-11 21:52:54 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
/* cache file generated by load_policy */
|
|
|
|
retval = load_policy(option, cachetmp);
|
|
|
|
if (retval == 0 && write_cache) {
|
|
|
|
if (cachetmp == -1) {
|
|
|
|
unlink(cachetmpname);
|
|
|
|
PERROR("Warning failed to create cache: %s\n",
|
|
|
|
basename);
|
|
|
|
} else {
|
|
|
|
install_cache(cachetmpname, cachename);
|
2009-07-24 07:36:09 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-25 17:09:26 -05:00
|
|
|
out:
|
2015-03-25 17:09:26 -05:00
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-10-26 00:15:13 -07:00
|
|
|
/* data - name of parent dir */
|
2014-10-02 12:58:54 -07:00
|
|
|
static int profile_dir_cb(DIR *dir unused, const char *name, struct stat *st,
|
2013-10-26 00:15:13 -07:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) {
|
|
|
|
const char *dirname = (const char *)data;
|
2015-03-25 17:09:26 -05:00
|
|
|
autofree char *path = NULL;
|
2013-10-26 00:15:13 -07:00
|
|
|
if (asprintf(&path, "%s/%s", dirname, name) < 0)
|
|
|
|
PERROR(_("Out of memory"));
|
|
|
|
rc = process_profile(option, path);
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* data - name of parent dir */
|
2014-10-02 12:58:54 -07:00
|
|
|
static int binary_dir_cb(DIR *dir unused, const char *name, struct stat *st,
|
2013-10-26 00:15:13 -07:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) {
|
|
|
|
const char *dirname = (const char *)data;
|
2015-03-25 17:09:26 -05:00
|
|
|
autofree char *path = NULL;
|
2013-10-26 00:15:13 -07:00
|
|
|
if (asprintf(&path, "%s/%s", dirname, name) < 0)
|
|
|
|
PERROR(_("Out of memory"));
|
|
|
|
rc = process_binary(option, path);
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-11-11 10:56:04 -08:00
|
|
|
static void setup_flags(void)
|
|
|
|
{
|
2015-03-25 17:09:26 -05:00
|
|
|
autofree char *cache_features_path = NULL;
|
|
|
|
autofree char *cache_flags = NULL;
|
2009-11-11 10:56:04 -08:00
|
|
|
|
|
|
|
/* Get the match string to determine type of regex support needed */
|
2014-04-23 10:53:46 -07:00
|
|
|
set_supported_features();
|
|
|
|
|
2010-09-16 10:24:50 -07:00
|
|
|
/* Gracefully handle AppArmor kernel without compatibility patch */
|
2014-04-23 10:53:46 -07:00
|
|
|
if (!features_string) {
|
2010-09-16 10:24:50 -07:00
|
|
|
PERROR("Cache read/write disabled: %s interface file missing. "
|
|
|
|
"(Kernel needs AppArmor 2.4 compatibility patch.)\n",
|
2014-04-23 10:53:46 -07:00
|
|
|
FEATURES_FILE);
|
2010-09-16 10:24:50 -07:00
|
|
|
write_cache = 0;
|
|
|
|
skip_read_cache = 1;
|
|
|
|
return;
|
2014-04-23 10:53:46 -07:00
|
|
|
}
|
2012-07-17 16:03:32 -07:00
|
|
|
|
2009-11-11 10:56:04 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Deal with cache directory versioning:
|
|
|
|
* - If cache/.features is missing, create it if --write-cache.
|
2014-04-23 10:53:46 -07:00
|
|
|
* - If cache/.features exists, and does not match features_string,
|
2009-11-11 10:56:04 -08:00
|
|
|
* force cache reading/writing off.
|
|
|
|
*/
|
2013-09-29 02:02:02 -07:00
|
|
|
if (asprintf(&cache_features_path, "%s/.features", cacheloc) == -1) {
|
|
|
|
PERROR(_("Memory allocation error."));
|
2009-11-11 10:56:04 -08:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2014-04-23 10:53:46 -07:00
|
|
|
cache_flags = load_features_file(cache_features_path);
|
2009-11-11 10:56:04 -08:00
|
|
|
if (cache_flags) {
|
2014-04-23 10:53:46 -07:00
|
|
|
if (strcmp(features_string, cache_flags) != 0) {
|
2013-10-14 17:35:29 -07:00
|
|
|
if (write_cache && cond_clear_cache) {
|
2013-09-29 02:03:37 -07:00
|
|
|
if (create_cache(cacheloc, cache_features_path,
|
2014-04-23 10:53:46 -07:00
|
|
|
features_string))
|
2012-08-13 16:58:33 -07:00
|
|
|
skip_read_cache = 1;
|
|
|
|
} else {
|
|
|
|
if (show_cache)
|
2014-04-23 10:53:46 -07:00
|
|
|
PERROR("Cache read/write disabled: %s does not match %s\n", FEATURES_FILE, cache_features_path);
|
2012-08-13 16:58:33 -07:00
|
|
|
write_cache = 0;
|
|
|
|
skip_read_cache = 1;
|
|
|
|
}
|
2009-11-11 10:56:04 -08:00
|
|
|
}
|
2012-08-13 16:58:33 -07:00
|
|
|
} else if (write_cache) {
|
2014-04-23 10:53:46 -07:00
|
|
|
create_cache(cacheloc, cache_features_path, features_string);
|
2009-11-11 10:56:04 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-04-11 21:52:54 +00:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2014-09-23 14:24:40 -07:00
|
|
|
int retval, last_error;
|
2009-07-24 07:35:39 +00:00
|
|
|
int i;
|
|
|
|
int optind;
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
/* name of executable, for error reporting and usage display */
|
|
|
|
progname = argv[0];
|
|
|
|
|
|
|
|
init_base_dir();
|
|
|
|
|
2011-08-09 06:52:43 -07:00
|
|
|
process_config_file("/etc/apparmor/parser.conf");
|
2009-07-24 07:35:39 +00:00
|
|
|
optind = process_args(argc, argv);
|
2006-04-11 21:52:54 +00:00
|
|
|
|
|
|
|
setlocale(LC_MESSAGES, "");
|
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
|
|
textdomain(PACKAGE);
|
|
|
|
|
|
|
|
/* Check to see if we have superuser rights, if we're not
|
|
|
|
* debugging */
|
|
|
|
if (!(UNPRIVILEGED_OPS) && ((retval = have_enough_privilege()))) {
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-09-29 02:02:02 -07:00
|
|
|
/* create the cacheloc once and use it everywhere */
|
|
|
|
if (!cacheloc) {
|
|
|
|
if (asprintf(&cacheloc, "%s/cache", basedir) == -1) {
|
|
|
|
PERROR(_("Memory allocation error."));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-13 16:58:33 -07:00
|
|
|
if (force_clear_cache)
|
2013-09-29 02:02:02 -07:00
|
|
|
exit(clear_cache_files(cacheloc));
|
2012-08-13 16:58:33 -07:00
|
|
|
|
2007-02-27 02:29:16 +00:00
|
|
|
/* Check to make sure there is an interface to load policy */
|
|
|
|
if (!(UNPRIVILEGED_OPS) && (subdomainbase == NULL) &&
|
2015-03-25 17:09:26 -05:00
|
|
|
!find_subdomainfs_mountpoint()) {
|
|
|
|
return 1;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|
|
|
|
|
2009-07-24 07:35:39 +00:00
|
|
|
if (!binary_input) parse_default_paths();
|
|
|
|
|
2009-11-11 10:56:04 -08:00
|
|
|
setup_flags();
|
|
|
|
|
2014-09-23 14:24:40 -07:00
|
|
|
retval = last_error = 0;
|
|
|
|
for (i = optind; i <= argc; i++) {
|
2013-10-26 00:15:13 -07:00
|
|
|
struct stat stat_file;
|
|
|
|
|
2009-07-24 07:35:39 +00:00
|
|
|
if (i < argc && !(profilename = strdup(argv[i]))) {
|
|
|
|
perror("strdup");
|
2014-09-23 14:24:40 -07:00
|
|
|
last_error = ENOMEM;
|
|
|
|
if (abort_on_error)
|
|
|
|
break;
|
|
|
|
continue;
|
2009-07-24 07:35:39 +00:00
|
|
|
}
|
|
|
|
/* skip stdin if we've seen other command line arguments */
|
|
|
|
if (i == argc && optind != argc)
|
|
|
|
continue;
|
|
|
|
|
2013-10-26 00:15:13 -07:00
|
|
|
if (profilename && stat(profilename, &stat_file) == -1) {
|
|
|
|
PERROR("File %s not found, skipping...\n", profilename);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (profilename && S_ISDIR(stat_file.st_mode)) {
|
|
|
|
int (*cb)(DIR *dir, const char *name, struct stat *st,
|
|
|
|
void *data);
|
|
|
|
cb = binary_input ? binary_dir_cb : profile_dir_cb;
|
2014-09-23 14:24:40 -07:00
|
|
|
if ((retval = dirat_for_each(NULL, profilename, profilename, cb))) {
|
2013-10-26 00:15:13 -07:00
|
|
|
PDEBUG("Failed loading profiles from %s\n",
|
|
|
|
profilename);
|
|
|
|
}
|
|
|
|
} else if (binary_input) {
|
2009-07-24 07:35:39 +00:00
|
|
|
retval = process_binary(option, profilename);
|
|
|
|
} else {
|
|
|
|
retval = process_profile(option, profilename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (profilename) free(profilename);
|
|
|
|
profilename = NULL;
|
2014-09-23 14:24:40 -07:00
|
|
|
|
|
|
|
if (retval) {
|
|
|
|
last_error = retval;
|
|
|
|
if (abort_on_error)
|
|
|
|
break;
|
|
|
|
}
|
2008-06-09 10:00:28 +00:00
|
|
|
}
|
2006-04-11 21:52:54 +00:00
|
|
|
|
2010-06-26 13:13:52 -07:00
|
|
|
if (ofile)
|
|
|
|
fclose(ofile);
|
|
|
|
|
2014-09-23 14:24:40 -07:00
|
|
|
return last_error;
|
2006-04-11 21:52:54 +00:00
|
|
|
}
|