apparmor/parser/parser_lex.l

580 lines
13 KiB
Text
Raw Normal View History

/* $Id$ */
/*
2007-04-11 08:12:51 +00:00
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
* 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.
*/
/* Definitions section */
/* %option main */
/* eliminates need to link with libfl */
%option noyywrap
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#define _(s) gettext(s)
#include "parser.h"
#include "parser_include.h"
#include "parser_yacc.h"
#ifdef PDEBUG
#undef PDEBUG
#endif
/* #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 */
int current_lineno = 1;
struct ignored_suffix_t {
char * text;
int len;
int silent;
};
struct ignored_suffix_t ignored_suffixes[] = {
/* Debian packging files, which are in flux during install
should be silently ignored. */
{ ".dpkg-new", 9, 1 },
{ ".dpkg-old", 9, 1 },
{ ".dpkg-dist", 10, 1 },
/* RPM packaging files have traditionally not been silently
ignored */
{ ".rpmnew", 7, 0 },
{ ".rpmsave", 8, 0 },
/* Backup files should be mentioned */
{ "~", 1, 0 },
{ NULL, 0, 0 }
};
void include_filename(char *filename, int search)
{
FILE *include_file = NULL;
struct stat my_stat;
char *fullpath = NULL;
if (search) include_file = search_path(filename, &fullpath);
else {
fullpath = strdup(filename);
include_file = fopen(fullpath, "r");
}
if (!include_file) yyerror(_("Could not open '%s'"), fullpath);
if (fstat(fileno(include_file), &my_stat))
yyerror(_("fstat failed for '%s'"), fullpath);
if (S_ISREG(my_stat.st_mode)) {
yyin = include_file;
PDEBUG("Opened include \"%s\"\n", fullpath);
yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE ));
}
if (S_ISDIR(my_stat.st_mode)) {
DIR *dir = NULL;
char *dirent_path = NULL;
struct dirent *dirent;
PDEBUG("Opened include directory \"%s\"\n", fullpath);
if (!(dir = opendir(fullpath)))
yyerror(_("opendir failed '%s'"), fullpath);
fclose(include_file);
include_file = NULL;
while ((dirent = readdir(dir)) != NULL) {
int name_len;
struct ignored_suffix_t *suffix;
/* skip dotfiles silently. */
if (dirent->d_name[0] == '.')
continue;
if (dirent_path) free(dirent_path);
if (asprintf(&dirent_path, "%s/%s", fullpath, dirent->d_name)<0)
yyerror("Out of memory");
name_len = strlen(dirent->d_name);
/* skip blacklisted suffixes */
for (suffix = ignored_suffixes; suffix->text; suffix++) {
char *found;
if ( (found = strstr(dirent->d_name, suffix->text)) &&
found - dirent->d_name + suffix->len == name_len ) {
name_len = 0;
if (!suffix->silent)
PERROR("Ignoring: '%s'\n", dirent_path);
break;
}
}
if (!name_len) continue;
if (stat(dirent_path, &my_stat))
yyerror(_("stat failed for '%s'"), dirent_path);
if (S_ISREG(my_stat.st_mode)) {
if (!(yyin = fopen(dirent_path,"r")))
yyerror(_("Could not open '%s'"), filename);
PDEBUG("Opened include \"%s\"\n", filename);
yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE ));
}
}
if (dirent_path) free(dirent_path);
closedir(dir);
}
}
%}
UP "^"
OPEN_BRACE \{
CLOSE_BRACE \}
SLASH \/
COLON :
END_OF_RULE [,]
SEPERATOR {UP}
RANGE -
2008-04-16 04:44:21 +00:00
MODE_CHARS ([RrWwaLlMmkXx])|(([Uu]|[Pp]|[Cc])[Xx])|(([Pp]|[Cc])?[Ii][Xx])
MODES {MODE_CHARS}+
WS [[:blank:]]
NUMBER [[:digit:]]+
ID [^ \t\n"!,]|(,[^ \t\n"!])
POST_VAR_ID [^ =\+\t\n"!,]|(,[^ =\+\t\n"!])
IP {NUMBER}\.{NUMBER}\.{NUMBER}\.{NUMBER}
ALLOWED_QUOTED_ID [^\0"]|\\\"
QUOTED_ID \"{ALLOWED_QUOTED_ID}*\"
HAT hat[ \t]+
KEYWORD [[:alpha:]_]+
VARIABLE_NAME [[:alpha:]][[:alnum:]_]*
SET_VAR_PREFIX @
SET_VARIABLE {SET_VAR_PREFIX}(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
FLAGOPEN_PAREN \(
FLAGCLOSE_PAREN \)
FLAGSEP \,
EQUALS =
ADD_ASSIGN \+=
ARROW ->
2008-04-06 18:55:46 +00:00
LT_EQUAL <=
%x SUB_NAME
%x SUB_NAME2
%x NETWORK_MODE
%x FLAGS_MODE
%x ASSIGN_MODE
2008-04-06 18:55:46 +00:00
%x RLIMIT_MODE
%x CHANGE_PROFILE_MODE
%x INCLUDE
%%
<INCLUDE>{
{WS}+ { /* Eat whitespace */ }
\<([^\> \t\n]+)\> { /* <filename> */
char *filename = strdup(yytext);
filename[strlen(filename)-1]='\0';
include_filename(filename+1, 1);
free(filename);
BEGIN(INITIAL);
}
\"([^\" \t\n]+)\" { /* "filename" */
char *filename = strdup(yytext);
filename[strlen(filename)-1]='\0';
include_filename(filename+1, 0);
free(filename);
BEGIN(INITIAL);
}
[^\<\>\"{WS}]+ { /* filename */
include_filename(yytext, 0);
BEGIN(INITIAL);
}
}
<<EOF>> {
yypop_buffer_state();
if ( !YY_CURRENT_BUFFER ) yyterminate();
}
<SUB_NAME>{
{ID}+ {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* seperate state. */
yylval = (YYSTYPE) processunquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval);
BEGIN(INITIAL);
return TOK_ID;
}
{QUOTED_ID} {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* seperate state. */
yylval = (YYSTYPE) processquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval);
BEGIN(INITIAL);
return TOK_ID;
}
[^\n] {
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
}
<SUB_NAME2>{
{ID}+ {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* seperate state. */
yylval = (YYSTYPE) processunquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval);
BEGIN(INITIAL);
return TOK_ID;
}
{QUOTED_ID} {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* seperate state. */
yylval = (YYSTYPE) processquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval);
BEGIN(INITIAL);
return TOK_ID;
}
{WS}+ { /* Ignoring whitespace */ }
[^\n] {
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
}
<FLAGS_MODE>{
{FLAGOPEN_PAREN} {
PDEBUG("FLag (\n");
return TOK_FLAG_OPENPAREN;
}
{FLAGCLOSE_PAREN} {
PDEBUG("Flag )\n");
BEGIN(INITIAL);
return TOK_FLAG_CLOSEPAREN;
}
2007-11-16 09:31:33 +00:00
{WS}+ { /* Eat whitespace */ }
{FLAGSEP} {
PDEBUG("Flag , \n");
return TOK_FLAG_SEP;
}
{EQUALS} {
PDEBUG("Flag = \n");
return TOK_EQUALS;
}
{KEYWORD} {
2007-11-16 09:31:33 +00:00
yylval = (YYSTYPE) strdup(yytext);
return TOK_FLAG_ID;
}
2007-11-16 09:31:33 +00:00
[^\n] {
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
}
<ASSIGN_MODE>{
{WS}+ { /* Eat whitespace */ }
{ID}+ {
yylval = (YYSTYPE) processunquoted(yytext, yyleng);
PDEBUG("Found assignment value: \"%s\"\n", yylval);
return TOK_VALUE;
}
{QUOTED_ID} {
yylval = (YYSTYPE) processquoted(yytext, yyleng);
PDEBUG("Found assignment value: \"%s\"\n", yylval);
return TOK_VALUE;
}
\\\n { current_lineno++ ; }
\r?\n {
current_lineno++;
BEGIN(INITIAL);
}
}
<NETWORK_MODE>{
{WS}+ { /* Eat whitespace */ }
{ID}+ {
yylval = (YYSTYPE) strdup(yytext);
return TOK_ID;
}
{END_OF_RULE} {
BEGIN(INITIAL);
return TOK_END_OF_RULE;
}
[^\n] {
/* Something we didn't expect */
yylval = (YYSTYPE) strdup(yytext);
yyerror(_("(network_mode) Found unexpected character: '%s'"), yylval);
}
\r?\n {
current_lineno++;
}
}
<CHANGE_PROFILE_MODE>{
{ARROW} {
PDEBUG("Matched a arrow\n");
yylval = (YYSTYPE) yytext;
return TOK_ARROW;
}
{ID}+ {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* seperate state. */
yylval = (YYSTYPE) processunquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval);
BEGIN(INITIAL);
return TOK_ID;
}
{QUOTED_ID} {
/* Ugh, this is a gross hack. I used to use
* {ID}+ to match all TOK_IDs, but that would
* also match TOK_MODE + TOK_END_OF_RULE
* without any spaces in between (because it's
* a longer match). So now, when I want to
* match any random string, I go into a
* seperate state. */
yylval = (YYSTYPE) processquoted(yytext, yyleng);
PDEBUG("Found sub name: \"%s\"\n", yylval);
BEGIN(INITIAL);
return TOK_ID;
}
{WS}+ { /* Ignoring whitespace */ }
[^\n] {
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
}
#include/.*\r?\n { /* include */
PDEBUG("Matched #include\n");
current_lineno++;
BEGIN(INCLUDE);
}
#.*\r?\n { /* normal comment */
PDEBUG("comment(%d): %s\n", current_lineno, yytext);
current_lineno++;
BEGIN(INITIAL);
}
{END_OF_RULE} { return TOK_END_OF_RULE; }
{SEPERATOR} {
PDEBUG("Matched a seperator\n");
yylval = (YYSTYPE) yytext;
BEGIN(SUB_NAME);
return TOK_SEP;
}
{ARROW} {
PDEBUG("Matched a arrow\n");
yylval = (YYSTYPE) yytext;
return TOK_ARROW;
}
{EQUALS} {
PDEBUG("Matched equals for assignment\n");
BEGIN(ASSIGN_MODE);
return TOK_EQUALS;
}
{ADD_ASSIGN} {
PDEBUG("Matched additive value assignment\n");
BEGIN(ASSIGN_MODE);
return TOK_ADD_ASSIGN;
}
2008-04-06 18:55:46 +00:00
<RLIMIT_MODE>{
{WS}+ { /* Eat whitespace */ }
-?{NUMBER}[kKMG]? {
yylval = (YYSTYPE) strdup(yytext);
return TOK_VALUE;
}
{KEYWORD} {
yylval = (YYSTYPE) strdup(yytext);
if (strcmp(yytext, "infinity") == 0)
return TOK_VALUE;
return TOK_ID;
}
{LT_EQUAL} { return TOK_LE; }
{END_OF_RULE} {
BEGIN(INITIAL);
return TOK_END_OF_RULE;
}
\\\n {
current_lineno++;
BEGIN(INITIAL);
}
\r?\n {
current_lineno++;
BEGIN(INITIAL);
}
}
{SET_VARIABLE} {
yylval = (YYSTYPE) strdup(yytext);
PDEBUG("Found set variable %s\n", yylval);
return TOK_SET_VAR;
}
{BOOL_VARIABLE} {
yylval = (YYSTYPE) strdup(yytext);
PDEBUG("Found boolean variable %s\n", yylval);
return TOK_BOOL_VAR;
}
{OPEN_BRACE} {
PDEBUG("Open Brace\n");
return TOK_OPEN;
}
{CLOSE_BRACE} {
PDEBUG("Close Brace\n");
return TOK_CLOSE;
}
{PATHNAME} {
yylval = (YYSTYPE) processunquoted(yytext, yyleng);
PDEBUG("Found id: \"%s\"\n", yylval);
return TOK_ID;
}
{QPATHNAME} {
yylval = (YYSTYPE) processquoted(yytext, yyleng);
PDEBUG("Found id: \"%s\"\n", yylval);
return TOK_ID;
}
{MODES} {
yylval = (YYSTYPE) strdup(yytext);
PDEBUG("Found modes: %s\n", yylval);
return TOK_MODE;
}
{HAT} {
BEGIN(SUB_NAME2);
return TOK_HAT;
}
{COLON} {
PDEBUG("Found a colon\n");
return TOK_COLON;
}
2007-11-16 09:31:33 +00:00
{FLAGOPEN_PAREN} {
PDEBUG("FLag (\n");
BEGIN(FLAGS_MODE);
return TOK_FLAG_OPENPAREN;
}
{VARIABLE_NAME} {
int token = get_keyword_token(yytext);
/* special cases */
switch (token) {
case -1:
/* no token found */
yylval = (YYSTYPE) processunquoted(yytext, yyleng);
PDEBUG("Found id: \"%s\"\n", yylval);
return TOK_ID;
break;
case TOK_PROFILE:
BEGIN(SUB_NAME2);
break;
case TOK_FLAGS:
BEGIN(FLAGS_MODE);
break;
2008-04-06 18:55:46 +00:00
case TOK_RLIMIT:
BEGIN(RLIMIT_MODE);
break;
case TOK_NETWORK:
BEGIN(NETWORK_MODE);
break;
case TOK_CHANGE_PROFILE:
BEGIN(CHANGE_PROFILE_MODE);
break;
default: /* nothing */
break;
}
return token;
}
{WS}+ { /* Ignoring whitespace */ }
\r?\n { current_lineno++ ; }
[^\n] {
/* Something we didn't expect */
yyerror(_("Found unexpected character: '%s'"), yytext);
}
%%