apparmor/parser/parser_yacc.y
John Johansen 3cb147e25c [https://bugzilla.novell.com/show_bug.cgi?id=172061]
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.
2006-08-04 17:14:49 +00:00

898 lines
20 KiB
Text

%{
/* $Id$ */
/*
* Copyright (c) 1999, 2000, 2001, 2002, 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.
*/
#define YYERROR_VERBOSE 1
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#define _(s) gettext(s)
/* #define DEBUG */
#include "parser.h"
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#ifndef NOLIBCAP
#include <sys/capability.h>
#else
#include <linux/capability.h>
#endif
/* A few utility defines */
#define CIDR_32 htonl(0xffffffff)
#define CIDR_24 htonl(0xffffff00)
#define CIDR_16 htonl(0xffff0000)
#define CIDR_8 htonl(0xff000000)
#define CAP_TO_MASK(x) (1 << (x))
static struct flagval force_complain_flags = {0, 1, 0};
/* from lex_config, for nice error messages */
/* extern char *current_file; */
extern int current_lineno;
struct value_list {
char *value;
struct value_list *next;
};
void free_value_list(struct value_list *list);
%}
%token TOK_ID
%token TOK_SEP
%token TOK_OPEN
%token TOK_CLOSE
%token TOK_MODE
%token TOK_END_OF_RULE
%token TOK_EQUALS
%token TOK_ADD_ASSIGN
%token TOK_SET_VAR
%token TOK_BOOL_VAR
%token TOK_VALUE
%token TOK_IF
%token TOK_ELSE
%token TOK_NOT
%token TOK_DEFINED
/* network tokens */
%token TOK_IP
%token TOK_IFACE
%token TOK_ACTION
%token TOK_PORT
%token TOK_PORT_IDENT
%token TOK_NUM
%token TOK_COLON
%token TOK_SLASH
%token TOK_RANGE
%token TOK_VIA
%token TOK_TO
%token TOK_FROM
%token TOK_TCP_CONN
%token TOK_TCP_ACPT
%token TOK_TCP_CONN_ESTB
%token TOK_TCP_ACPT_ESTB
%token TOK_UDP_SEND
%token TOK_UDP_RECV
/* capabilities */
%token TOK_CAPABILITY
%token TOK_CAP_CHOWN
%token TOK_CAP_DAC_OVERRIDE
%token TOK_CAP_DAC_READ_SEARCH
%token TOK_CAP_FOWNER
%token TOK_CAP_FSETID
%token TOK_CAP_KILL
%token TOK_CAP_SETGID
%token TOK_CAP_SETUID
%token TOK_CAP_SETPCAP
%token TOK_CAP_LINUX_IMMUTABLE
%token TOK_CAP_NET_BIND_SERVICE
%token TOK_CAP_NET_BROADCAST
%token TOK_CAP_NET_ADMIN
%token TOK_CAP_NET_RAW
%token TOK_CAP_IPC_LOCK
%token TOK_CAP_IPC_OWNER
%token TOK_CAP_SYS_MODULE
%token TOK_CAP_SYS_RAWIO
%token TOK_CAP_SYS_CHROOT
%token TOK_CAP_SYS_PTRACE
%token TOK_CAP_SYS_PACCT
%token TOK_CAP_SYS_ADMIN
%token TOK_CAP_SYS_BOOT
%token TOK_CAP_SYS_NICE
%token TOK_CAP_SYS_RESOURCE
%token TOK_CAP_SYS_TIME
%token TOK_CAP_SYS_TTY_CONFIG
%token TOK_CAP_MKNOD
%token TOK_CAP_LEASE
/* debug flag values */
%token TOK_FLAGS
%token TOK_FLAG_OPENPAREN
%token TOK_FLAG_CLOSEPAREN
%token TOK_FLAG_SEP
%token TOK_FLAG_DEBUG
%token TOK_FLAG_COMPLAIN
%token TOK_FLAG_AUDIT
%union {
char *id;
char *ip;
char *iface;
char *mode;
char *eth;
/* char * action; */
char *via;
/* char * port; */
unsigned long int num;
struct codomain *cod;
struct cod_global_entry *entry;
struct cod_net_entry *net_entry;
struct cod_entry *user_entry;
struct ipv4_desc *ipv4;
struct ipv4_endpoints *endpoints;
unsigned short (*port)[2];
int action;
struct flagval flags;
unsigned int cap;
char *set_var;
char *bool_var;
char *var_val;
struct value_list *val_list;
int boolean;
}
%type <id> TOK_ID
%type <mode> TOK_MODE
%type <cod> profile
%type <cod> rules
%type <cod> hat
%type <cod> cond_rule
%type <net_entry> netrule
%type <user_entry> rule
%type <ipv4> address
%type <endpoints> addresses
%type <num> mask
%type <port> ports
%type <ip> TOK_IP
%type <iface> TOK_IFACE interface
%type <action> TOK_ACTION
%type <via> TOK_VIA
%type <port> TOK_PORT_IDENT
%type <num> TOK_NUM
%type <action> action
%type <flags> flags
%type <flags> flagvals
%type <flags> flagval
%type <cap> cap
%type <cap> capability
%type <set_var> TOK_SET_VAR
%type <bool_var> TOK_BOOL_VAR
%type <var_val> TOK_VALUE
%type <val_list> valuelist
%type <boolean> expr
%%
list: varlist profilelist
{ /* nothing */ };
profilelist: { /* nothing */ };
profilelist: profilelist profile
{
PDEBUG("Matched: list profile\n");
add_to_list($2);
};
profile: TOK_ID flags TOK_OPEN rules TOK_CLOSE
{
struct codomain *cod = $4;
PDEBUG("Matched: id (%s) open rules close\n", $1);
if (!cod) {
yyerror(_("Memory allocation error."));
}
cod->name = $1;
cod->flags = $2;
if (force_complain)
cod->flags = force_complain_flags;
PDEBUG("%s: flags='%s%s'\n",
$1,
cod->flags.complain ? "complain, " : "",
cod->flags.audit ? "audit" : "");
$$ = cod;
};
varlist: { /* nothing */ }
varlist: varlist varassign
{ /* nothing */ }
varassign: TOK_SET_VAR TOK_EQUALS valuelist
{
struct value_list *list = $3;
char *var_name = process_var($1);
int err;
if (!list || !list->value)
yyerror("Assert: valuelist returned NULL");
PDEBUG("Matched: set assignment for (%s)\n", $1);
err = new_set_var(var_name, list->value);
if (err) {
yyerror("variable %s was previously declared", $1);
/* FIXME: it'd be handy to report the previous location */
}
for (list = list->next; list; list = list->next) {
err = add_set_value(var_name, list->value);
if (err)
yyerror("Error adding %s to set var %s",
list->value, $1);
}
free_value_list($3);
free(var_name);
free($1);
}
varassign: TOK_SET_VAR TOK_ADD_ASSIGN valuelist
{
struct value_list *list = $3;
char *var_name = process_var($1);
int err;
if (!list || !list->value)
yyerror("Assert: valuelist returned NULL");
PDEBUG("Matched: additive assignment for (%s)\n", $1);
/* do the first one outside the loop, subsequent
* failures are indicative of symtab failures */
err = add_set_value(var_name, list->value);
if (err) {
yyerror("variable %s was not previously declared, but is being assigned additional values", $1);
}
for (list = list->next; list; list = list->next) {
err = add_set_value(var_name, list->value);
if (err)
yyerror("Error adding %s to set var %s",
list->value, $1);
}
free_value_list($3);
free(var_name);
free($1);
}
varassign: TOK_BOOL_VAR TOK_EQUALS TOK_VALUE
{
int boolean, err;
char *var_name = process_var($1);
PDEBUG("Matched: boolean assignment (%s) to %s\n", $1, $3);
boolean = str_to_boolean($3);
if (boolean == -1) {
yyerror("Invalid boolean assignment for (%s): %s is not true or false",
$1, $3);
}
err = add_boolean_var(var_name, boolean);
if (err) {
yyerror("variable %s was previously declared", $1);
/* FIXME: it'd be handy to report the previous location */
}
free(var_name);
free($1);
free($3);
}
valuelist: TOK_VALUE
{
struct value_list *new = malloc(sizeof(struct value_list));
if (!new)
yyerror(_("Memory allocation error."));
PDEBUG("Matched: value (%s)\n", $1);
new->value = $1;
new->next = NULL;
$$ = new;
}
valuelist: valuelist TOK_VALUE
{
struct value_list *new = malloc(sizeof(struct value_list));
if (!new)
yyerror(_("Memory allocation error."));
PDEBUG("Matched: value (%s)\n", $1);
new->value = $2;
new->next = $1;
$$ = new;
}
flags: { /* nothing */
struct flagval fv = { 0, 0, 0 };
$$ = fv;
};
flags: TOK_FLAGS TOK_EQUALS TOK_FLAG_OPENPAREN flagvals TOK_FLAG_CLOSEPAREN
{
$$ = $4;
};
flagvals: flagvals TOK_FLAG_SEP flagval
{
$1.complain = $1.complain || $3.complain;
$1.audit = $1.audit || $3.audit;
$$ = $1;
};
flagvals: flagval
{
$$ = $1;
};
flagval: TOK_FLAG_DEBUG
{
PDEBUG("Matched: flag debug\n");
yyerror(_("flags=(debug) is no longer supported, sorry."));
};
flagval: TOK_FLAG_COMPLAIN
{
struct flagval fv = { 0, 1, 0 };
PDEBUG("Matched: flag complain\n");
$$ = fv;
};
flagval: TOK_FLAG_AUDIT
{
struct flagval fv = { 0, 0, 1 };
PDEBUG("Matched: flag audit\n");
$$ = fv;
};
rules: { /* nothing */
struct codomain *cod = NULL;
cod = (struct codomain *) calloc(1, sizeof(struct codomain));
if (!cod) {
yyerror(_("Memory allocation error."));
}
$$ = cod;
};
rules: rules rule
{
PDEBUG("matched: rules rule\n");
PDEBUG("rules rule: (%s)\n", $2->name);
if (!$2)
yyerror(_("Assert: `rule' returned NULL."));
add_entry_to_policy($1, $2);
$$ = $1;
};
rules: rules netrule
{
PDEBUG("Matched: netrules rule\n");
if (!$2)
yyerror(_("Assert: `netrule' returned NULL."));
PDEBUG("Assigning %s\n", inet_ntoa(*$2->saddr));
PDEBUG("Assigning %s\n", inet_ntoa(*$2->daddr));
add_netrule_to_policy($1, $2);
$$ = $1;
};
rules: rules capability
{
$1->capabilities = $1->capabilities | CAP_TO_MASK($2);
$$ = $1;
};
rules: rules hat
{
PDEBUG("Matched: hat rule\n");
if (!$2)
yyerror(_("Assert: 'hat rule' returned NULL."));
add_hat_to_policy($1, $2);
$$ = $1;
};
rules: rules cond_rule
{
PDEBUG("Matched: conditional rules\n");
$$ = merge_policy($1, $2);
}
cond_rule: TOK_IF expr TOK_OPEN rules TOK_CLOSE
{
struct codomain *ret = NULL;
PDEBUG("Matched: found conditional rules\n");
if ($2) {
ret = $4;
} else {
free_policy($4);
}
$$ = ret;
}
cond_rule: TOK_IF expr TOK_OPEN rules TOK_CLOSE TOK_ELSE TOK_OPEN rules TOK_CLOSE
{
struct codomain *ret = NULL;
PDEBUG("Matched: found conditional else rules\n");
if ($2) {
ret = $4;
free_policy($8);
} else {
ret = $8;
free_policy($4);
}
$$ = ret;
}
cond_rule: TOK_IF expr TOK_OPEN rules TOK_CLOSE TOK_ELSE cond_rule
{
struct codomain *ret = NULL;
PDEBUG("Matched: found conditional else-if rules\n");
if ($2) {
ret = $4;
free_policy($7);
} else {
ret = $7;
free_policy($4);
}
$$ = ret;
}
expr: TOK_NOT expr
{
$$ = !$2;
}
expr: TOK_BOOL_VAR
{
char *var_name = process_var($1);
int boolean = get_boolean_var(var_name);
PDEBUG("Matched: boolean expr %s value: %d\n", $1, boolean);
if (boolean < 0) {
/* FIXME check for set var */
yyerror(_("Unset boolean variable %s used in if-expression"),
$1);
}
$$ = boolean;
free(var_name);
free($1);
}
expr: TOK_DEFINED TOK_SET_VAR
{
char *var_name = process_var($2);
void *set_value = get_set_var(var_name);
PDEBUG("Matched: defined set expr %s value %lx\n", $2, (long) set_value);
$$ = !! (long) set_value;
free(var_name);
free($2);
}
expr: TOK_DEFINED TOK_BOOL_VAR
{
char *var_name = process_var($2);
int boolean = get_boolean_var(var_name);
PDEBUG("Matched: defined set expr %s value %d\n", $2, boolean);
$$ = (boolean != -1);
free(var_name);
free($2);
}
rule: TOK_ID TOK_MODE TOK_END_OF_RULE
{
struct cod_entry *entry;
PDEBUG("Matched: tok_id (%s) tok_mode (%s)\n", $1, $2);
entry = new_entry($1, $2);
if (!entry)
yyerror(_("Memory allocation error."));
PDEBUG("rule.entry: (%s)\n", entry->name);
free($2);
$$ = entry;
};
rule: TOK_SET_VAR TOK_MODE TOK_END_OF_RULE
{
struct cod_entry *entry;
PDEBUG("Matched: tok_id (%s) tok_mode (%s)\n", $1, $2);
entry = new_entry($1, $2);
if (!entry)
yyerror(_("Memory allocation error."));
PDEBUG("rule.entry: (%s)\n", entry->name);
free($2);
$$ = entry;
};
rule: TOK_ID TOK_MODE TOK_ID
{
/* Oopsie, we appear to be missing an EOL marker. If we
* were *smart*, we could work around it. Since we're
* obviously not smart, we'll just punt with a more
* sensible error. */
yyerror(_("missing an end of line character? (entry: %s)"), $1);
};
hat: TOK_SEP TOK_ID flags TOK_OPEN rules TOK_CLOSE
{
struct codomain *cod = $5;
PDEBUG("Matched: sep id (%s) open rules close\n", $2);
if (!cod) {
yyerror(_("Memory allocation error."));
}
cod->name = $2;
cod->flags = $3;
if (force_complain)
cod->flags = force_complain_flags;
PDEBUG("^%s: flags='%s%s'\n",
$2,
cod->flags.complain ? "complain, " : "",
cod->flags.audit ? "audit" : "");
$$ = cod;
};
/*
* The addition of an entirely new grammer set to our (previously) slim
* profile spec is below. It's designed to look quite similar to
* (Free|Open)BSD's ipfw rules:
*
* 'tcp_connect to 10.0.0.40:80 via eth0' for example.
*
*
* mb:
* We need to verify the following rules:
*/
netrule: action addresses interface TOK_END_OF_RULE
{
struct cod_net_entry *entry;
entry = NULL;
if (!$2)
yyerror(_("Assert: `addresses' returned NULL."));
PDEBUG("Matched action (%d) via (%s)\n", $1, $3);
entry = new_network_entry($1, $2, $3);
if (!entry)
yyerror(_("Memory allocation error."));
free_ipv4_endpoints($2);
$$ = entry;
};
action: TOK_TCP_CONN { $$ = KERN_COD_TCP_CONNECT; }
| TOK_TCP_ACPT { $$ = KERN_COD_TCP_ACCEPT; }
| TOK_TCP_CONN_ESTB { $$ = KERN_COD_TCP_CONNECTED; }
| TOK_TCP_ACPT_ESTB { $$ = KERN_COD_TCP_ACCEPTED; }
| TOK_UDP_SEND { $$ = KERN_COD_UDP_SEND; }
| TOK_UDP_RECV { $$ = KERN_COD_UDP_RECEIVE; }
;
interface: /* nothing, no interface specified */
{
$$ = NULL;
};
interface: TOK_VIA TOK_IFACE
{
PDEBUG ("Matched an interface (%s)\n", $2);
$$ = $2;
};
addresses: /* Nothing */
{
struct ipv4_endpoints *addresses;
addresses = (struct ipv4_endpoints *)
malloc (sizeof (struct ipv4_endpoints));
if (!addresses)
yyerror(_("Memory allocation error."));
addresses->src = NULL;
addresses->dest = NULL;
$$ = addresses;
};
addresses: TOK_TO address
{
struct ipv4_endpoints *addresses;
addresses = (struct ipv4_endpoints *)
malloc(sizeof (struct ipv4_endpoints));
if (!addresses)
yyerror(_("Memory allocation error."));
addresses->src = NULL;
addresses->dest = $2;
$$ = addresses;
};
addresses: TOK_FROM address
{
struct ipv4_endpoints *addresses;
addresses = (struct ipv4_endpoints *)
malloc(sizeof (struct ipv4_endpoints));
if (!addresses)
yyerror(_("Memory allocation error."));
addresses->src = $2;
addresses->dest = NULL;
$$ = addresses;
};
addresses: TOK_FROM address TOK_TO address
{
struct ipv4_endpoints *addresses;
addresses = (struct ipv4_endpoints *)
malloc (sizeof (struct ipv4_endpoints));
if (!addresses)
yyerror(_("Memory allocation error."));
addresses->src = $2;
addresses->dest = $4;
$$ = addresses;
};
addresses: TOK_TO address TOK_FROM address
{
struct ipv4_endpoints *addresses;
addresses = (struct ipv4_endpoints *)
malloc(sizeof (struct ipv4_endpoints));
if (!addresses)
yyerror(_("Memory allocation error."));
addresses->src = $4;
addresses->dest = $2;
$$ = addresses;
};
addresses: TOK_TO address TOK_TO
{
/* better error warnings (hopefully) */
yyerror(_("Network entries can only have one TO address."));
};
addresses: TOK_FROM address TOK_FROM
{
/* better error warnings (hopefully) */
yyerror(_("Network entries can only have one FROM address."));
};
address: TOK_IP ports
{
/* Bleah, I have to handle address as two rules, because
* if the user provides an ip of 0.0.0.0 and no mask, we
* treat it as 0.0.0.0/0 instead of 0.0.0.0/32. */
struct ipv4_desc *address;
address = (struct ipv4_desc *)
malloc (sizeof (struct ipv4_desc));
if (!address)
yyerror(_("Memory allocation error."));
address->port[0] = (*$2)[0];
address->port[1] = (*$2)[1];
if (inet_aton($1, &(address->addr)) == 0)
yyerror(_("`%s' is not a valid ip address."), $1);
if (address->addr.s_addr == 0) {
/* the user specified 0.0.0.0 without giving an
* explicit mask, so treat it as 0.0.0.0/0 */
address->mask = htonl (0UL);
} else {
/* otherwise, treat it as /32 */
address->mask = htonl (0xffffffff);
}
PDEBUG("Matched an IP (%s/%d:%d-%d)\n",
inet_ntoa(address->addr), address->mask,
address->port[0], address->port[1]);
free($1);
free(*$2);
$$ = address;
};
address: TOK_IP mask ports
{
struct ipv4_desc *address;
address = (struct ipv4_desc *)
malloc(sizeof (struct ipv4_desc));
if (!address)
yyerror(_("Memory allocation error."));
address->mask = $2;
address->port[0] = (*$3)[0];
address->port[1] = (*$3)[1];
if (inet_aton($1, &(address->addr)) == 0)
yyerror(_("`%s' is not a valid ip address."), $1);
PDEBUG("Matched an IP (%s/%d:%d-%d)\n",
inet_ntoa(address->addr), address->mask,
address->port[0], address->port[1]);
free($1);
free(*$3);
$$ = address;
};
mask: TOK_SLASH TOK_NUM
{
PDEBUG("Matched a netmask (%d)\n", $2);
if (($2 < 0) || ($2 > 32))
yyerror(_("`/%d' is not a valid netmask."), $2);
$$ = htonl(0xffffffff << (32 - $2));
};
mask: TOK_SLASH TOK_IP
{
struct in_addr mask;
if (inet_aton($2, &mask) == 0)
yyerror(_("`%s' is not a valid netmask."), $2);
PDEBUG("Matched a netmask (%d)\n", mask.s_addr);
$$ = mask.s_addr;
};
ports: {
/* nothing, return all ports */
unsigned short (*ports)[2];
ports = (unsigned short (*)[2])
malloc(sizeof (unsigned short [2]));
if (!ports)
yyerror(_("Memory allocation error."));
(*ports)[0] = MIN_PORT;
(*ports)[1] = MAX_PORT;
$$ = ports;
};
ports: TOK_COLON TOK_NUM
{
unsigned short (*ports)[2];
PDEBUG("Matched a single port (%d)\n", $2);
ports = (unsigned short (*)[2])
malloc(sizeof (unsigned short [2]));
if (($2 < MIN_PORT) || ($2 > MAX_PORT))
yyerror(_("ports must be between %d and %d"),
MIN_PORT, MAX_PORT);
if (!ports)
yyerror(_("Memory allocation error."));
(*ports)[0] = $2;
(*ports)[1] = $2;
$$ = ports;
};
ports: TOK_COLON TOK_NUM TOK_RANGE TOK_NUM
{
unsigned short (*ports)[2];
PDEBUG("Matched a port range (%d,%d)\n", $2, $4);
ports = (unsigned short (*)[2])
malloc(sizeof (unsigned short [2]));
if (!ports)
yyerror(_("Memory allocation error."));
if (($2 < MIN_PORT) || ($4 > MAX_PORT)
|| ($2 < MIN_PORT) || ($4 > MAX_PORT))
yyerror(_("ports must be between %d and %d"),
MIN_PORT, MAX_PORT);
(*ports)[0] = $2;
(*ports)[1] = $4;
if ((*ports)[0] > (*ports)[1])
{
unsigned short tmp;
pwarn("expected first port number to be less than the second, swapping (%ld,%ld)\n",
$2, $4);
tmp = (*ports)[0];
(*ports)[0] = (*ports)[1];
(*ports)[1] = tmp;
}
$$ = ports;
};
capability: TOK_CAPABILITY cap TOK_END_OF_RULE
{
$$ = $2;
};
cap: TOK_CAP_CHOWN { $$ = CAP_CHOWN; }
| TOK_CAP_DAC_OVERRIDE { $$ = CAP_DAC_OVERRIDE; }
| TOK_CAP_DAC_READ_SEARCH { $$ = CAP_DAC_READ_SEARCH; }
| TOK_CAP_FOWNER { $$ = CAP_FOWNER; }
| TOK_CAP_FSETID { $$ = CAP_FSETID; }
| TOK_CAP_KILL { $$ = CAP_KILL; }
| TOK_CAP_SETGID { $$ = CAP_SETGID; }
| TOK_CAP_SETUID { $$ = CAP_SETUID; }
| TOK_CAP_SETPCAP { $$ = CAP_SETPCAP; }
| TOK_CAP_LINUX_IMMUTABLE { $$ = CAP_LINUX_IMMUTABLE; }
| TOK_CAP_NET_BIND_SERVICE { $$ = CAP_NET_BIND_SERVICE; }
| TOK_CAP_NET_BROADCAST { $$ = CAP_NET_BROADCAST; }
| TOK_CAP_NET_ADMIN { $$ = CAP_NET_ADMIN; }
| TOK_CAP_NET_RAW { $$ = CAP_NET_RAW; }
| TOK_CAP_IPC_LOCK { $$ = CAP_IPC_LOCK; }
| TOK_CAP_IPC_OWNER { $$ = CAP_IPC_OWNER; }
| TOK_CAP_SYS_MODULE { $$ = CAP_SYS_MODULE; }
| TOK_CAP_SYS_RAWIO { $$ = CAP_SYS_RAWIO; }
| TOK_CAP_SYS_CHROOT { $$ = CAP_SYS_CHROOT; }
| TOK_CAP_SYS_PTRACE { $$ = CAP_SYS_PTRACE; }
| TOK_CAP_SYS_PACCT { $$ = CAP_SYS_PACCT; }
| TOK_CAP_SYS_ADMIN { $$ = CAP_SYS_ADMIN; }
| TOK_CAP_SYS_BOOT { $$ = CAP_SYS_BOOT; }
| TOK_CAP_SYS_NICE { $$ = CAP_SYS_NICE; }
| TOK_CAP_SYS_RESOURCE { $$ = CAP_SYS_RESOURCE; }
| TOK_CAP_SYS_TIME { $$ = CAP_SYS_TIME; }
| TOK_CAP_SYS_TTY_CONFIG { $$ = CAP_SYS_TTY_CONFIG; }
| TOK_CAP_MKNOD { $$ = CAP_MKNOD; }
| TOK_CAP_LEASE { $$ = CAP_LEASE; }
%%
#define MAXBUFSIZE 4096
void yyerror(char *msg, ...)
{
va_list arg;
char buf[MAXBUFSIZE];
va_start(arg, msg);
vsnprintf(buf, sizeof(buf), msg, arg);
va_end(arg);
if (profilename) {
PERROR(_("AppArmor parser error in %s at line %d: %s\n"),
profilename, current_lineno, buf);
} else {
PERROR(_("AppArmor parser error, line %d: %s\n"),
current_lineno, buf);
}
exit(1);
}
void free_value_list(struct value_list *list)
{
struct value_list *next;
while (list) {
next = list->next;
if (list->value)
free(list->value);
free(list);
list = next;
}
}