diff --git a/parser/apparmor_parser.pod b/parser/apparmor_parser.pod index 51f6838a6..874de6f6f 100644 --- a/parser/apparmor_parser.pod +++ b/parser/apparmor_parser.pod @@ -149,6 +149,11 @@ Report on the profiles as they are loaded, and show warnings. Print the version number and exit. +=item -p, --preprocess + +Dump the input profile to stdout out applying preprocessing flattening +includes into the output profile. + =item -d, --debug Given once, only checks the profiles to ensure syntactic correctness. diff --git a/parser/parser.h b/parser/parser.h index cc54167d5..30367512e 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -178,7 +178,7 @@ struct var_string { extern int flag_changehat_version; extern int read_implies_exec; extern dfaflags_t dfaflags; - +extern int preprocess_only; #define PATH_CHROOT_REL 0x1 #define PATH_NS_REL 0x2 diff --git a/parser/parser_lex.l b/parser/parser_lex.l index c9e1bcefc..2a56bf574 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -49,6 +49,8 @@ #endif #define NPDEBUG(fmt, args...) /* Do nothing */ +#define DUMP_PREPROCESS do { if (preprocess_only) ECHO; } while (0) + int current_lineno = 1; char *current_filename = NULL; @@ -81,8 +83,12 @@ void include_filename(char *filename, int search) char *fullpath = NULL; if (search) { + if (preprocess_only) + fprintf(yyout, "\n\n##included <%s>\n", filename); include_file = search_path(filename, &fullpath); } else { + if (preprocess_only) + fprintf(yyout, "\n\n##included \"%s\"\n", filename); fullpath = strdup(filename); include_file = fopen(fullpath, "r"); } @@ -246,6 +252,7 @@ LT_EQUAL <= * a longer match). So now, when I want to * match any random string, I go into a * separate state. */ + DUMP_PREPROCESS; yylval.id = processunquoted(yytext, yyleng); PDEBUG("Found sub name: \"%s\"\n", yylval.id); BEGIN(INITIAL); @@ -259,6 +266,7 @@ LT_EQUAL <= * a longer match). So now, when I want to * match any random string, I go into a * separate state. */ + DUMP_PREPROCESS; yylval.id = processquoted(yytext, yyleng); PDEBUG("Found sub name: \"%s\"\n", yylval.id); BEGIN(INITIAL); @@ -266,6 +274,7 @@ LT_EQUAL <= } [^\n] { + DUMP_PREPROCESS; /* Something we didn't expect */ yyerror(_("Found unexpected character: '%s'"), yytext); } @@ -280,6 +289,7 @@ LT_EQUAL <= * a longer match). So now, when I want to * match any random string, I go into a * separate state. */ + DUMP_PREPROCESS; yylval.id = processunquoted(yytext, yyleng); PDEBUG("Found sub name: \"%s\"\n", yylval.id); BEGIN(INITIAL); @@ -293,14 +303,16 @@ LT_EQUAL <= * a longer match). So now, when I want to * match any random string, I go into a * separate state. */ + DUMP_PREPROCESS; yylval.id = processquoted(yytext, yyleng); PDEBUG("Found sub name: \"%s\"\n", yylval.id); BEGIN(INITIAL); return TOK_ID; } - {WS}+ { /* Ignoring whitespace */ } + {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } [^\n] { + DUMP_PREPROCESS; /* Something we didn't expect */ yyerror(_("Found unexpected character: '%s'"), yytext); } @@ -308,85 +320,99 @@ LT_EQUAL <= { {FLAGOPEN_PAREN} { + DUMP_PREPROCESS; PDEBUG("FLag (\n"); return TOK_FLAG_OPENPAREN; } {FLAGCLOSE_PAREN} { + DUMP_PREPROCESS; PDEBUG("Flag )\n"); BEGIN(INITIAL); return TOK_FLAG_CLOSEPAREN; } - {WS}+ { /* Eat whitespace */ } + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } {FLAGSEP} { + DUMP_PREPROCESS; PDEBUG("Flag , \n"); return TOK_FLAG_SEP; } {EQUALS} { + DUMP_PREPROCESS; PDEBUG("Flag = \n"); return TOK_EQUALS; } {KEYWORD} { + DUMP_PREPROCESS; yylval.flag_id = strdup(yytext); PDEBUG("Found flag: \"%s\"\n", yylval.flag_id); return TOK_FLAG_ID; } [^\n] { + DUMP_PREPROCESS; /* Something we didn't expect */ yyerror(_("Found unexpected character: '%s'"), yytext); } } { - {WS}+ { /* Eat whitespace */ } + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } {ID}+ { + DUMP_PREPROCESS; yylval.var_val = processunquoted(yytext, yyleng); PDEBUG("Found assignment value: \"%s\"\n", yylval.var_val); return TOK_VALUE; } {QUOTED_ID} { + DUMP_PREPROCESS; yylval.var_val = processquoted(yytext, yyleng); PDEBUG("Found assignment value: \"%s\"\n", yylval.var_val); return TOK_VALUE; } - \\\n { current_lineno++ ; } + \\\n { DUMP_PREPROCESS; current_lineno++ ; } \r?\n { + DUMP_PREPROCESS; current_lineno++; BEGIN(INITIAL); } } { - {WS}+ { /* Eat whitespace */ } + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } {ID}+ { + DUMP_PREPROCESS; yylval.id = strdup(yytext); return TOK_ID; } {END_OF_RULE} { + DUMP_PREPROCESS; BEGIN(INITIAL); return TOK_END_OF_RULE; } [^\n] { + DUMP_PREPROCESS; /* Something we didn't expect */ yylval.id = strdup(yytext); yyerror(_("(network_mode) Found unexpected character: '%s'"), yylval.id); } \r?\n { + DUMP_PREPROCESS; current_lineno++; } } { {ARROW} { + DUMP_PREPROCESS; PDEBUG("Matched a change profile arrow\n"); return TOK_ARROW; } @@ -399,6 +425,7 @@ LT_EQUAL <= * a longer match). So now, when I want to * match any random string, I go into a * separate state. */ + DUMP_PREPROCESS; yylval.id = processunquoted(yytext, yyleng); PDEBUG("Found change profile name: \"%s\"\n", yylval.id); BEGIN(INITIAL); @@ -412,14 +439,16 @@ LT_EQUAL <= * a longer match). So now, when I want to * match any random string, I go into a * separate state. */ + DUMP_PREPROCESS; yylval.id = processquoted(yytext, yyleng); PDEBUG("Found change profile quoted name: \"%s\"\n", yylval.id); BEGIN(INITIAL); return TOK_ID; } - {WS}+ { /* Ignoring whitespace */ } + {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } [^\n] { + DUMP_PREPROCESS; /* Something we didn't expect */ yyerror(_("Found unexpected character: '%s'"), yytext); } @@ -431,122 +460,143 @@ LT_EQUAL <= } #.*\r?\n { /* normal comment */ + DUMP_PREPROCESS; PDEBUG("comment(%d): %s\n", current_lineno, yytext); current_lineno++; BEGIN(INITIAL); } -{END_OF_RULE} { return TOK_END_OF_RULE; } +{END_OF_RULE} { DUMP_PREPROCESS; return TOK_END_OF_RULE; } {SEPARATOR} { + DUMP_PREPROCESS; PDEBUG("Matched a separator\n"); BEGIN(SUB_NAME); return TOK_SEP; } {ARROW} { + DUMP_PREPROCESS; PDEBUG("Matched a arrow\n"); return TOK_ARROW; } {EQUALS} { + DUMP_PREPROCESS; PDEBUG("Matched equals for assignment\n"); BEGIN(ASSIGN_MODE); return TOK_EQUALS; } {ADD_ASSIGN} { + DUMP_PREPROCESS; PDEBUG("Matched additive value assignment\n"); BEGIN(ASSIGN_MODE); return TOK_ADD_ASSIGN; } { - {WS}+ { /* Eat whitespace */ } + {WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ } -?{NUMBER}[kKMG]? { + DUMP_PREPROCESS; yylval.var_val = strdup(yytext); return TOK_VALUE; } {KEYWORD} { + DUMP_PREPROCESS; yylval.id = strdup(yytext); if (strcmp(yytext, "infinity") == 0) return TOK_VALUE; return TOK_ID; } - {LT_EQUAL} { return TOK_LE; } + {LT_EQUAL} { DUMP_PREPROCESS; return TOK_LE; } {END_OF_RULE} { + DUMP_PREPROCESS; BEGIN(INITIAL); return TOK_END_OF_RULE; } \\\n { + DUMP_PREPROCESS; current_lineno++; BEGIN(INITIAL); } \r?\n { + DUMP_PREPROCESS; current_lineno++; BEGIN(INITIAL); } } {SET_VARIABLE} { + DUMP_PREPROCESS; yylval.set_var = strdup(yytext); PDEBUG("Found set variable %s\n", yylval.set_var); return TOK_SET_VAR; } {BOOL_VARIABLE} { + DUMP_PREPROCESS; yylval.bool_var = strdup(yytext); PDEBUG("Found boolean variable %s\n", yylval.bool_var); return TOK_BOOL_VAR; } {OPEN_BRACE} { + DUMP_PREPROCESS; PDEBUG("Open Brace\n"); return TOK_OPEN; } {CLOSE_BRACE} { + DUMP_PREPROCESS; PDEBUG("Close Brace\n"); return TOK_CLOSE; } {PATHNAME} { + DUMP_PREPROCESS; yylval.id = processunquoted(yytext, yyleng); PDEBUG("Found id: \"%s\"\n", yylval.id); return TOK_ID; } {QPATHNAME} { + DUMP_PREPROCESS; yylval.id = processquoted(yytext, yyleng); PDEBUG("Found id: \"%s\"\n", yylval.id); return TOK_ID; } {MODES} { + DUMP_PREPROCESS; yylval.mode = strdup(yytext); PDEBUG("Found modes: %s\n", yylval.mode); return TOK_MODE; } {HAT} { + DUMP_PREPROCESS; BEGIN(SUB_NAME2); return TOK_HAT; } {COLON} { + DUMP_PREPROCESS; PDEBUG("Found a colon\n"); return TOK_COLON; } {FLAGOPEN_PAREN} { + DUMP_PREPROCESS; PDEBUG("FLag (\n"); BEGIN(FLAGS_MODE); return TOK_FLAG_OPENPAREN; } {VARIABLE_NAME} { + DUMP_PREPROCESS; int token = get_keyword_token(yytext); /* special cases */ @@ -578,11 +628,13 @@ LT_EQUAL <= return token; } -{WS}+ { /* Ignoring whitespace */ } +{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } -\r?\n { current_lineno++ ; } +\r?\n { DUMP_PREPROCESS; current_lineno++ ; } [^\n] { + DUMP_PREPROCESS; + /* Something we didn't expect */ yyerror(_("Found unexpected character: '%s'"), yytext); } diff --git a/parser/parser_main.c b/parser/parser_main.c index dd32f96bd..f1cd20259 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -76,6 +76,7 @@ int read_implies_exec = 1; #else int read_implies_exec = 0; #endif +int preprocess_only = 0; char *subdomainbase = NULL; char *match_string = NULL; @@ -119,6 +120,7 @@ struct option long_options[] = { {"Dump", 1, 0, 'D'}, {"optimize", 1, 0, 'O'}, {"Optimize", 1, 0, 'O'}, + {"preprocess", 0, 0, 'p'}, {NULL, 0, 0, 0}, }; @@ -158,9 +160,10 @@ static void display_usage(char *command) "-Q, --skip-kernel-load Do everything except loading into kernel\n" "-V, --version Display version info and exit\n" "-d, --debug Debug apparmor definitions\n" + "-p, --preprocess Dump preprocessed profile\n" "-D [n], --dump Dump internal info for debugging\n" "-O [n], --Optimize Control dfa optimizations\n" - "-h [command], --help Display this text or info about command\n" + "-h [cmd], --help[=cmd] Display this text or info about cmd\n" ,command); } @@ -244,7 +247,7 @@ static int process_args(int argc, char *argv[]) int count = 0; option = OPTION_ADD; - while ((c = getopt_long(argc, argv, "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkO:", long_options, &o)) != -1) + while ((c = getopt_long(argc, argv, "adf:h::rRVvI:b:BCD:NSm:qQn:XKTWkO:p", long_options, &o)) != -1) { switch (c) { case 0: @@ -435,6 +438,12 @@ static int process_args(int argc, char *argv[]) case 'Q': kernel_load = 0; break; + case 'p': + count++; + kernel_load = 0; + skip_cache = 1; + preprocess_only = 1; + break; default: display_usage(progname); exit(0); @@ -803,6 +812,9 @@ int process_profile(int option, char *profilename) if (retval != 0) goto out; + if (preprocess_only) + goto out; + if (names_only) { dump_policy_names(); goto out;