mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 01:57:43 +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:
parent
1aba3394a3
commit
ab84444d3a
@ -76,8 +76,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
|
||||
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_yacc.c parser_regex.c parser_variable.c parser_policy.c \
|
||||
parser_alias.c mount.c lib.c
|
||||
HDRS = parser.h parser_include.h immunix.h mount.h lib.h
|
||||
parser_alias.c mount.c dbus.c lib.c
|
||||
HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h
|
||||
TOOLS = apparmor_parser
|
||||
|
||||
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
|
||||
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
dbus.o: dbus.c dbus.h parser.h immunix.h
|
||||
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||
|
||||
parser_version.h: Makefile
|
||||
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
|
||||
@mv -f .ver $@
|
||||
|
183
parser/dbus.c
Normal file
183
parser/dbus.c
Normal 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
48
parser/dbus.h
Normal 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 */
|
@ -40,6 +40,13 @@
|
||||
#define AA_EXEC_MOD_2 (1 << 12)
|
||||
#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 | \
|
||||
AA_MAY_READ | AA_MAY_APPEND | \
|
||||
AA_MAY_LINK | AA_MAY_LOCK | \
|
||||
|
@ -142,6 +142,7 @@ struct codomain {
|
||||
|
||||
char *exec_table[AA_EXEC_COUNT];
|
||||
struct cod_entry *entries;
|
||||
struct dbus_entry *dbus_ents;
|
||||
struct mnt_entry *mnt_ents;
|
||||
|
||||
void *hat_table;
|
||||
@ -301,6 +302,8 @@ extern char *basedir;
|
||||
/* parser_regex.c */
|
||||
extern int process_regex(struct codomain *cod);
|
||||
extern int post_process_entry(struct cod_entry *entry);
|
||||
extern int process_dbus(struct codomain *cod);
|
||||
|
||||
extern void reset_regex(void);
|
||||
|
||||
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 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_list(struct cond_entry *ents);
|
||||
extern void print_cond_entry(struct cond_entry *ent);
|
||||
extern char *processid(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 char *process_var(const char *var);
|
||||
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,
|
||||
char *link_id);
|
||||
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 void free_cod_entries(struct cod_entry *list);
|
||||
extern void free_mnt_entries(struct mnt_entry *list);
|
||||
extern void free_dbus_entries(struct dbus_entry *list);
|
||||
|
||||
/* parser_symtab.c */
|
||||
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 int post_process_policy(int debug_only);
|
||||
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_policydb(struct codomain *cod);
|
||||
extern int post_merge_rules(void);
|
||||
|
@ -53,6 +53,12 @@
|
||||
#define NPDEBUG(fmt, args...) /* Do nothing */
|
||||
|
||||
#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
|
||||
|
||||
@ -198,9 +204,11 @@ POST_VAR_ID_CHARS [^ \t\n"!,]{-}[=\+]
|
||||
POST_VAR_ID {POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS})
|
||||
LIST_VALUE_ID_CHARS [^ \t\n"!,]{-}[()]
|
||||
LIST_VALUE_ID {LIST_VALUE_ID_CHARS}+
|
||||
QUOTED_LIST_VALUE_ID {LIST_VALUE_ID}|\"{LIST_VALUE_ID}\"
|
||||
ID_CHARS_NOEQ [^ \t\n"!,]{-}[=]
|
||||
LEADING_ID_CHARS_NOEQ [^ \t\n"!,]{-}[=()]
|
||||
ID_NOEQ {ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ})
|
||||
IDS_NOEQ {ID_NOEQ}+
|
||||
IDS_NOEQ {LEADING_ID_CHARS_NOEQ}{ID_NOEQ}*
|
||||
ALLOWED_QUOTED_ID [^\0"]|\\\"
|
||||
QUOTED_ID \"{ALLOWED_QUOTED_ID}*\"
|
||||
|
||||
@ -228,11 +236,16 @@ LT_EQUAL <=
|
||||
%x SUB_ID
|
||||
%x SUB_VALUE
|
||||
%x EXTCOND_MODE
|
||||
%x EXTCONDLIST_MODE
|
||||
%x NETWORK_MODE
|
||||
%x LIST_VAL_MODE
|
||||
%x LIST_COND_MODE
|
||||
%x LIST_COND_VAL
|
||||
%x LIST_COND_PAREN_VAL
|
||||
%x ASSIGN_MODE
|
||||
%x RLIMIT_MODE
|
||||
%x MOUNT_MODE
|
||||
%x DBUS_MODE
|
||||
%x CHANGE_PROFILE_MODE
|
||||
%x INCLUDE
|
||||
|
||||
@ -278,7 +291,7 @@ LT_EQUAL <=
|
||||
if ( !YY_CURRENT_BUFFER ) yyterminate();
|
||||
}
|
||||
|
||||
<INITIAL,MOUNT_MODE>{
|
||||
<INITIAL,MOUNT_MODE,DBUS_MODE>{
|
||||
{VARIABLE_NAME}/{WS}*= {
|
||||
/* we match to the = in the lexer so that
|
||||
* can switch scanner state. By the time
|
||||
@ -286,12 +299,20 @@ LT_EQUAL <=
|
||||
* as bison may have requested the next
|
||||
* token from the scanner
|
||||
*/
|
||||
int token = get_keyword_token(yytext);
|
||||
|
||||
DUMP_PREPROCESS;
|
||||
if (token == TOK_PEER) {
|
||||
PDEBUG("conditional list %s=\n", yytext);
|
||||
yy_push_state(EXTCONDLIST_MODE);
|
||||
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}*\( {
|
||||
/* we match to 'in' in the lexer so that
|
||||
* we can switch scanner state. By the time
|
||||
@ -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>{
|
||||
{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 */ }
|
||||
|
||||
{ARROW} {
|
||||
@ -709,6 +856,10 @@ LT_EQUAL <=
|
||||
PDEBUG("Entering mount\n");
|
||||
yy_push_state(MOUNT_MODE);
|
||||
break;
|
||||
case TOK_DBUS:
|
||||
PDEBUG("Entering dbus\n");
|
||||
yy_push_state(DBUS_MODE);
|
||||
break;
|
||||
default: /* nothing */
|
||||
break;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "parser.h"
|
||||
#include "parser_yacc.h"
|
||||
#include "mount.h"
|
||||
#include "dbus.h"
|
||||
|
||||
/* #define DEBUG */
|
||||
#ifdef DEBUG
|
||||
@ -85,6 +86,14 @@ static struct keyword_table keyword_table[] = {
|
||||
{"unmount", TOK_UMOUNT},
|
||||
{"pivot_root", TOK_PIVOTROOT},
|
||||
{"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 */
|
||||
{NULL, 0}
|
||||
};
|
||||
@ -724,6 +733,81 @@ int parse_mode(const char *str_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 *entry = NULL;
|
||||
@ -803,6 +887,16 @@ void free_mnt_entries(struct mnt_entry *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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (ent) {
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "parser.h"
|
||||
#include "mount.h"
|
||||
#include "dbus.h"
|
||||
#include "parser_yacc.h"
|
||||
|
||||
/* #define DEBUG */
|
||||
@ -818,6 +819,7 @@ void free_policy(struct codomain *cod)
|
||||
free_hat_table(cod->hat_table);
|
||||
free_cod_entries(cod->entries);
|
||||
free_mnt_entries(cod->mnt_ents);
|
||||
free_dbus_entries(cod->dbus_ents);
|
||||
if (cod->dfarules)
|
||||
aare_delete_ruleset(cod->dfarules);
|
||||
if (cod->dfa)
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
#include "mount.h"
|
||||
#include "dbus.h"
|
||||
#include "policydb.h"
|
||||
|
||||
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 count = 0;
|
||||
@ -1058,10 +1159,37 @@ int post_process_policydb_ents(struct codomain *cod)
|
||||
} else if (cod->mnt_ents && !kernel_supports_mount)
|
||||
pwarn("profile %s mount rules not enforced\n", cod->name);
|
||||
|
||||
cod->policy_rule_count = count;
|
||||
cod->policy_rule_count += count;
|
||||
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 error = -1;
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include "parser.h"
|
||||
#include "mount.h"
|
||||
#include "dbus.h"
|
||||
#include "parser_include.h"
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
@ -81,6 +82,7 @@ void add_local_entry(struct codomain *cod);
|
||||
|
||||
%token TOK_ID
|
||||
%token TOK_CONDID
|
||||
%token TOK_CONDLISTID
|
||||
%token TOK_CARET
|
||||
%token TOK_OPEN
|
||||
%token TOK_CLOSE
|
||||
@ -122,6 +124,13 @@ void add_local_entry(struct codomain *cod);
|
||||
%token TOK_UMOUNT
|
||||
%token TOK_PIVOTROOT
|
||||
%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 */
|
||||
%token TOK_RLIMIT
|
||||
@ -158,6 +167,7 @@ void add_local_entry(struct codomain *cod);
|
||||
struct cod_net_entry *net_entry;
|
||||
struct cod_entry *user_entry;
|
||||
struct mnt_entry *mnt_entry;
|
||||
struct dbus_entry *dbus_entry;
|
||||
|
||||
struct flagval flags;
|
||||
int fmode;
|
||||
@ -174,6 +184,7 @@ void add_local_entry(struct codomain *cod);
|
||||
|
||||
%type <id> TOK_ID
|
||||
%type <id> TOK_CONDID
|
||||
%type <id> TOK_CONDLISTID
|
||||
%type <mode> TOK_MODE
|
||||
%type <fmode> file_mode
|
||||
%type <cod> profile_base
|
||||
@ -192,6 +203,8 @@ void add_local_entry(struct codomain *cod);
|
||||
%type <mnt_entry> mnt_rule
|
||||
%type <cond_entry> opt_conds
|
||||
%type <cond_entry> cond
|
||||
%type <cond_entry> cond_list
|
||||
%type <cond_entry> opt_cond_list
|
||||
%type <flags> flags
|
||||
%type <flags> flagvals
|
||||
%type <flags> flagval
|
||||
@ -211,6 +224,10 @@ void add_local_entry(struct codomain *cod);
|
||||
%type <boolean> opt_flags
|
||||
%type <id> opt_namespace
|
||||
%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 <boolean> opt_unsafe
|
||||
%type <boolean> opt_file
|
||||
@ -680,6 +697,25 @@ rules: rules opt_audit_flag mnt_rule
|
||||
$$ = $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
|
||||
{
|
||||
PDEBUG("matched: rules change_profile\n");
|
||||
@ -1103,6 +1139,14 @@ opt_conds: { /* nothing */ $$ = NULL; }
|
||||
$$ = $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
|
||||
{
|
||||
$$ = 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);
|
||||
}
|
||||
|
||||
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 {}
|
||||
| TOK_HAT {}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user