apparmor/parser/dbus.c

208 lines
5.7 KiB
C
Raw Normal View History

/*
* 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;
}
#define DUP_STRING(orig, new, field) \
(new)->field = (orig)->field ? strdup((orig)->field) : NULL
struct dbus_entry *dup_dbus_entry(struct dbus_entry *orig)
{
struct dbus_entry *ent = NULL;
ent = (struct dbus_entry *) calloc(1, sizeof(struct dbus_entry));
if (!ent)
return NULL;
DUP_STRING(orig, ent, bus);
DUP_STRING(orig, ent, name);
DUP_STRING(orig, ent, peer_label);
DUP_STRING(orig, ent, path);
DUP_STRING(orig, ent, interface);
DUP_STRING(orig, ent, member);
ent->mode = orig->mode;
ent->audit = orig->audit;
ent->deny = orig->deny;
ent->next = orig->next;
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");
}