mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
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.
This commit is contained in:
parent
cafbfe7cd3
commit
3cb147e25c
7 changed files with 132 additions and 38 deletions
|
@ -34,7 +34,8 @@
|
|||
#define POS_KERN_COD_EXEC_UNCONSTRAINED (POS_KERN_COD_EXEC_INHERIT + 1)
|
||||
#define POS_KERN_COD_EXEC_PROFILE (POS_KERN_COD_EXEC_UNCONSTRAINED + 1)
|
||||
#define POS_KERN_COD_EXEC_MMAP (POS_KERN_COD_EXEC_PROFILE + 1)
|
||||
#define POS_KERN_COD_FILE_MAX POS_KERN_COD_EXEC_MMAP
|
||||
#define POS_KERN_COD_EXEC_UNSAFE (POS_KERN_COD_EXEC_MMAP + 1)
|
||||
#define POS_KERN_COD_FILE_MAX POS_KERN_COD_EXEC_UNSAFE
|
||||
|
||||
#define POS_KERN_COD_NET_MIN (POS_KERN_COD_FILE_MAX + 1)
|
||||
#define POS_KERN_COD_TCP_CONNECT POS_KERN_COD_NET_MIN
|
||||
|
@ -62,6 +63,7 @@
|
|||
#define KERN_COD_EXEC_UNCONSTRAINED (0x01 << POS_KERN_COD_EXEC_UNCONSTRAINED)
|
||||
#define KERN_COD_EXEC_PROFILE (0x01 << POS_KERN_COD_EXEC_PROFILE)
|
||||
#define KERN_COD_EXEC_MMAP (0x01 << POS_KERN_COD_EXEC_MMAP)
|
||||
#define KERN_COD_EXEC_UNSAFE (0x01 << POS_KERN_COD_EXEC_UNSAFE)
|
||||
#define KERN_EXEC_MODIFIERS(X) (X & (KERN_COD_EXEC_INHERIT | \
|
||||
KERN_COD_EXEC_UNCONSTRAINED | \
|
||||
KERN_COD_EXEC_PROFILE))
|
||||
|
|
|
@ -105,8 +105,10 @@ struct var_string {
|
|||
#define COD_EXEC_CHAR 'x'
|
||||
#define COD_INHERIT_CHAR 'i'
|
||||
#define COD_LINK_CHAR 'l'
|
||||
#define COD_UNCONSTRAINED_CHAR 'u'
|
||||
#define COD_PROFILE_CHAR 'p'
|
||||
#define COD_UNCONSTRAINED_CHAR 'U'
|
||||
#define COD_UNSAFE_UNCONSTRAINED_CHAR 'u'
|
||||
#define COD_PROFILE_CHAR 'P'
|
||||
#define COD_UNSAFE_PROFILE_CHAR 'p'
|
||||
#define COD_MMAP_CHAR 'm'
|
||||
|
||||
#define OPTION_ADD 1
|
||||
|
@ -121,9 +123,6 @@ struct var_string {
|
|||
#endif
|
||||
#define NPDEBUG(fmt, args...) /* Do nothing */
|
||||
|
||||
/* FIXME: PWARN needs to become a true function so we can i18n-ize calls
|
||||
* to it */
|
||||
#define PWARN(fmt, args...) fprintf(stderr, _("Warning (line %d): " fmt), current_lineno, ## args)
|
||||
#define PERROR(fmt, args...) fprintf(stderr, fmt, ## args)
|
||||
|
||||
#ifndef TRUE
|
||||
|
@ -147,6 +146,7 @@ extern char *profilename;
|
|||
|
||||
/* from parser_main */
|
||||
extern int force_complain;
|
||||
extern void pwarn(char *fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
|
||||
|
||||
extern int yyparse(void);
|
||||
extern void yyerror(char *msg, ...);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -55,13 +56,16 @@ const char *parser_title = "Novell/SUSE AppArmor parser";
|
|||
const char *parser_copyright = "Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 Novell Inc.";
|
||||
|
||||
char *progname;
|
||||
int option = OPTION_ADD;
|
||||
int force_complain = 0;
|
||||
int names_only = 0;
|
||||
int dump_vars = 0;
|
||||
int dump_expanded_vars = 0;
|
||||
int conf_quiet = 0;
|
||||
char *subdomainbase = NULL;
|
||||
char *profilename;
|
||||
char *match_string = NULL;
|
||||
extern int current_lineno;
|
||||
|
||||
struct option long_options[] = {
|
||||
{"add", 0, 0, 'a'},
|
||||
|
@ -81,6 +85,7 @@ struct option long_options[] = {
|
|||
{"names", 0, 0, 'N'}, /* undocumented only emit profilenames */
|
||||
{"stdout", 0, 0, 'S'},
|
||||
{"match-string", 1, 0, 'm'},
|
||||
{"quiet", 0, 0, 'q'},
|
||||
{NULL, 0, 0, 0},
|
||||
};
|
||||
|
||||
|
@ -110,16 +115,38 @@ static void display_usage(char *command)
|
|||
"-b n, --base n Set base dir and cwd\n"
|
||||
"-f n, --subdomainfs n Set location of apparmor filesystem\n"
|
||||
"-S, --stdout Write output to stdout\n"
|
||||
"-m n, --match-string n Use only match features n\n", command);
|
||||
"-m n, --match-string n Use only match features n\n"
|
||||
"-q, --quiet Don't emit warnings\n", command);
|
||||
}
|
||||
|
||||
void pwarn(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char *newfmt;
|
||||
int rc;
|
||||
|
||||
if (conf_quiet || names_only || option == OPTION_REMOVE)
|
||||
return;
|
||||
|
||||
rc = asprintf(&newfmt, "Warning (%s line %d): %s",
|
||||
profilename ? profilename : "stdin",
|
||||
current_lineno,
|
||||
fmt);
|
||||
if (!newfmt)
|
||||
return;
|
||||
|
||||
va_start(arg, fmt);
|
||||
vfprintf(stderr, newfmt, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
static int process_args(int argc, char *argv[])
|
||||
{
|
||||
int c, o;
|
||||
int option = OPTION_ADD;
|
||||
int count = 0;
|
||||
option = OPTION_ADD;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "adf:hrRvpI:b:CNSm:", long_options, &o)) != -1)
|
||||
while ((c = getopt_long(argc, argv, "adf:hrRvpI:b:CNSm:q", long_options, &o)) != -1)
|
||||
{
|
||||
switch (c) {
|
||||
case 0:
|
||||
|
@ -182,6 +209,9 @@ static int process_args(int argc, char *argv[])
|
|||
case 'm':
|
||||
match_string = strdup(optarg);
|
||||
break;
|
||||
case 'q':
|
||||
conf_quiet = 1;
|
||||
break;
|
||||
default:
|
||||
display_usage(progname);
|
||||
exit(0);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "parser.h"
|
||||
|
||||
|
||||
static inline int count_net_entries(struct codomain *cod)
|
||||
{
|
||||
struct cod_net_entry *list;
|
||||
|
@ -73,14 +74,23 @@ static int process_file_entries(struct codomain *cod)
|
|||
qsort(table, count, sizeof(struct cod_entry *), file_comp);
|
||||
table[count] = NULL;
|
||||
|
||||
#define CHECK_CONFLICT_UNSAFE(a, b) \
|
||||
(((a & KERN_COD_EXEC_UNSAFE) ^ (b & KERN_COD_EXEC_UNSAFE)) && \
|
||||
(KERN_EXEC_MODIFIERS(a) & ~KERN_COD_EXEC_INHERIT) && \
|
||||
(KERN_EXEC_MODIFIERS(b) & ~KERN_COD_EXEC_INHERIT))
|
||||
|
||||
/* walk the sorted table merging similar entries */
|
||||
for (cur = table[0], next = table[1], n = 1; next != NULL; n++, next = table[n]) {
|
||||
if (file_comp(&cur, &next) == 0) {
|
||||
int conflict = CHECK_CONFLICT_UNSAFE(cur->mode, next->mode);
|
||||
PDEBUG("%s: cur_mode: %x next_mode: %x conflict %d\n",
|
||||
__FUNCTION__, cur->mode, next->mode, conflict);
|
||||
cur->mode |= next->mode;
|
||||
/* check for merged x consistency */
|
||||
if ((KERN_COD_MAY_EXEC & cur->mode) &&
|
||||
(KERN_EXEC_MODIFIERS(cur->mode) &
|
||||
(KERN_EXEC_MODIFIERS(cur->mode) - 1))) {
|
||||
if (KERN_COD_MAY_EXEC & cur->mode &&
|
||||
((KERN_EXEC_MODIFIERS(cur->mode) &
|
||||
(KERN_EXEC_MODIFIERS(cur->mode) - 1)) ||
|
||||
conflict)) {
|
||||
PERROR(_("profile %s: has merged rule %s with multiple x modifiers\n"),
|
||||
cod->name, cur->name);
|
||||
return 0;
|
||||
|
|
|
@ -261,6 +261,16 @@ int str_to_boolean(const char *value)
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int warned_uppercase = 0;
|
||||
|
||||
static void warn_uppercase(void)
|
||||
{
|
||||
if (!warned_uppercase) {
|
||||
pwarn("Uppercase qualifiers \"RWLIMX\" are deprecated, please convert to lowercase\n"
|
||||
"See the apparmor.d(5) manpage for details.\n");
|
||||
warned_uppercase = 1;
|
||||
}
|
||||
}
|
||||
int parse_mode(const char *str_mode)
|
||||
{
|
||||
/* The 'check' int is a bit of a kludge, but we need some context
|
||||
|
@ -276,9 +286,11 @@ int parse_mode(const char *str_mode)
|
|||
|
||||
p = str_mode;
|
||||
while (*p) {
|
||||
char this = tolower(*p);
|
||||
char next = tolower(*(p + 1));
|
||||
char this = *p;
|
||||
char next = *(p + 1);
|
||||
char lower;
|
||||
|
||||
reeval:
|
||||
switch (this) {
|
||||
case COD_READ_CHAR:
|
||||
PDEBUG("Parsing mode: found READ\n");
|
||||
|
@ -297,24 +309,34 @@ int parse_mode(const char *str_mode)
|
|||
|
||||
case COD_INHERIT_CHAR:
|
||||
PDEBUG("Parsing mode: found INHERIT\n");
|
||||
if (next != COD_EXEC_CHAR) {
|
||||
if (next != COD_EXEC_CHAR && tolower(next) != COD_EXEC_CHAR) {
|
||||
yyerror(_("Exec qualifier 'i' must be followed by 'x'"));
|
||||
} else if (IS_DIFF_QUAL(this)) {
|
||||
yyerror(_("Exec qualifier 'i' invalid, conflicting qualifier already specified"));
|
||||
} else {
|
||||
if (next != tolower(next))
|
||||
warn_uppercase();
|
||||
mode |=
|
||||
(KERN_COD_EXEC_INHERIT | KERN_COD_MAY_EXEC);
|
||||
p++; /* skip 'x' */
|
||||
}
|
||||
break;
|
||||
|
||||
case COD_UNSAFE_UNCONSTRAINED_CHAR:
|
||||
mode |= KERN_COD_EXEC_UNSAFE;
|
||||
pwarn("Unconstrained exec qualifier (%c%c) allows some dangerous environment variables\n"
|
||||
"to be passed to the unconfined process; see the apparmor.d(5) manpage for details.\n",
|
||||
COD_UNSAFE_UNCONSTRAINED_CHAR, COD_EXEC_CHAR);
|
||||
/* fall through */
|
||||
case COD_UNCONSTRAINED_CHAR:
|
||||
PDEBUG("Parsing mode: found UNCONSTRAINED\n");
|
||||
if (next != COD_EXEC_CHAR) {
|
||||
if (next != COD_EXEC_CHAR && tolower(next) != COD_EXEC_CHAR) {
|
||||
yyerror(_("Exec qualifier 'u' must be followed by 'x'"));
|
||||
} else if (IS_DIFF_QUAL(this)) {
|
||||
yyerror(_("Exec qualifier 'u' invalid, conflicting qualifier already specified"));
|
||||
} else {
|
||||
if (next != tolower(next))
|
||||
warn_uppercase();
|
||||
mode |=
|
||||
(KERN_COD_EXEC_UNCONSTRAINED |
|
||||
KERN_COD_MAY_EXEC);
|
||||
|
@ -322,13 +344,18 @@ int parse_mode(const char *str_mode)
|
|||
}
|
||||
break;
|
||||
|
||||
case COD_UNSAFE_PROFILE_CHAR:
|
||||
mode |= KERN_COD_EXEC_UNSAFE;
|
||||
/* fall through */
|
||||
case COD_PROFILE_CHAR:
|
||||
PDEBUG("Parsing mode: found PROFILE\n");
|
||||
if (next != COD_EXEC_CHAR) {
|
||||
if (next != COD_EXEC_CHAR && tolower(next) != COD_EXEC_CHAR) {
|
||||
yyerror(_("Exec qualifier 'p' must be followed by 'x'"));
|
||||
} else if (IS_DIFF_QUAL(this)) {
|
||||
yyerror(_("Exec qualifier 'p' invalid, conflicting qualifier already specified"));
|
||||
} else {
|
||||
if (next != tolower(next))
|
||||
warn_uppercase();
|
||||
mode |=
|
||||
(KERN_COD_EXEC_PROFILE | KERN_COD_MAY_EXEC);
|
||||
p++; /* skip 'x' */
|
||||
|
@ -345,8 +372,28 @@ int parse_mode(const char *str_mode)
|
|||
yyerror(_("Invalid mode, 'x' must be preceded by exec qualifier 'i', 'p', or 'u'"));
|
||||
break;
|
||||
|
||||
/* error cases */
|
||||
|
||||
default:
|
||||
yyerror(_("Internal: unexpected mode character in input"));
|
||||
lower = tolower(this);
|
||||
switch (lower) {
|
||||
case COD_READ_CHAR:
|
||||
case COD_WRITE_CHAR:
|
||||
case COD_LINK_CHAR:
|
||||
case COD_INHERIT_CHAR:
|
||||
case COD_MMAP_CHAR:
|
||||
case COD_EXEC_CHAR:
|
||||
PDEBUG("Parsing mode: found invalid upper case char %c\n", this);
|
||||
warn_uppercase();
|
||||
this = lower;
|
||||
goto reeval;
|
||||
break;
|
||||
default:
|
||||
yyerror(_("Internal: unexpected mode character '%c' in input"),
|
||||
this);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
@ -510,21 +557,29 @@ void debug_cod_entries(struct cod_entry *list)
|
|||
|
||||
printf("Mode:\t");
|
||||
if (item->mode & KERN_COD_MAY_READ)
|
||||
printf("r");
|
||||
printf("%c", COD_READ_CHAR);
|
||||
if (item->mode & KERN_COD_MAY_WRITE)
|
||||
printf("w");
|
||||
if (item->mode & KERN_COD_MAY_EXEC)
|
||||
printf("x");
|
||||
printf("%c", COD_WRITE_CHAR);
|
||||
if (item->mode & KERN_COD_MAY_LINK)
|
||||
printf("l");
|
||||
printf("%c", COD_LINK_CHAR);
|
||||
if (item->mode & KERN_COD_EXEC_INHERIT)
|
||||
printf("i");
|
||||
if (item->mode & KERN_COD_EXEC_UNCONSTRAINED)
|
||||
printf("u");
|
||||
if (item->mode & KERN_COD_EXEC_PROFILE)
|
||||
printf("p");
|
||||
printf("%c", COD_INHERIT_CHAR);
|
||||
if (item->mode & KERN_COD_EXEC_UNCONSTRAINED) {
|
||||
if (item->mode & KERN_COD_EXEC_UNSAFE)
|
||||
printf("%c", COD_UNSAFE_UNCONSTRAINED_CHAR);
|
||||
else
|
||||
printf("%c", COD_UNCONSTRAINED_CHAR);
|
||||
}
|
||||
if (item->mode & KERN_COD_EXEC_PROFILE) {
|
||||
if (item->mode & KERN_COD_EXEC_UNSAFE)
|
||||
printf("%c", COD_UNSAFE_PROFILE_CHAR);
|
||||
else
|
||||
printf("%c", COD_PROFILE_CHAR);
|
||||
}
|
||||
if (item->mode & KERN_COD_EXEC_MMAP)
|
||||
printf("%c", COD_MMAP_CHAR);
|
||||
if (item->mode & KERN_COD_MAY_EXEC)
|
||||
printf("%c", COD_EXEC_CHAR);
|
||||
|
||||
if (item->name)
|
||||
printf("\tName:\t(%s)\n", item->name);
|
||||
|
@ -712,6 +767,3 @@ int main(void)
|
|||
return rc;
|
||||
}
|
||||
#endif /* UNIT_TEST */
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -814,7 +814,7 @@ ports: TOK_COLON TOK_NUM TOK_RANGE TOK_NUM
|
|||
if ((*ports)[0] > (*ports)[1])
|
||||
{
|
||||
unsigned short tmp;
|
||||
PWARN("expected first port number to be less than the second, swapping (%ld,%ld)\n",
|
||||
pwarn("expected first port number to be less than the second, swapping (%ld,%ld)\n",
|
||||
$2, $4);
|
||||
tmp = (*ports)[0];
|
||||
(*ports)[0] = (*ports)[1];
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
/does/not/exist {
|
||||
#include <includes/base>
|
||||
|
||||
/usr/X11R6/lib/lib*so* rRr,
|
||||
/usr/X11R6/lib/lib*so* rrr,
|
||||
/does/not/exist r,
|
||||
/var/log/messages wWw,
|
||||
/tmp/sd*.foo rwRWwrlL,
|
||||
/bin/cat PxpxpXPXpx,
|
||||
/bin/ls iXIXIxIX,
|
||||
/bin/echo uXUXuxUxuX,
|
||||
/var/log/messages www,
|
||||
/tmp/sd*.foo rwrwwrll,
|
||||
/bin/cat pxpxpxpxpx,
|
||||
/bin/ls ixixixix,
|
||||
/bin/echo uxuxuxuxux,
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue