apparmor/parser/parser_misc.c
Steve Beattie 6d3e74907d Import the rest of the core functionality of the internal apparmor
development tree (trunk branch). From svn repo version 6381.
2006-04-11 21:52:54 +00:00

710 lines
16 KiB
C

/* $Id: parser_misc.c 6263 2006-02-11 08:26:48Z steve $ */
/*
* Copyright (c) 1999, 2000, 2002, 2003, 2004, 2005 NOVELL (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.
*/
/* assistance routines */
#define _GNU_SOURCE /* for strndup */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <libintl.h>
#define _(s) gettext(s)
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "parser.h"
#include "parser_yacc.h"
/* #define DEBUG */
#ifdef DEBUG
#define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args)
#else
#define PDEBUG(fmt, args...) /* Do nothing */
#endif
#define NPDEBUG(fmt, args...) /* Do nothing */
struct keyword_table {
char *keyword;
int token;
};
static struct keyword_table keyword_table[] = {
/* capabilities */
{"capability", TOK_CAPABILITY},
{"chown", TOK_CAP_CHOWN},
{"dac_override", TOK_CAP_DAC_OVERRIDE},
{"dac_read_search", TOK_CAP_DAC_READ_SEARCH},
{"fowner", TOK_CAP_FOWNER},
{"fsetid", TOK_CAP_FSETID},
{"kill", TOK_CAP_KILL},
{"setgid", TOK_CAP_SETGID},
{"setuid", TOK_CAP_SETUID},
{"setpcap", TOK_CAP_SETPCAP},
{"linux_immutable", TOK_CAP_LINUX_IMMUTABLE},
{"net_bind_service", TOK_CAP_NET_BIND_SERVICE},
{"net_broadcast", TOK_CAP_NET_BROADCAST},
{"net_admin", TOK_CAP_NET_ADMIN},
{"net_raw", TOK_CAP_NET_RAW},
{"ipc_lock", TOK_CAP_IPC_LOCK},
{"ipc_owner", TOK_CAP_IPC_OWNER},
{"sys_module", TOK_CAP_SYS_MODULE},
{"sys_rawio", TOK_CAP_SYS_RAWIO},
{"sys_chroot", TOK_CAP_SYS_CHROOT},
{"sys_ptrace", TOK_CAP_SYS_PTRACE},
{"sys_pacct", TOK_CAP_SYS_PACCT},
{"sys_admin", TOK_CAP_SYS_ADMIN},
{"sys_boot", TOK_CAP_SYS_BOOT},
{"sys_nice", TOK_CAP_SYS_NICE},
{"sys_resource", TOK_CAP_SYS_RESOURCE},
{"sys_time", TOK_CAP_SYS_TIME},
{"sys_tty_config", TOK_CAP_SYS_TTY_CONFIG},
{"mknod", TOK_CAP_MKNOD},
{"lease", TOK_CAP_LEASE},
/* flags */
{"flags", TOK_FLAGS},
{"debug", TOK_FLAG_DEBUG},
{"complain", TOK_FLAG_COMPLAIN},
{"audit", TOK_FLAG_AUDIT},
/* network */
{"via", TOK_VIA},
{"tcp_connect", TOK_TCP_CONN},
{"tcp_accept", TOK_TCP_ACPT},
{"tcp_connected", TOK_TCP_CONN_ESTB},
{"tcp_accepted", TOK_TCP_ACPT_ESTB},
{"udp_send", TOK_UDP_SEND},
{"udp_receive", TOK_UDP_RECV},
{"to", TOK_TO},
{"from", TOK_FROM},
/* misc keywords */
{"if", TOK_IF},
{"else", TOK_ELSE},
{"not", TOK_NOT},
{"defined", TOK_DEFINED},
/* terminate */
{NULL, 0}
};
/* for alpha matches, check for keywords */
int get_keyword_token(const char *keyword)
{
int i;
for (i = 0; keyword_table[i].keyword; i++) {
PDEBUG("Checking keyword %s\n", keyword_table[i].keyword);
if (strcmp(keyword, keyword_table[i].keyword) == 0) {
PDEBUG("Found keyword %s\n", keyword_table[i].keyword);
return keyword_table[i].token;
}
}
PDEBUG("Unable to find keyword %s\n", keyword);
return -1;
}
char *processunquoted(char *string, int len)
{
char *tmp, *s;
int l;
tmp = (char *)malloc(len + 1);
if (!tmp)
return NULL;
s = tmp;
for (l = 0; l < len; l++) {
if (string[l] == '\\' && l < len - 3) {
if (strchr("0123", string[l + 1]) &&
strchr("0123456789", string[l + 2]) &&
strchr("0123456789", string[l + 3])) {
/* three digit octal */
int res = (string[l + 1] - '0') * 64 +
(string[l + 2] - '0') * 8 +
(string[l + 3] - '0');
*s = res;
l += 3;
} else {
*s = string[l];
}
s++;
} else {
*s = string[l];
s++;
}
}
*s = 0;
return tmp;
}
/* rewrite a quoted string substituting escaped characters for the
* real thing. Strip the quotes around the string */
char *processquoted(char *string, int len)
{
char *tmp, *s;
int l;
/* the result string will be shorter or equal in length */
tmp = (char *)malloc(len + 1);
if (!tmp)
return NULL;
s = tmp;
for (l = 1; l < len - 1; l++) {
if (string[l] == '\\' && l < len - 2) {
switch (string[l + 1]) {
case 't':
*s = '\t';
l++;
break;
case 'n':
*s = '\n';
l++;
break;
case 'r':
*s = '\r';
l++;
break;
case '"':
*s = '"';
l++;
break;
case '\\':
*s = '\\';
l++;
break;
case '0' - '3':
if ((l < len - 4) &&
strchr("0123456789", string[l + 2]) &&
strchr("0123456789", string[l + 3])) {
/* three digit octal */
int res = (string[l + 1] - '0') * 64 +
(string[l + 2] - '0') * 8 +
(string[l + 3] - '0');
*s = res;
l += 3;
break;
}
/* fall through */
default:
/* any unsupported escape sequence results in all
chars being copied. */
*s = string[l];
}
s++;
} else {
*s = string[l];
s++;
}
}
*s = 0;
return tmp;
}
/* strip off surrounding delimiters around variables */
char *process_var(const char *var)
{
const char *orig = var;
int len = strlen(var);
if (*orig == '@' || *orig == '$') {
orig++;
len--;
} else {
PERROR("ASSERT: Found var '%s' without variable prefix\n",
var);
return NULL;
}
if (*orig == '{') {
orig++;
len--;
if (orig[len - 1] != '}') {
PERROR("ASSERT: No matching '}' in variable '%s'\n",
var);
return NULL;
} else
len--;
}
return strndup(orig, len);
}
/* returns -1 if value != true or false, otherwise 0 == false, 1 == true */
int str_to_boolean(const char *value)
{
int retval = -1;
if (strcasecmp("TRUE", value) == 0)
retval = 1;
if (strcasecmp("FALSE", value) == 0)
retval = 0;
return retval;
}
int parse_mode(const char *str_mode)
{
/* The 'check' int is a bit of a kludge, but we need some context
when we're doing permission checking */
#define IS_DIFF_QUAL(q) (qual && qual != (q) ? TRUE : (qual = (q), FALSE))
int mode = 0;
const char *p;
char qual = 0;
PDEBUG("Parsing mode: %s\n", str_mode);
p = str_mode;
while (*p) {
char this = tolower(*p);
char next = tolower(*(p + 1));
switch (this) {
case COD_READ_CHAR:
PDEBUG("Parsing mode: found READ\n");
mode |= KERN_COD_MAY_READ;
break;
case COD_WRITE_CHAR:
PDEBUG("Parsing mode: found WRITE\n");
mode |= KERN_COD_MAY_WRITE;
break;
case COD_LINK_CHAR:
PDEBUG("Parsing mode: found LINK\n");
mode |= KERN_COD_MAY_LINK;
break;
case COD_INHERIT_CHAR:
PDEBUG("Parsing mode: found INHERIT\n");
if (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 {
mode |=
(KERN_COD_EXEC_INHERIT | KERN_COD_MAY_EXEC);
p++; /* skip 'x' */
}
break;
case COD_UNCONSTRAINED_CHAR:
PDEBUG("Parsing mode: found UNCONSTRAINED\n");
if (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 {
mode |=
(KERN_COD_EXEC_UNCONSTRAINED |
KERN_COD_MAY_EXEC);
p++; /* skip 'x' */
}
break;
case COD_PROFILE_CHAR:
PDEBUG("Parsing mode: found PROFILE\n");
if (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 {
mode |=
(KERN_COD_EXEC_PROFILE | KERN_COD_MAY_EXEC);
p++; /* skip 'x' */
}
break;
case COD_EXEC_CHAR:
PDEBUG("Parsing mode: found EXEC\n");
yyerror(_("Invalid mode, 'x' must be preceded by exec qualifier 'i', 'u' or 'p'"));
break;
default:
yyerror(_("Internal: unexpected mode character in input"));
}
p++;
}
PDEBUG("Parsed mode: %s 0x%x\n", str_mode, mode);
return mode;
}
struct cod_net_entry *new_network_entry(int action,
struct ipv4_endpoints *addrs,
char *interface)
{
struct cod_net_entry *entry = NULL;
entry = (struct cod_net_entry *)
malloc(sizeof(struct cod_net_entry));
entry->saddr = (struct in_addr *)malloc(sizeof(struct in_addr));
entry->smask = (struct in_addr *)malloc(sizeof(struct in_addr));
entry->daddr = (struct in_addr *)malloc(sizeof(struct in_addr));
entry->dmask = (struct in_addr *)malloc(sizeof(struct in_addr));
if (!addrs || !entry || !entry->saddr || !entry->smask ||
!entry->daddr || !entry->dmask) {
yyerror(_("Memory allocation error."));
return NULL;
}
entry->next = NULL;
entry->mode = action;
entry->iface = interface ? interface : NULL;
if (addrs->src) {
PDEBUG("Assigning source\n");
entry->saddr->s_addr = addrs->src->addr.s_addr & addrs->src->mask;
entry->smask->s_addr = addrs->src->mask;
entry->src_port[0] = addrs->src->port[0];
entry->src_port[1] = addrs->src->port[1];
} else {
entry->saddr->s_addr = 0;
entry->smask->s_addr = 0;
entry->src_port[0] = MIN_PORT;
entry->src_port[1] = MAX_PORT;
}
if (addrs->dest) {
PDEBUG("Assigning source\n");
entry->daddr->s_addr = addrs->dest->addr.s_addr & addrs->dest->mask;
entry->dmask->s_addr = addrs->dest->mask;
entry->dst_port[0] = addrs->dest->port[0];
entry->dst_port[1] = addrs->dest->port[1];
} else {
entry->daddr->s_addr = 0;
entry->dmask->s_addr = 0;
entry->dst_port[0] = MIN_PORT;
entry->dst_port[1] = MAX_PORT;
}
return entry;
}
struct cod_entry *new_entry(char *id, char *mode)
{
struct cod_entry *entry = NULL;
entry = (struct cod_entry *)malloc(sizeof(struct cod_entry));
if (!entry)
return NULL;
entry->name = id ? id : NULL;
entry->mode = mode ? parse_mode(mode) : 0;
entry->deny = FALSE;
entry->pattern_type = ePatternInvalid;
entry->pat.regex = NULL;
entry->pat.compiled = NULL;
entry->next = NULL;
PDEBUG(" Insertion of: (%s)\n", entry->name);
return entry;
}
struct cod_entry *copy_cod_entry(struct cod_entry *orig)
{
struct cod_entry *entry = NULL;
entry = (struct cod_entry *)malloc(sizeof(struct cod_entry));
if (!entry)
return NULL;
entry->name = strdup(orig->name);
entry->mode = orig->mode;
entry->deny = orig->deny;
/* XXX - need to create copies of the patterns, too */
entry->pattern_type = orig->pattern_type;
entry->pat.regex = NULL;
entry->pat.compiled = NULL;
entry->next = orig->next;
return entry;
}
void free_ipv4_endpoints(struct ipv4_endpoints *addrs)
{
if (!addrs)
return;
if (addrs->src)
free(addrs->src);
if (addrs->dest)
free(addrs->dest);
free(addrs);
}
void free_cod_entries(struct cod_entry *list)
{
if (!list)
return;
if (list->next)
free_cod_entries(list->next);
if (list->name)
free(list->name);
if (list->pat.regex)
free(list->pat.regex);
if (list->pat.compiled)
free(list->pat.compiled);
free(list);
}
void free_net_entries(struct cod_net_entry *list)
{
if (!list)
return;
if (list->next)
free_net_entries(list->next);
if (list->saddr)
free(list->saddr);
if (list->smask)
free(list->smask);
if (list->daddr)
free(list->daddr);
if (list->dmask)
free(list->dmask);
if (list->iface)
free(list->iface);
free(list);
}
void debug_cod_entries(struct cod_entry *list)
{
struct cod_entry *item = NULL;
printf("--- Entries ---\n");
for (item = list; item; item = item->next) {
if (!item)
printf("Item is NULL!\n");
printf("Mode:\t");
if (item->mode & KERN_COD_MAY_READ)
printf("r");
if (item->mode & KERN_COD_MAY_WRITE)
printf("w");
if (item->mode & KERN_COD_MAY_EXEC)
printf("x");
if (item->mode & KERN_COD_MAY_LINK)
printf("l");
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");
if (item->name)
printf("\tName:\t(%s)\n", item->name);
else
printf("\tName:\tNULL\n");
}
}
void debug_cod_net_entries(struct cod_net_entry *list)
{
struct cod_net_entry *item = NULL;
struct in_addr src_addr, dst_addr;
unsigned long smask;
unsigned long dmask;
printf("--- NetwerkEntries --- \n");
for (item = list; item; item = item->next) {
if (!item)
printf("Item is NULL");
src_addr.s_addr = item->saddr->s_addr;
dst_addr.s_addr = item->daddr->s_addr;
smask = ntohl(item->smask->s_addr);
dmask = ntohl(item->dmask->s_addr);
printf("Source IP: %s\n", inet_ntoa(src_addr));
printf("Source Port: (%hu) - (%hu)\n", item->src_port[0],
item->src_port[1]);
printf("Source netmask: %lx\n", smask);
fflush(stdout);
printf("Destination IP: %s\n", inet_ntoa(dst_addr));
printf("Destination Port: %hu - %hu\n", item->dst_port[0],
item->dst_port[1]);
printf("Destination netmask: %lx\n", dmask);
fflush(stdout);
printf("Mode:\t");
if (item->mode & KERN_COD_TCP_ACCEPT)
printf("TA");
if (item->mode & KERN_COD_TCP_CONNECT)
printf("TC");
if (item->mode & KERN_COD_TCP_ACCEPTED)
printf("Ta");
if (item->mode & KERN_COD_TCP_CONNECTED)
printf("Tc");
if (item->mode & KERN_COD_UDP_SEND)
printf("US");
if (item->mode & KERN_COD_UDP_RECEIVE)
printf("UR");
if (item->iface != NULL)
printf("\nInterface: %s\n", item->iface);
printf("\n");
}
}
static const char *capnames[] = {
"chown",
"dac_override",
"dac_read_search",
"fowner",
"fsetid",
"kill",
"setgid",
"setuid",
"setpcap",
"linux_immutable",
"net_bind_service",
"net_broadcast",
"net_admin",
"net_raw",
"ipc_lock",
"ipc_owner",
"sys_module",
"sys_rawio",
"sys_chroot",
"sys_ptrace",
"sys_pacct",
"sys_admin",
"sys_boot",
"sys_nice",
"sys_resource",
"sys_time",
"sys_tty_config",
"mknod",
"lease"
};
const char *capability_to_name(unsigned int cap)
{
const char *capname;
capname = (cap < (sizeof(capnames) / sizeof(char *))
? capnames[cap] : "invalid-capability");
return capname;
}
void debug_cod_list(struct codomain *cod)
{
unsigned int i;
if (cod->name)
printf("Name:\t\t%s\n", cod->name);
else
printf("Name:\t\tNULL\n");
if (cod->sub_name)
printf("Subname:\t%s\n", cod->sub_name);
else
printf("Subname:\tNULL\n");
if (cod->default_deny)
printf("Type:\t\tDefault Deny\t\n");
else
printf("Type:\t\tDefault Allow\t\n");
printf("Capabilities:\t");
for (i = 0; i < (sizeof(capnames)/sizeof(char *)); i++) {
if (((1 << i) & cod->capabilities) != 0) {
printf ("%s ", capability_to_name(i));
}
}
printf("\n");
if (cod->entries)
debug_cod_entries(cod->entries);
if (cod->net_entries)
debug_cod_net_entries(cod->net_entries);
printf("\n");
dump_policy_hats(cod);
}
#ifdef UNIT_TEST
#define MY_TEST(statement, error) \
if (!(statement)) { \
PERROR("FAIL: %s\n", error); \
rc = 1; \
}
/* Guh, fake routine */
void yyerror(char *msg, ...)
{
va_list arg;
char buf[PATH_MAX];
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
PERROR(_("AppArmor parser error: %s\n"), buf);
exit(1);
}
int test_str_to_boolean(void)
{
int rc = 0;
int retval;
retval = str_to_boolean("TRUE");
MY_TEST(retval == 1, "str2bool for TRUE");
retval = str_to_boolean("TrUe");
MY_TEST(retval == 1, "str2bool for TrUe");
retval = str_to_boolean("false");
MY_TEST(retval == 0, "str2bool for false");
retval = str_to_boolean("flase");
MY_TEST(retval == -1, "str2bool for flase");
return rc;
}
int main(void)
{
int rc = 0;
int retval;
retval = test_str_to_boolean();
if (retval != 0)
rc = retval;
return rc;
}
#endif /* UNIT_TEST */