2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-22 10:07:12 +00:00

parser: Add support for DBus rules

This patch implements the parsing of DBus rules.

It attempts to catch all corner cases, such as specifying a bind
permission with an interface conditional or specifying a subject name
conditional and a peer name conditional in the same rule.

It introduces the concept of conditional lists to the lexer and parser
in order to handle 'peer=(label=/usr/bin/foo name=com.foo.bar)', since
the existing list support in the lexer only supports a list of values.

The DBus rules are encoded as follows:

bus,name<bind_perm>,peer_label,path,interface,member<rw_perms>

Bind rules stop matching at name<bind_perm>. Note that name is used for
the subject name in bind rules and the peer name in rw rules. The
function new_dbus_entry() is what does the proper sanitization to make
sure that if a name conditional is specified, that it is the subject
name in the case of a bind rule or that it is the peer name in the case
of a rw rule.

Signed-off-by: Tyler Hicks <tyhicks@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
This commit is contained in:
Tyler Hicks 2013-07-31 09:05:51 -07:00
parent 1aba3394a3
commit ab84444d3a
10 changed files with 732 additions and 11 deletions

View File

@ -76,8 +76,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \ parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \ parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
parser_alias.c mount.c lib.c parser_alias.c mount.c dbus.c lib.c
HDRS = parser.h parser_include.h immunix.h mount.h lib.h HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h
TOOLS = apparmor_parser TOOLS = apparmor_parser
OBJECTS = $(SRCS:.c=.o) OBJECTS = $(SRCS:.c=.o)
@ -207,6 +207,9 @@ mount.o: mount.c mount.h parser.h immunix.h
lib.o: lib.c lib.h parser.h lib.o: lib.c lib.h parser.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $< $(CC) $(EXTRA_CFLAGS) -c -o $@ $<
dbus.o: dbus.c dbus.h parser.h immunix.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
parser_version.h: Makefile parser_version.h: Makefile
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver @echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
@mv -f .ver $@ @mv -f .ver $@

183
parser/dbus.c Normal file
View File

@ -0,0 +1,183 @@
/*
* Copyright (c) 2013
* Canonical, Ltd. (All rights reserved)
*
* 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
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#include <stdlib.h>
#include <string.h>
#include "parser.h"
#include "parser_yacc.h"
#include "dbus.h"
void free_dbus_entry(struct dbus_entry *ent)
{
if (!ent)
return;
free(ent->bus);
free(ent->name);
free(ent->peer_label);
free(ent->path);
free(ent->interface);
free(ent->member);
free(ent);
}
static int list_len(struct value_list *v)
{
int len = 0;
struct value_list *tmp;
list_for_each(v, tmp)
len++;
return len;
}
static void move_conditional_value(char **dst_ptr, struct cond_entry *cond_ent)
{
if (*dst_ptr)
yyerror("dbus conditional \"%s\" can only be specified once\n",
cond_ent->name);
*dst_ptr = cond_ent->vals->value;
cond_ent->vals->value = NULL;
}
static void move_conditionals(struct dbus_entry *ent, struct cond_entry *conds)
{
struct cond_entry *cond_ent;
list_for_each(conds, cond_ent) {
/* for now disallow keyword 'in' (list) */
if (!cond_ent->eq)
yyerror("keyword \"in\" is not allowed in dbus rules\n");
if (list_len(cond_ent->vals) > 1)
yyerror("dbus conditional \"%s\" only supports a single value\n",
cond_ent->name);
if (strcmp(cond_ent->name, "bus") == 0) {
move_conditional_value(&ent->bus, cond_ent);
} else if (strcmp(cond_ent->name, "name") == 0) {
move_conditional_value(&ent->name, cond_ent);
} else if (strcmp(cond_ent->name, "label") == 0) {
move_conditional_value(&ent->peer_label, cond_ent);
} else if (strcmp(cond_ent->name, "path") == 0) {
move_conditional_value(&ent->path, cond_ent);
} else if (strcmp(cond_ent->name, "interface") == 0) {
move_conditional_value(&ent->interface, cond_ent);
} else if (strcmp(cond_ent->name, "member") == 0) {
move_conditional_value(&ent->member, cond_ent);
} else {
yyerror("invalid dbus conditional \"%s\"\n",
cond_ent->name);
}
}
}
struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds,
struct cond_entry *peer_conds)
{
struct dbus_entry *ent;
int name_is_subject_cond = 0, message_rule = 0, service_rule = 0;
ent = (struct dbus_entry*) calloc(1, sizeof(struct dbus_entry));
if (!ent)
goto out;
/* Move the global/subject conditionals over & check the results */
move_conditionals(ent, conds);
if (ent->name)
name_is_subject_cond = 1;
if (ent->peer_label)
yyerror("dbus \"label\" conditional can only be used inside of the \"peer=()\" grouping\n");
/* Move the peer conditionals */
move_conditionals(ent, peer_conds);
if (ent->path || ent->interface || ent->member || ent->peer_label ||
(ent->name && !name_is_subject_cond))
message_rule = 1;
if (ent->name && name_is_subject_cond)
service_rule = 1;
if (message_rule && service_rule)
yyerror("dbus rule contains message conditionals and service conditionals\n");
/* Copy mode. If no mode was specified, assign an implied mode. */
if (mode) {
ent->mode = mode;
if (ent->mode & ~AA_VALID_DBUS_PERMS)
yyerror("mode contains unknown dbus accesss\n");
else if (message_rule && (ent->mode & AA_DBUS_BIND))
yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n");
else if (service_rule && (ent->mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)))
yyerror("dbus \"send\" and/or \"receive\" accesses cannot be used with service rule conditionals\n");
} else {
ent->mode = AA_VALID_DBUS_PERMS;
if (message_rule)
ent->mode &= ~AA_DBUS_BIND;
else if (service_rule)
ent->mode &= ~(AA_DBUS_SEND | AA_DBUS_RECEIVE);
}
out:
free_cond_list(conds);
return ent;
}
void print_dbus_entry(struct dbus_entry *ent)
{
if (ent->audit)
fprintf(stderr, "audit ");
if (ent->deny)
fprintf(stderr, "deny ");
fprintf(stderr, "dbus ( ");
if (ent->mode & AA_DBUS_SEND)
fprintf(stderr, "send ");
if (ent->mode & AA_DBUS_RECEIVE)
fprintf(stderr, "receive ");
if (ent->mode & AA_DBUS_BIND)
fprintf(stderr, "bind ");
fprintf(stderr, ")");
if (ent->bus)
fprintf(stderr, " bus=\"%s\"", ent->bus);
if ((ent->mode & AA_DBUS_BIND) && ent->name)
fprintf(stderr, " name=\"%s\"", ent->name);
if (ent->path)
fprintf(stderr, " path=\"%s\"", ent->path);
if (ent->interface)
fprintf(stderr, " interface=\"%s\"", ent->interface);
if (ent->member)
fprintf(stderr, " member=\"%s\"", ent->member);
if (!(ent->mode & AA_DBUS_BIND) && (ent->peer_label || ent->name)) {
fprintf(stderr, " peer=( ");
if (ent->peer_label)
fprintf(stderr, "label=\"%s\" ", ent->peer_label);
if (ent->name)
fprintf(stderr, "name=\"%s\" ", ent->name);
fprintf(stderr, ")");
}
fprintf(stderr, ",\n");
}

48
parser/dbus.h Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2013
* Canonical, Ltd. (All rights reserved)
*
* 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
* along with this program; if not, contact Novell, Inc. or Canonical
* Ltd.
*/
#ifndef __AA_DBUS_H
#define __AA_DBUS_H
#include "parser.h"
struct dbus_entry {
char *bus;
/**
* Be careful! ->name can be the subject or the peer name, depending on
* whether the rule is a bind rule or a send/receive rule. See the
* comments in new_dbus_entry() for details.
*/
char *name;
char *peer_label;
char *path;
char *interface;
char *member;
int mode;
int audit;
int deny;
struct dbus_entry *next;
};
void free_dbus_entry(struct dbus_entry *ent);
struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds,
struct cond_entry *peer_conds);
void print_dbus_entry(struct dbus_entry *ent);
#endif /* __AA_DBUS_H */

View File

@ -40,6 +40,13 @@
#define AA_EXEC_MOD_2 (1 << 12) #define AA_EXEC_MOD_2 (1 << 12)
#define AA_EXEC_MOD_3 (1 << 13) #define AA_EXEC_MOD_3 (1 << 13)
#define AA_DBUS_SEND AA_MAY_WRITE
#define AA_DBUS_RECEIVE AA_MAY_READ
#define AA_DBUS_BIND (1 << 6)
#define AA_VALID_DBUS_PERMS (AA_DBUS_SEND | AA_DBUS_RECEIVE | \
AA_DBUS_BIND)
#define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \ #define AA_BASE_PERMS (AA_MAY_EXEC | AA_MAY_WRITE | \
AA_MAY_READ | AA_MAY_APPEND | \ AA_MAY_READ | AA_MAY_APPEND | \
AA_MAY_LINK | AA_MAY_LOCK | \ AA_MAY_LINK | AA_MAY_LOCK | \

View File

@ -142,6 +142,7 @@ struct codomain {
char *exec_table[AA_EXEC_COUNT]; char *exec_table[AA_EXEC_COUNT];
struct cod_entry *entries; struct cod_entry *entries;
struct dbus_entry *dbus_ents;
struct mnt_entry *mnt_ents; struct mnt_entry *mnt_ents;
void *hat_table; void *hat_table;
@ -301,6 +302,8 @@ extern char *basedir;
/* parser_regex.c */ /* parser_regex.c */
extern int process_regex(struct codomain *cod); extern int process_regex(struct codomain *cod);
extern int post_process_entry(struct cod_entry *entry); extern int post_process_entry(struct cod_entry *entry);
extern int process_dbus(struct codomain *cod);
extern void reset_regex(void); extern void reset_regex(void);
extern int process_policydb(struct codomain *cod); extern int process_policydb(struct codomain *cod);
@ -319,6 +322,7 @@ extern void free_value_list(struct value_list *list);
extern void print_value_list(struct value_list *list); extern void print_value_list(struct value_list *list);
extern struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list); extern struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list);
extern void free_cond_entry(struct cond_entry *ent); extern void free_cond_entry(struct cond_entry *ent);
extern void free_cond_list(struct cond_entry *ents);
extern void print_cond_entry(struct cond_entry *ent); extern void print_cond_entry(struct cond_entry *ent);
extern char *processid(char *string, int len); extern char *processid(char *string, int len);
extern char *processquoted(char *string, int len); extern char *processquoted(char *string, int len);
@ -328,6 +332,7 @@ extern int name_to_capability(const char *keyword);
extern int get_rlimit(const char *name); extern int get_rlimit(const char *name);
extern char *process_var(const char *var); extern char *process_var(const char *var);
extern int parse_mode(const char *mode); extern int parse_mode(const char *mode);
extern int parse_dbus_mode(const char *str_mode, int *mode, int fail);
extern struct cod_entry *new_entry(char *namespace, char *id, int mode, extern struct cod_entry *new_entry(char *namespace, char *id, int mode,
char *link_id); char *link_id);
extern struct aa_network_entry *new_network_ent(unsigned int family, extern struct aa_network_entry *new_network_ent(unsigned int family,
@ -344,6 +349,7 @@ extern int str_to_boolean(const char* str);
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod); extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
extern void free_cod_entries(struct cod_entry *list); extern void free_cod_entries(struct cod_entry *list);
extern void free_mnt_entries(struct mnt_entry *list); extern void free_mnt_entries(struct mnt_entry *list);
extern void free_dbus_entries(struct dbus_entry *list);
/* parser_symtab.c */ /* parser_symtab.c */
struct set_value {; struct set_value {;
@ -385,6 +391,7 @@ extern void post_process_file_entries(struct codomain *cod);
extern void post_process_mnt_entries(struct codomain *cod); extern void post_process_mnt_entries(struct codomain *cod);
extern int post_process_policy(int debug_only); extern int post_process_policy(int debug_only);
extern int process_hat_regex(struct codomain *cod); extern int process_hat_regex(struct codomain *cod);
extern int process_hat_dbus(struct codomain *cod);
extern int process_hat_variables(struct codomain *cod); extern int process_hat_variables(struct codomain *cod);
extern int process_hat_policydb(struct codomain *cod); extern int process_hat_policydb(struct codomain *cod);
extern int post_merge_rules(void); extern int post_merge_rules(void);

View File

@ -53,6 +53,12 @@
#define NPDEBUG(fmt, args...) /* Do nothing */ #define NPDEBUG(fmt, args...) /* Do nothing */
#define DUMP_PREPROCESS do { if (preprocess_only) ECHO; } while (0) #define DUMP_PREPROCESS do { if (preprocess_only) ECHO; } while (0)
#define RETURN_TOKEN(X) \
do { \
DUMP_PREPROCESS; \
PDEBUG("Matched: %s\n", yytext); \
return (X); \
} while (0)
#define YY_NO_INPUT #define YY_NO_INPUT
@ -198,9 +204,11 @@ POST_VAR_ID_CHARS [^ \t\n"!,]{-}[=\+]
POST_VAR_ID {POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS}) POST_VAR_ID {POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS})
LIST_VALUE_ID_CHARS [^ \t\n"!,]{-}[()] LIST_VALUE_ID_CHARS [^ \t\n"!,]{-}[()]
LIST_VALUE_ID {LIST_VALUE_ID_CHARS}+ LIST_VALUE_ID {LIST_VALUE_ID_CHARS}+
QUOTED_LIST_VALUE_ID {LIST_VALUE_ID}|\"{LIST_VALUE_ID}\"
ID_CHARS_NOEQ [^ \t\n"!,]{-}[=] ID_CHARS_NOEQ [^ \t\n"!,]{-}[=]
LEADING_ID_CHARS_NOEQ [^ \t\n"!,]{-}[=()]
ID_NOEQ {ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ}) ID_NOEQ {ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ})
IDS_NOEQ {ID_NOEQ}+ IDS_NOEQ {LEADING_ID_CHARS_NOEQ}{ID_NOEQ}*
ALLOWED_QUOTED_ID [^\0"]|\\\" ALLOWED_QUOTED_ID [^\0"]|\\\"
QUOTED_ID \"{ALLOWED_QUOTED_ID}*\" QUOTED_ID \"{ALLOWED_QUOTED_ID}*\"
@ -228,11 +236,16 @@ LT_EQUAL <=
%x SUB_ID %x SUB_ID
%x SUB_VALUE %x SUB_VALUE
%x EXTCOND_MODE %x EXTCOND_MODE
%x EXTCONDLIST_MODE
%x NETWORK_MODE %x NETWORK_MODE
%x LIST_VAL_MODE %x LIST_VAL_MODE
%x LIST_COND_MODE
%x LIST_COND_VAL
%x LIST_COND_PAREN_VAL
%x ASSIGN_MODE %x ASSIGN_MODE
%x RLIMIT_MODE %x RLIMIT_MODE
%x MOUNT_MODE %x MOUNT_MODE
%x DBUS_MODE
%x CHANGE_PROFILE_MODE %x CHANGE_PROFILE_MODE
%x INCLUDE %x INCLUDE
@ -278,7 +291,7 @@ LT_EQUAL <=
if ( !YY_CURRENT_BUFFER ) yyterminate(); if ( !YY_CURRENT_BUFFER ) yyterminate();
} }
<INITIAL,MOUNT_MODE>{ <INITIAL,MOUNT_MODE,DBUS_MODE>{
{VARIABLE_NAME}/{WS}*= { {VARIABLE_NAME}/{WS}*= {
/* we match to the = in the lexer so that /* we match to the = in the lexer so that
* can switch scanner state. By the time * can switch scanner state. By the time
@ -286,11 +299,19 @@ LT_EQUAL <=
* as bison may have requested the next * as bison may have requested the next
* token from the scanner * token from the scanner
*/ */
int token = get_keyword_token(yytext);
DUMP_PREPROCESS; DUMP_PREPROCESS;
PDEBUG("conditional %s=\n", yytext); if (token == TOK_PEER) {
yylval.id = processid(yytext, yyleng); PDEBUG("conditional list %s=\n", yytext);
yy_push_state(EXTCOND_MODE); yy_push_state(EXTCONDLIST_MODE);
return TOK_CONDID; return TOK_CONDLISTID;
} else {
PDEBUG("conditional %s=\n", yytext);
yylval.id = processid(yytext, yyleng);
yy_push_state(EXTCOND_MODE);
return TOK_CONDID;
}
} }
{VARIABLE_NAME}/{WS}+in{WS}*\( { {VARIABLE_NAME}/{WS}+in{WS}*\( {
/* we match to 'in' in the lexer so that /* we match to 'in' in the lexer so that
@ -422,6 +443,116 @@ LT_EQUAL <=
} }
<LIST_COND_VAL>{
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
({LIST_VALUE_ID}|{QUOTED_LIST_VALUE_ID}) {
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
PDEBUG("listcond value: \"%s\"\n", yylval.id);
yy_pop_state();
return TOK_VALUE;
}
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
}
<LIST_COND_PAREN_VAL>{
{CLOSE_PAREN} {
DUMP_PREPROCESS;
yy_pop_state();
}
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
({LIST_VALUE_ID}|{QUOTED_LIST_VALUE_ID}) {
DUMP_PREPROCESS;
yylval.id = processid(yytext, yyleng);
PDEBUG("listcond paren value: \"%s\"\n", yylval.id);
return TOK_VALUE;
}
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
}
<LIST_COND_MODE>{
{CLOSE_PAREN} {
DUMP_PREPROCESS;
PDEBUG("listcond: )\n");
yy_pop_state();
return TOK_CLOSEPAREN;
}
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
{COMMA} {
DUMP_PREPROCESS;
PDEBUG("listcond: , \n");
/* Eat comma, its an optional separator */
}
{ID_CHARS_NOEQ}+ {
DUMP_PREPROCESS;
PDEBUG("listcond conditional %s=\n", yytext);
yylval.id = processid(yytext, yyleng);
return TOK_CONDID;
}
{EQUALS}{WS}*{OPEN_PAREN} {
DUMP_PREPROCESS;
yy_push_state(LIST_COND_PAREN_VAL);
return TOK_EQUALS;
}
{EQUALS} {
DUMP_PREPROCESS;
yy_push_state(LIST_COND_VAL);
return TOK_EQUALS;
}
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
}
<EXTCONDLIST_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
{EQUALS} {
DUMP_PREPROCESS;
return TOK_EQUALS;
}
{OPEN_PAREN} {
DUMP_PREPROCESS;
PDEBUG("extcondlist (\n");
/* Don't push state here as this is a transition
* start condition and we want to return to the start
* condition that invoked <EXTCONDLIST_MODE> when
* LIST_VAL_ID is done
*/
BEGIN(LIST_COND_MODE);
return TOK_OPENPAREN;
}
[^\n] {
DUMP_PREPROCESS;
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s' %d"), yytext, *yytext);
}
}
<ASSIGN_MODE>{ <ASSIGN_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
@ -547,7 +678,23 @@ LT_EQUAL <=
} }
} }
<MOUNT_MODE>{ <DBUS_MODE>{
send { RETURN_TOKEN(TOK_SEND); }
receive { RETURN_TOKEN(TOK_RECEIVE); }
bind { RETURN_TOKEN(TOK_BIND); }
read { RETURN_TOKEN(TOK_READ); }
write { RETURN_TOKEN(TOK_WRITE); }
{OPEN_PAREN} {
yy_push_state(LIST_VAL_MODE);
RETURN_TOKEN(TOK_OPENPAREN);
}
(r|w|rw|wr)/([[:space:],]) {
yylval.mode = strdup(yytext);
RETURN_TOKEN(TOK_MODE);
}
}
<MOUNT_MODE,DBUS_MODE>{
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
{ARROW} { {ARROW} {
@ -709,6 +856,10 @@ LT_EQUAL <=
PDEBUG("Entering mount\n"); PDEBUG("Entering mount\n");
yy_push_state(MOUNT_MODE); yy_push_state(MOUNT_MODE);
break; break;
case TOK_DBUS:
PDEBUG("Entering dbus\n");
yy_push_state(DBUS_MODE);
break;
default: /* nothing */ default: /* nothing */
break; break;
} }

View File

@ -38,6 +38,7 @@
#include "parser.h" #include "parser.h"
#include "parser_yacc.h" #include "parser_yacc.h"
#include "mount.h" #include "mount.h"
#include "dbus.h"
/* #define DEBUG */ /* #define DEBUG */
#ifdef DEBUG #ifdef DEBUG
@ -85,6 +86,14 @@ static struct keyword_table keyword_table[] = {
{"unmount", TOK_UMOUNT}, {"unmount", TOK_UMOUNT},
{"pivot_root", TOK_PIVOTROOT}, {"pivot_root", TOK_PIVOTROOT},
{"in", TOK_IN}, {"in", TOK_IN},
{"dbus", TOK_DBUS},
{"send", TOK_SEND},
{"receive", TOK_RECEIVE},
{"bind", TOK_BIND},
{"read", TOK_READ},
{"write", TOK_WRITE},
{"peer", TOK_PEER},
/* terminate */ /* terminate */
{NULL, 0} {NULL, 0}
}; };
@ -724,6 +733,81 @@ int parse_mode(const char *str_mode)
return mode; return mode;
} }
static int parse_dbus_sub_mode(const char *str_mode, int *result, int fail, const char *mode_desc __unused)
{
int mode = 0;
const char *p;
PDEBUG("Parsing DBus mode: %s\n", str_mode);
if (!str_mode)
return 0;
p = str_mode;
while (*p) {
char this = *p;
char lower;
reeval:
switch (this) {
case COD_READ_CHAR:
PDEBUG("Parsing DBus mode: found %s READ\n", mode_desc);
mode |= AA_DBUS_RECEIVE;
break;
case COD_WRITE_CHAR:
PDEBUG("Parsing DBus mode: found %s WRITE\n",
mode_desc);
mode |= AA_DBUS_SEND;
break;
/* error cases */
default:
lower = tolower(this);
switch (lower) {
case COD_READ_CHAR:
case COD_WRITE_CHAR:
PDEBUG("Parsing DBus mode: found invalid upper case char %c\n",
this);
warn_uppercase();
this = lower;
goto reeval;
break;
default:
if (fail)
yyerror(_("Internal: unexpected DBus mode character '%c' in input"),
this);
else
return 0;
break;
}
break;
}
p++;
}
PDEBUG("Parsed DBus mode: %s 0x%x\n", str_mode, mode);
*result = mode;
return 1;
}
int parse_dbus_mode(const char *str_mode, int *mode, int fail)
{
*mode = 0;
if (!parse_dbus_sub_mode(str_mode, mode, fail, ""))
return 0;
if (*mode & ~AA_VALID_DBUS_PERMS) {
if (fail)
yyerror(_("Internal error generated invalid DBus perm 0x%x\n"),
mode);
else
return 0;
}
return 1;
}
struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id) struct cod_entry *new_entry(char *namespace, char *id, int mode, char *link_id)
{ {
struct cod_entry *entry = NULL; struct cod_entry *entry = NULL;
@ -803,6 +887,16 @@ void free_mnt_entries(struct mnt_entry *list)
free(list); free(list);
} }
void free_dbus_entries(struct dbus_entry *list)
{
if (!list)
return;
if (list->next)
free_dbus_entries(list->next);
free_dbus_entry(list);
}
static void debug_base_perm_mask(int mask) static void debug_base_perm_mask(int mask)
{ {
if (HAS_MAY_READ(mask)) if (HAS_MAY_READ(mask))
@ -1148,6 +1242,15 @@ void free_cond_entry(struct cond_entry *ent)
} }
} }
void free_cond_list(struct cond_entry *ents)
{
struct cond_entry *entry, *tmp;
list_for_each_safe(ents, entry, tmp) {
free_cond_entry(entry);
}
}
void print_cond_entry(struct cond_entry *ent) void print_cond_entry(struct cond_entry *ent)
{ {
if (ent) { if (ent) {

View File

@ -30,6 +30,7 @@
#include "parser.h" #include "parser.h"
#include "mount.h" #include "mount.h"
#include "dbus.h"
#include "parser_yacc.h" #include "parser_yacc.h"
/* #define DEBUG */ /* #define DEBUG */
@ -818,6 +819,7 @@ void free_policy(struct codomain *cod)
free_hat_table(cod->hat_table); free_hat_table(cod->hat_table);
free_cod_entries(cod->entries); free_cod_entries(cod->entries);
free_mnt_entries(cod->mnt_ents); free_mnt_entries(cod->mnt_ents);
free_dbus_entries(cod->dbus_ents);
if (cod->dfarules) if (cod->dfarules)
aare_delete_ruleset(cod->dfarules); aare_delete_ruleset(cod->dfarules);
if (cod->dfa) if (cod->dfa)

View File

@ -29,6 +29,7 @@
#include "libapparmor_re/apparmor_re.h" #include "libapparmor_re/apparmor_re.h"
#include "libapparmor_re/aare_rules.h" #include "libapparmor_re/aare_rules.h"
#include "mount.h" #include "mount.h"
#include "dbus.h"
#include "policydb.h" #include "policydb.h"
enum error_type { enum error_type {
@ -1041,7 +1042,107 @@ fail:
} }
int post_process_policydb_ents(struct codomain *cod) static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry)
{
char busbuf[PATH_MAX + 3];
char namebuf[PATH_MAX + 3];
char peer_labelbuf[PATH_MAX + 3];
char pathbuf[PATH_MAX + 3];
char ifacebuf[PATH_MAX + 3];
char memberbuf[PATH_MAX + 3];
char *p, *vec[6];
pattern_t ptype;
int pos;
if (!entry) /* shouldn't happen */
return TRUE;
p = busbuf;
p += sprintf(p, "\\x%02x", AA_CLASS_DBUS);
if (entry->bus) {
ptype = convert_aaregex_to_pcre(entry->bus, 0, p,
PATH_MAX+3 - (p - busbuf), &pos);
if (ptype == ePatternInvalid)
goto fail;
} else {
/* match any char except \000 0 or more times */
strcpy(p, "[^\\000]*");
}
vec[0] = busbuf;
if (entry->name) {
ptype = convert_aaregex_to_pcre(entry->name, 0, namebuf,
PATH_MAX+3, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[1] = namebuf;
} else {
/* match any char except \000 0 or more times */
vec[1] = "[^\\000]*";
}
if (entry->peer_label) {
ptype = convert_aaregex_to_pcre(entry->peer_label, 0,
peer_labelbuf, PATH_MAX+3,
&pos);
if (ptype == ePatternInvalid)
goto fail;
vec[2] = peer_labelbuf;
} else {
/* match any char except \000 0 or more times */
vec[2] = "[^\\000]*";
}
if (entry->path) {
ptype = convert_aaregex_to_pcre(entry->path, 0, pathbuf,
PATH_MAX+3, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[3] = pathbuf;
} else {
/* match any char except \000 0 or more times */
vec[3] = "[^\\000]*";
}
if (entry->interface) {
ptype = convert_aaregex_to_pcre(entry->interface, 0, ifacebuf,
PATH_MAX+3, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[4] = ifacebuf;
} else {
/* match any char except \000 0 or more times */
vec[4] = "[^\\000]*";
}
if (entry->member) {
ptype = convert_aaregex_to_pcre(entry->member, 0, memberbuf,
PATH_MAX+3, &pos);
if (ptype == ePatternInvalid)
goto fail;
vec[5] = memberbuf;
} else {
/* match any char except \000 0 or more times */
vec[5] = "[^\\000]*";
}
if (entry->mode & AA_DBUS_BIND) {
if (!aare_add_rule_vec(dfarules, entry->deny, entry->mode & AA_DBUS_BIND, entry->audit & AA_DBUS_BIND, 2, vec, dfaflags))
goto fail;
}
if (entry->mode & ~AA_DBUS_BIND) {
if (!aare_add_rule_vec(dfarules, entry->deny, entry->mode, entry->audit, 6, vec, dfaflags))
goto fail;
}
return TRUE;
fail:
return FALSE;
}
static int post_process_mnt_ents(struct codomain *cod)
{ {
int ret = TRUE; int ret = TRUE;
int count = 0; int count = 0;
@ -1058,10 +1159,37 @@ int post_process_policydb_ents(struct codomain *cod)
} else if (cod->mnt_ents && !kernel_supports_mount) } else if (cod->mnt_ents && !kernel_supports_mount)
pwarn("profile %s mount rules not enforced\n", cod->name); pwarn("profile %s mount rules not enforced\n", cod->name);
cod->policy_rule_count = count; cod->policy_rule_count += count;
return ret; return ret;
} }
static int post_process_dbus_ents(struct codomain *cod)
{
int ret = TRUE;
struct dbus_entry *entry;
int count = 0;
list_for_each(cod->dbus_ents, entry) {
if (regex_type == AARE_DFA &&
!process_dbus_entry(cod->policy_rules, entry))
ret = FALSE;
count++;
}
cod->policy_rule_count += count;
return ret;
}
int post_process_policydb_ents(struct codomain *cod)
{
if (!post_process_mnt_ents(cod))
return FALSE;
if (!post_process_dbus_ents(cod))
return FALSE;
return TRUE;
}
int process_policydb(struct codomain *cod) int process_policydb(struct codomain *cod)
{ {
int error = -1; int error = -1;

View File

@ -33,6 +33,7 @@
#include "parser.h" #include "parser.h"
#include "mount.h" #include "mount.h"
#include "dbus.h"
#include "parser_include.h" #include "parser_include.h"
#include <unistd.h> #include <unistd.h>
#include <netinet/in.h> #include <netinet/in.h>
@ -81,6 +82,7 @@ void add_local_entry(struct codomain *cod);
%token TOK_ID %token TOK_ID
%token TOK_CONDID %token TOK_CONDID
%token TOK_CONDLISTID
%token TOK_CARET %token TOK_CARET
%token TOK_OPEN %token TOK_OPEN
%token TOK_CLOSE %token TOK_CLOSE
@ -122,6 +124,13 @@ void add_local_entry(struct codomain *cod);
%token TOK_UMOUNT %token TOK_UMOUNT
%token TOK_PIVOTROOT %token TOK_PIVOTROOT
%token TOK_IN %token TOK_IN
%token TOK_DBUS
%token TOK_SEND
%token TOK_RECEIVE
%token TOK_BIND
%token TOK_READ
%token TOK_WRITE
%token TOK_PEER
/* rlimits */ /* rlimits */
%token TOK_RLIMIT %token TOK_RLIMIT
@ -158,6 +167,7 @@ void add_local_entry(struct codomain *cod);
struct cod_net_entry *net_entry; struct cod_net_entry *net_entry;
struct cod_entry *user_entry; struct cod_entry *user_entry;
struct mnt_entry *mnt_entry; struct mnt_entry *mnt_entry;
struct dbus_entry *dbus_entry;
struct flagval flags; struct flagval flags;
int fmode; int fmode;
@ -174,6 +184,7 @@ void add_local_entry(struct codomain *cod);
%type <id> TOK_ID %type <id> TOK_ID
%type <id> TOK_CONDID %type <id> TOK_CONDID
%type <id> TOK_CONDLISTID
%type <mode> TOK_MODE %type <mode> TOK_MODE
%type <fmode> file_mode %type <fmode> file_mode
%type <cod> profile_base %type <cod> profile_base
@ -192,6 +203,8 @@ void add_local_entry(struct codomain *cod);
%type <mnt_entry> mnt_rule %type <mnt_entry> mnt_rule
%type <cond_entry> opt_conds %type <cond_entry> opt_conds
%type <cond_entry> cond %type <cond_entry> cond
%type <cond_entry> cond_list
%type <cond_entry> opt_cond_list
%type <flags> flags %type <flags> flags
%type <flags> flagvals %type <flags> flagvals
%type <flags> flagval %type <flags> flagval
@ -211,6 +224,10 @@ void add_local_entry(struct codomain *cod);
%type <boolean> opt_flags %type <boolean> opt_flags
%type <id> opt_namespace %type <id> opt_namespace
%type <id> opt_id %type <id> opt_id
%type <fmode> dbus_perm
%type <fmode> dbus_perms
%type <fmode> opt_dbus_perm
%type <dbus_entry> dbus_rule
%type <transition> opt_named_transition %type <transition> opt_named_transition
%type <boolean> opt_unsafe %type <boolean> opt_unsafe
%type <boolean> opt_file %type <boolean> opt_file
@ -680,6 +697,25 @@ rules: rules opt_audit_flag mnt_rule
$$ = $1; $$ = $1;
} }
rules: rules opt_audit_flag TOK_DENY dbus_rule
{
$4->deny = $4->mode;
if ($2)
$4->audit = $4->mode;
$4->next = $1->dbus_ents;
$1->dbus_ents = $4;
$$ = $1;
}
rules: rules opt_audit_flag dbus_rule
{
if ($2)
$3->audit = $3->mode;
$3->next = $1->dbus_ents;
$1->dbus_ents = $3;
$$ = $1;
}
rules: rules change_profile rules: rules change_profile
{ {
PDEBUG("matched: rules change_profile\n"); PDEBUG("matched: rules change_profile\n");
@ -1103,6 +1139,14 @@ opt_conds: { /* nothing */ $$ = NULL; }
$$ = $2; $$ = $2;
} }
cond_list: TOK_CONDLISTID TOK_EQUALS TOK_OPENPAREN opt_conds TOK_CLOSEPAREN
{
$$ = $4;
}
opt_cond_list: { /* nothing */ $$ = NULL; }
| cond_list { $$ = $1; }
mnt_rule: TOK_MOUNT opt_conds opt_id TOK_END_OF_RULE mnt_rule: TOK_MOUNT opt_conds opt_id TOK_END_OF_RULE
{ {
$$ = do_mnt_rule($2, $3, NULL, NULL, AA_MAY_MOUNT); $$ = do_mnt_rule($2, $3, NULL, NULL, AA_MAY_MOUNT);
@ -1142,6 +1186,51 @@ mnt_rule: TOK_PIVOTROOT opt_conds opt_id opt_named_transition TOK_END_OF_RULE
$$ = do_pivot_rule($2, $3, name); $$ = do_pivot_rule($2, $3, name);
} }
dbus_perm: TOK_VALUE
{
if (strcmp($1, "bind") == 0)
$$ = AA_DBUS_BIND;
else if (strcmp($1, "send") == 0 || strcmp($1, "write") == 0)
$$ = AA_DBUS_SEND;
else if (strcmp($1, "receive") == 0 || strcmp($1, "read") == 0)
$$ = AA_DBUS_RECEIVE;
else if ($1) {
parse_dbus_mode($1, &$$, 1);
} else
$$ = 0;
if ($1)
free($1);
}
| TOK_BIND { $$ = AA_DBUS_BIND; }
| TOK_SEND { $$ = AA_DBUS_SEND; }
| TOK_RECEIVE { $$ = AA_DBUS_RECEIVE; }
| TOK_READ { $$ = AA_DBUS_RECEIVE; }
| TOK_WRITE { $$ = AA_DBUS_SEND; }
| TOK_MODE
{
parse_dbus_mode($1, &$$, 1);
}
dbus_perms: { /* nothing */ $$ = 0; }
| dbus_perms dbus_perm { $$ = $1 | $2; }
| dbus_perms TOK_COMMA dbus_perm { $$ = $1 | $3; }
opt_dbus_perm: { /* nothing */ $$ = 0; }
| dbus_perm { $$ = $1; }
| TOK_OPENPAREN dbus_perms TOK_CLOSEPAREN { $$ = $2; }
dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE
{
struct dbus_entry *ent;
ent = new_dbus_entry($2, $3, $4);
if (!ent) {
yyerror(_("Memory allocation error."));
}
$$ = ent;
}
hat_start: TOK_CARET {} hat_start: TOK_CARET {}
| TOK_HAT {} | TOK_HAT {}