diff --git a/parser/parser_include.c b/parser/parser_include.c index 866a18874..4896d2010 100644 --- a/parser/parser_include.c +++ b/parser/parser_include.c @@ -270,3 +270,63 @@ static char *stripblanks(char *s) *s = 0; return c; } + +struct include_stack_t { + char *filename; + int lineno; + struct include_stack_t *next; +}; + +struct include_stack_t *include_stack_head = NULL; + +static void start_include_position(char *filename) +{ + if (current_filename) + free(current_filename); + current_filename = strdup(filename ? filename : "stdin"); + current_lineno = 0; +} + +void push_include_stack(char *filename) +{ + struct include_stack_t *include = NULL; + + include = malloc(sizeof(*include)); + if (!include) { + perror("malloc of included file stack tracker"); + /* failures in this area are non-fatal */ + return; + } + + include->filename = strdup(current_filename); + include->lineno = current_lineno; + include->next = include_stack_head; + include_stack_head = include; + + start_include_position(filename); +} + +void pop_include_stack(void) +{ + struct include_stack_t *include = NULL; + + if (!include_stack_head) + return; + + include = include_stack_head; + include_stack_head = include->next; + + if (current_filename) + free(current_filename); + current_filename = include->filename; + current_lineno = include->lineno; + free(include); +} + +void reset_include_stack(char *filename) +{ + while (include_stack_head) + pop_include_stack(); + + start_include_position(filename); +} diff --git a/parser/parser_include.h b/parser/parser_include.h index 5634768bb..eabe962e7 100644 --- a/parser/parser_include.h +++ b/parser/parser_include.h @@ -1,5 +1,3 @@ -/* $Id$ */ - /* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) @@ -21,6 +19,8 @@ #define PARSER_INCLUDE_H extern int preprocess_only; +extern int current_lineno; +extern char *current_filename; extern int add_search_dir(char *dir); extern void init_base_dir(void); @@ -29,4 +29,8 @@ extern void parse_default_paths(void); extern int do_include_preprocessing(char *profilename); FILE *search_path(char *filename, char **fullpath); +extern void push_include_stack(char *filename); +extern void pop_include_stack(void); +extern void reset_include_stack(char *filename); + #endif diff --git a/parser/parser_lex.l b/parser/parser_lex.l index 979345209..337b28196 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -49,7 +49,8 @@ #endif #define NPDEBUG(fmt, args...) /* Do nothing */ -int current_lineno = 1; +int current_lineno = 0; +char *current_filename = NULL; struct ignored_suffix_t { char * text; @@ -87,7 +88,8 @@ void include_filename(char *filename, int search) } if (!include_file) - yyerror(_("Could not open '%s'"), fullpath); + yyerror(_("Could not open '%s'"), + fullpath ? fullpath: filename); if (fstat(fileno(include_file), &my_stat)) yyerror(_("fstat failed for '%s'"), fullpath); @@ -95,6 +97,7 @@ void include_filename(char *filename, int search) if (S_ISREG(my_stat.st_mode)) { yyin = include_file; PDEBUG("Opened include \"%s\"\n", fullpath); + push_include_stack(fullpath); yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE )); } @@ -139,8 +142,9 @@ void include_filename(char *filename, int search) 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); + yyerror(_("Could not open '%s' in '%s'"), dirent_path, filename); + PDEBUG("Opened include \"%s\" in \"%s\"\n", dirent_path, filename); + push_include_stack(dirent_path); yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE)); } } @@ -228,6 +232,7 @@ LT_EQUAL <= <> { fclose(yyin); + pop_include_stack(); yypop_buffer_state(); if ( !YY_CURRENT_BUFFER ) yyterminate(); } diff --git a/parser/parser_main.c b/parser/parser_main.c index 65b0c59bb..2bf6f9eec 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -89,8 +89,6 @@ int perms_create = 0; /* perms contain create flag */ char *profile_namespace = NULL; int flag_changehat_version = FLAG_CHANGEHAT_1_5; -extern int current_lineno; - /* per-profile settings */ int force_complain = 0; char *profilename = NULL; @@ -228,8 +226,10 @@ void pwarn(char *fmt, ...) if (conf_quiet || names_only || option == OPTION_REMOVE) return; - rc = asprintf(&newfmt, _("Warning (%s line %d): %s"), + rc = asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"), profilename ? profilename : "stdin", + current_filename ? current_filename : "", + current_filename ? " " : "", current_lineno, fmt); if (!newfmt) @@ -699,12 +699,13 @@ int process_binary(int option, char *profilename) return retval; } -void reset_parser(void) +void reset_parser(char *filename) { free_aliases(); free_symtabs(); free_policies(); reset_regex(); + reset_include_stack(filename); } int process_profile(int option, char *profilename) @@ -800,7 +801,7 @@ int process_profile(int option, char *profilename) if (yyin) yyrestart(yyin); - reset_parser(); + reset_parser(profilename); retval = yyparse(); if (retval != 0) diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index c482f32e7..280d4667e 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -32,6 +32,7 @@ /* #define DEBUG */ #include "parser.h" +#include "parser_include.h" #include #include #include @@ -63,10 +64,6 @@ #define CAP_TO_MASK(x) (1ull << (x)) -/* from lex_config, for nice error messages */ -/* extern char *current_file; */ -extern int current_lineno; - struct value_list { char *value; struct value_list *next; @@ -1109,10 +1106,15 @@ void yyerror(char *msg, ...) va_end(arg); if (profilename) { - PERROR(_("AppArmor parser error in %s at line %d: %s\n"), - profilename, current_lineno, buf); + PERROR(_("AppArmor parser error for %s%s%s at line %d: %s\n"), + profilename, + current_filename ? " in " : "", + current_filename ? current_filename : "", + current_lineno, buf); } else { - PERROR(_("AppArmor parser error, line %d: %s\n"), + PERROR(_("AppArmor parser error,%s%s line %d: %s\n"), + current_filename ? " in " : "", + current_filename ? current_filename : "", current_lineno, buf); } diff --git a/parser/tst/Makefile b/parser/tst/Makefile index 8daf26642..de2c315ff 100644 --- a/parser/tst/Makefile +++ b/parser/tst/Makefile @@ -1,8 +1,9 @@ # -# $Id$ -# PROVE=/usr/bin/prove TESTS=simple.pl +PARSER_DIR=.. +PARSER_BIN=apparmor_parser +PARSER=$(PARSER_DIR)/$(PARSER_BIN) ifeq ($(VERBOSE),1) PROVE_ARG=-v @@ -10,9 +11,19 @@ endif all: tests -.PHONY: tests -tests: ../apparmor_parser +.PHONY: tests error_output parser_sanity +tests: error_output parser_sanity + +error_output: $(PARSER) + $(PARSER) -S -I errors >/dev/null errors/okay.sd + LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/single.sd | \ + grep -q "AppArmor parser error for errors/single.sd in errors/single.sd at line 3: Could not open 'failure'" + LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/double.sd | \ + grep -q "AppArmor parser error for errors/double.sd in errors/includes/busted at line 67: Could not open 'does-not-exist'" + @echo "Error Output: PASS" + +parser_sanity: $(PARSER) $(Q)${PROVE} ${PROVE_ARG} ${TESTS} -../apparmor_parser: - make -C .. apparmor_parser +$(PARSER): + make -C $(PARSER_DIR) $(PARSER_BIN) diff --git a/parser/tst/README b/parser/tst/README index 4c4c64986..26a742119 100644 --- a/parser/tst/README +++ b/parser/tst/README @@ -64,5 +64,3 @@ The simple script looks for a few special comments in the profile, loop. Otherwise, the profile is passed on as-is to the subdomain parser. - -$Id$ diff --git a/parser/tst/errors/double.sd b/parser/tst/errors/double.sd new file mode 100644 index 000000000..8c2a2bc46 --- /dev/null +++ b/parser/tst/errors/double.sd @@ -0,0 +1,5 @@ +# +/does/not/exist { + #include + #include +} diff --git a/parser/tst/errors/includes/base b/parser/tst/errors/includes/base new file mode 100644 index 000000000..f34651374 --- /dev/null +++ b/parser/tst/errors/includes/base @@ -0,0 +1,81 @@ +# $Id$ +# ------------------------------------------------------------------ +# +# Copyright (C) 2002-2005 Novell/SUSE +# +# 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. +# +# ------------------------------------------------------------------ + + + + # (Note that the ldd profile has inlined this file; if you make + # modifications here, please consider including them in the ldd + # profile as well.) + + # The __canary_death_handler function writes a time-stamped log + # message to /dev/log for logging by syslogd. So, /dev/log, timezones, + # and localisations of date should be available EVERYWHERE, so + # StackGuard, FormatGuard, etc., alerts can be properly logged. + /dev/log w, + /dev/urandom r, + /etc/locale/** r, + /etc/localtime r, + /usr/share/locale/** r, + /usr/share/zoneinfo/** r, + + /usr/lib64/locale/** r, + /usr/lib64/gconv/*.so r, + /usr/lib64/gconv/gconv-modules* r, + /usr/lib/locale/** r, + /usr/lib/gconv/*.so r, + /usr/lib/gconv/gconv-modules* r, + + # used by glibc when binding to ephemeral ports + /etc/bindresvport.blacklist r, + + # ld.so.cache and ld are used to load shared libraries; they are best + # available everywhere + /etc/ld.so.cache r, + # 'px' requires a profile to be available for the transition to + # function; without a loaded profile, the kernel will fail the exec. + /lib/ld-*.so px, + /lib64/ld-*.so px, + /opt/*-linux-uclibc/lib/ld-uClibc*so* px, + + # we might as well allow everything to use common libraries + /lib/lib*.so* r, + /lib/tls/lib*.so* r, + /lib/power4/lib*.so* r, + /lib/power5/lib*.so* r, + /lib/power5+/lib*.so* r, + /lib64/power4/lib*.so* r, + /lib64/power5/lib*.so* r, + /lib64/power5+/lib*.so* r, + /usr/lib/*.so* r, + /usr/lib/tls/lib*.so* r, + /usr/lib/power4/lib*.so* r, + /usr/lib/power5/lib*.so* r, + /usr/lib/power5+/lib*.so* r, + /lib64/lib*.so* r, + /lib64/tls/lib*.so* r, + /usr/lib64/*.so* r, + /usr/lib64/tls/lib*.so* r, + + # /dev/null is pretty harmless and frequently used + /dev/null rw, + # as is /dev/zero + /dev/zero rw, + + # Sometimes used to determine kernel/user interfaces to use + /proc/sys/kernel/version r, + # Depending on which glibc routine uses this file, base may not be the + # best place -- but many profiles require it, and it is quite harmless. + /proc/sys/kernel/ngroups_max r, + + # glibc's sysconf(3) routine to determine free memory, etc + /proc/meminfo r, + /proc/stat r, + /proc/cpuinfo r, diff --git a/parser/tst/errors/includes/busted b/parser/tst/errors/includes/busted new file mode 100644 index 000000000..e7186009d --- /dev/null +++ b/parser/tst/errors/includes/busted @@ -0,0 +1,83 @@ +# $Id$ +# ------------------------------------------------------------------ +# +# Copyright (C) 2002-2005 Novell/SUSE +# +# 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. +# +# ------------------------------------------------------------------ + + + + # (Note that the ldd profile has inlined this file; if you make + # modifications here, please consider including them in the ldd + # profile as well.) + + # The __canary_death_handler function writes a time-stamped log + # message to /dev/log for logging by syslogd. So, /dev/log, timezones, + # and localisations of date should be available EVERYWHERE, so + # StackGuard, FormatGuard, etc., alerts can be properly logged. + /dev/log w, + /dev/urandom r, + /etc/locale/** r, + /etc/localtime r, + /usr/share/locale/** r, + /usr/share/zoneinfo/** r, + + /usr/lib64/locale/** r, + /usr/lib64/gconv/*.so r, + /usr/lib64/gconv/gconv-modules* r, + /usr/lib/locale/** r, + /usr/lib/gconv/*.so r, + /usr/lib/gconv/gconv-modules* r, + + # used by glibc when binding to ephemeral ports + /etc/bindresvport.blacklist r, + + # ld.so.cache and ld are used to load shared libraries; they are best + # available everywhere + /etc/ld.so.cache r, + # 'px' requires a profile to be available for the transition to + # function; without a loaded profile, the kernel will fail the exec. + /lib/ld-*.so px, + /lib64/ld-*.so px, + /opt/*-linux-uclibc/lib/ld-uClibc*so* px, + + # we might as well allow everything to use common libraries + /lib/lib*.so* r, + /lib/tls/lib*.so* r, + /lib/power4/lib*.so* r, + /lib/power5/lib*.so* r, + /lib/power5+/lib*.so* r, + /lib64/power4/lib*.so* r, + /lib64/power5/lib*.so* r, + /lib64/power5+/lib*.so* r, + /usr/lib/*.so* r, + /usr/lib/tls/lib*.so* r, + /usr/lib/power4/lib*.so* r, + /usr/lib/power5/lib*.so* r, + /usr/lib/power5+/lib*.so* r, + /lib64/lib*.so* r, + /lib64/tls/lib*.so* r, + /usr/lib64/*.so* r, + /usr/lib64/tls/lib*.so* r, + + #include + + # /dev/null is pretty harmless and frequently used + /dev/null rw, + # as is /dev/zero + /dev/zero rw, + + # Sometimes used to determine kernel/user interfaces to use + /proc/sys/kernel/version r, + # Depending on which glibc routine uses this file, base may not be the + # best place -- but many profiles require it, and it is quite harmless. + /proc/sys/kernel/ngroups_max r, + + # glibc's sysconf(3) routine to determine free memory, etc + /proc/meminfo r, + /proc/stat r, + /proc/cpuinfo r, diff --git a/parser/tst/errors/okay.sd b/parser/tst/errors/okay.sd new file mode 100644 index 000000000..de4eb7612 --- /dev/null +++ b/parser/tst/errors/okay.sd @@ -0,0 +1,4 @@ +# +/does/not/exist { + #include +} diff --git a/parser/tst/errors/single.sd b/parser/tst/errors/single.sd new file mode 100644 index 000000000..e782957c9 --- /dev/null +++ b/parser/tst/errors/single.sd @@ -0,0 +1,7 @@ +# +# +#include +# +/does/not/exist { + #include +}