=== added file '.bzrignore' --- .bzrignore 1970-01-01 00:00:00 +0000 +++ .bzrignore 2010-08-03 17:27:13 +0000 @@ -0,0 +1,165 @@ +parser/po/*.mo +parser/af_names.h +parser/cap_names.h +parser/tst_misc +parser/tst_regex +parser/tst_symtab +parser/tst_variable +parser/parser_lex.c +parser/parser_version.h +parser/parser_yacc.c +parser/parser_yacc.h +parser/pod2htm*.tmp +parser/*.7 +parser/*.5 +parser/*.8 +parser/*.7.html +parser/*.5.html +parser/*.8.html +parser/common +parser/apparmor_parser +parser/libapparmor_re/regexp.cc +parser/techdoc.aux +parser/techdoc.log +parser/techdoc.pdf +parser/techdoc.toc +libraries/libapparmor/Makefile +libraries/libapparmor/Makefile.in +libraries/libapparmor/aclocal.m4 +libraries/libapparmor/audit.log +libraries/libapparmor/autom4te.cache +libraries/libapparmor/compile +libraries/libapparmor/config.guess +libraries/libapparmor/config.log +libraries/libapparmor/config.status +libraries/libapparmor/config.sub +libraries/libapparmor/configure +libraries/libapparmor/depcomp +libraries/libapparmor/install-sh +libraries/libapparmor/libtool +libraries/libapparmor/ltmain.sh +libraries/libapparmor/missing +libraries/libapparmor/ylwrap +libraries/libapparmor/doc/Makefile +libraries/libapparmor/doc/Makefile.in +libraries/libapparmor/doc/aa_change_hat.2 +libraries/libapparmor/src/.deps +libraries/libapparmor/src/.libs +libraries/libapparmor/src/Makefile +libraries/libapparmor/src/Makefile.in +libraries/libapparmor/src/af_protos.h +libraries/libapparmor/src/change_hat.lo +libraries/libapparmor/src/grammar.lo +libraries/libapparmor/src/libaalogparse.lo +libraries/libapparmor/src/libimmunix_warning.lo +libraries/libapparmor/src/scanner.lo +libraries/libapparmor/src/libapparmor.la +libraries/libapparmor/src/libimmunix.la +libraries/libapparmor/src/grammar.c +libraries/libapparmor/src/grammar.h +libraries/libapparmor/src/scanner.c +libraries/libapparmor/src/scanner.h +libraries/libapparmor/src/tst_aalogmisc +libraries/libapparmor/swig/Makefile +libraries/libapparmor/swig/Makefile.in +libraries/libapparmor/swig/perl/LibAppArmor.bs +libraries/libapparmor/swig/perl/LibAppArmor.pm +libraries/libapparmor/swig/perl/Makefile +libraries/libapparmor/swig/perl/Makefile.PL +libraries/libapparmor/swig/perl/Makefile.in +libraries/libapparmor/swig/perl/Makefile.perl +libraries/libapparmor/swig/perl/blib +libraries/libapparmor/swig/perl/libapparmor_wrap.c +libraries/libapparmor/swig/perl/pm_to_blib +libraries/libapparmor/swig/python/Makefile +libraries/libapparmor/swig/python/Makefile.in +libraries/libapparmor/swig/python/setup.py +libraries/libapparmor/swig/ruby/Makefile +libraries/libapparmor/swig/ruby/Makefile.in +libraries/libapparmor/testsuite/.deps +libraries/libapparmor/testsuite/.libs +libraries/libapparmor/testsuite/Makefile +libraries/libapparmor/testsuite/Makefile.in +libraries/libapparmor/testsuite/libaalogparse.log +libraries/libapparmor/testsuite/libaalogparse.sum +libraries/libapparmor/testsuite/site.exp +libraries/libapparmor/testsuite/test_multi.multi +libraries/libapparmor/testsuite/config/Makefile +libraries/libapparmor/testsuite/config/Makefile.in +libraries/libapparmor/testsuite/lib/Makefile +libraries/libapparmor/testsuite/lib/Makefile.in +libraries/libapparmor/testsuite/libaalogparse.test/Makefile +libraries/libapparmor/testsuite/libaalogparse.test/Makefile.in +libraries/libapparmor/testsuite/test_multi/out +changehat/mod_apparmor/.libs +changehat/mod_apparmor/common +changehat/pam_apparmor/common +changehat/tomcat_apparmor/common +utils/common +utils/*.8 +utils/*.8.html +utils/*.5 +utils/*.5.html +utils/*.tmp +utils/po/*.mo +tests/regression/apparmor/access +tests/regression/apparmor/changehat +tests/regression/apparmor/changehat_fail +tests/regression/apparmor/changehat_fork +tests/regression/apparmor/changehat_misc +tests/regression/apparmor/changehat_misc2 +tests/regression/apparmor/changehat_pthread +tests/regression/apparmor/changehat_twice +tests/regression/apparmor/changehat_wrapper +tests/regression/apparmor/changeprofile +tests/regression/apparmor/chdir +tests/regression/apparmor/chgrp +tests/regression/apparmor/chmod +tests/regression/apparmor/chown +tests/regression/apparmor/clone +tests/regression/apparmor/deleted +tests/regression/apparmor/env_check +tests/regression/apparmor/environ +tests/regression/apparmor/exec +tests/regression/apparmor/exec_qual +tests/regression/apparmor/exec_qual2 +tests/regression/apparmor/fchdir +tests/regression/apparmor/fchgrp +tests/regression/apparmor/fchmod +tests/regression/apparmor/fchown +tests/regression/apparmor/fork +tests/regression/apparmor/link +tests/regression/apparmor/link_subset +tests/regression/apparmor/mkdir +tests/regression/apparmor/mmap +tests/regression/apparmor/mount +tests/regression/apparmor/named_pipe +tests/regression/apparmor/net_raw +tests/regression/apparmor/open +tests/regression/apparmor/openat +tests/regression/apparmor/pipe +tests/regression/apparmor/ptrace +tests/regression/apparmor/ptrace_helper +tests/regression/apparmor/pwrite +tests/regression/apparmor/readdir +tests/regression/apparmor/rename +tests/regression/apparmor/rw +tests/regression/apparmor/swap +tests/regression/apparmor/symlink +tests/regression/apparmor/syscall_chroot +tests/regression/apparmor/syscall_mknod +tests/regression/apparmor/syscall_mlockall +tests/regression/apparmor/syscall_ptrace +tests/regression/apparmor/syscall_reboot +tests/regression/apparmor/syscall_setdomainname +tests/regression/apparmor/syscall_sethostname +tests/regression/apparmor/syscall_setpriority +tests/regression/apparmor/syscall_setscheduler +tests/regression/apparmor/syscall_sysctl +tests/regression/apparmor/sysctl_proc +tests/regression/apparmor/tcp +tests/regression/apparmor/unix_fd_client +tests/regression/apparmor/unix_fd_server +tests/regression/apparmor/unlink +tests/regression/apparmor/xattrs +tests/regression/apparmor/coredump === added file 'README' --- README 1970-01-01 00:00:00 +0000 +++ README 2010-08-03 17:27:13 +0000 @@ -0,0 +1,155 @@ +------------ +Introduction +------------ +AppArmor protects systems from insecure or untrusted processes by +running them in restricted confinement, while still allowing processes +to share files, exercise privilege and communicate with other processes. +AppArmor is a Mandatory Access Control (MAC) mechanism which uses the +Linux Security Module (LSM) framework. The confinement's restrictions +are mandatory and are not bound to identity, group membership, or object +ownership. The protections provided are in addition to the kernel's +regular access control mechanisms (including DAC) and can be used to +restrict the superuser. + +The AppArmor kernel module and accompanying user-space tools are +available under the GPL license (the exception is the libapparmor +library, available under the LGPL license, which allows change_hat(2) +and change_profile(2) to be used by non-GPL binaries). + +For more information, you can read the techdoc.pdf (available after +building the parser) and http://apparmor.wiki.kernel.org. + + +------------- +Source Layout +------------- + +AppArmor consists of several different parts: + +changehat/ source for using changehat with Apache, PAM and Tomcat +common/ common makefile rules +desktop/ empty +kernel-patches/ patches for various kernel versions +libraries/ libapparmor source and language bindings +parser/ source for parser/loader and corresponding documentation +profiles/ configuration files, reference profiles and abstractions +tests/ regression and stress testsuites +utils/ high-level utilities for working with AppArmor + + +------------------------------------------ +Building and Installing AppArmor Userspace +------------------------------------------ + +To build and install AppArmor userspace on your system, build and install in +the following order. + + +libapparmor: +$ cd ./libraries/libapparmor +$ sh ./autogen.sh +$ sh ./configure --prefix=/usr --with-perl +$ make +$ make check + + +Utilities: +$ cd utils +$ make +$ make install + + +parser: +$ cd parser +$ make +$ make tests # not strictly necessary as they are run during the + # build by default +$ make install + + +Apache mod_apparmor: +$ cd changehat/mod_apparmor +$ LIBS="-lapparmor" make +$ make install + + +PAM AppArmor: +$ cd changehat/pam_apparmor +$ LIBS="-lapparmor -lpam" make +$ make install + + +Profiles: +$ cd profiles +$ make +$ make install + + + +------------------- +AppArmor Testsuites +------------------- + +A number of testsuites are in the AppArmor sources. Most have documentation on +usage and how to update and add tests. Below is a quick overview of their +location and how to run them. + + +Regression tests +---------------- +For details on structure and adding tests, see +tests/regression/apparmor/README. + +To run: +$ cd tests/regression/apparmor (requires root) +$ make +$ sudo make tests +$ sudo bash open.sh -r # runs and saves the last testcase from open.sh + + +Parser tests +------------ +For details on structure and adding tests, see parser/tst/README. + +To run: +$ cd parser/tst +$ make +$ make tests + + +Libapparmor +----------- +For details on structure and adding tests, see libraries/libapparmor/README. +$ cd libraries/libapparmor +$ make check + + +Stress Tests +------------ +To run AppArmor stress tests: +$ make all + +Use these: +$ ./change_hat +$ ./child +$ ./kill.sh +$ ./open +$ ./s.sh + +Or run all at once: +$ ./stress.sh + +Please note that the above will stress the system so much it may end up +invoking the OOM killer. + +To run parser stress tests (requires /usr/bin/ruby): +$ ./stress.sh + +(see stress.sh -h for options) + +----------------------------------------------- +Building and Installing AppArmor Kernel Patches +----------------------------------------------- + +TODO + === modified file 'common/Make.rules' --- common/Make.rules 2010-03-11 07:07:29 +0000 +++ common/Make.rules 2010-08-03 17:27:13 +0000 @@ -48,7 +48,7 @@ echo "/tmp/${NAME}" ; \ fi ;) endif -RPMHOSTVENDOR=$(shell rpm --eval "%{_host_vendor}") +RPMHOSTVENDOR=$(shell which rpm && rpm --eval "%{_host_vendor}") ifndef DISTRO DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \ echo slackware ; \ @@ -92,30 +92,16 @@ ifndef SPECFILE SPECFILE = $(NAME).spec endif -RELEASE = $(shell rpm -q --specfile --define "_sourcedir ." ${RPMARG} --qf "%{RELEASE}" ${SPECFILE}) +RELEASE = $(shell lsb_release -is) $(shell lsb_release -rs) RELEASE_DIR = $(NAME)-$(VERSION) TARBALL = $(NAME)-$(VERSION)-${REPO_VERSION}.tar.gz -TAR = /bin/tar czvp -h --exclude .svn --exclude CVS --exclude .cvsignore --exclude ${TARBALL} --exclude ${RELEASE_DIR}/${RELEASE_DIR} $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude") +TAR = /bin/tar czvp -h --exclude .svn --exclude .bzr --exclude .bzrignore --exclude ${TARBALL} --exclude ${RELEASE_DIR}/${RELEASE_DIR} $(shell test -f ${NAME}.exclude && echo "-X ${NAME}.exclude") LDCONFIG = /sbin/ldconfig -CVSPKG_VERSION=$(shell rpm -q --specfile --define "_sourcedir ." ${RPMARG} ${SPECFILE} | head -1 | tr "." "_") - RPMSUBDIRS=SOURCES SPECS BUILD BUILDROOT SRPMS RPMS/i386 RPMS/i586 \ RPMS/i686 RPMS/athlon RPMS/noarch RPMS/x86_64 BUILDRPMSUBDIRS=$(foreach subdir, $(RPMSUBDIRS), $(BUILDDIR:/=)/$(subdir)) -.PHONY: cvs_tag -cvs_tag: - cvs tag IMMUNIX-${CVSPKG_VERSION} - -.PHONY: checkin -checkin: - if cvs -q up -d | grep -q "^\?" ; then echo "Hey! You have" \ - "files in the directory you have not added into cvs."; exit 1; \ - fi - cvs ci - make cvs_tag - ifdef EXTERNAL_PACKAGE .PHONY: rpm rpm: clean $(BUILDRPMSUBDIRS) === modified file 'libraries/libapparmor/src/aalogparse.h' --- libraries/libapparmor/src/aalogparse.h 2009-09-18 21:13:04 +0000 +++ libraries/libapparmor/src/aalogparse.h 2010-08-03 17:27:13 +0000 @@ -129,6 +129,7 @@ unsigned long fsuid; /* fsuid of task - if logged */ unsigned long ouid; /* ouid of task - if logged */ char *profile; /* The name of the profile */ + char *comm; /* Command that triggered msg */ char *name; char *name2; char *namespace; === modified file 'libraries/libapparmor/src/change_hat.c' --- libraries/libapparmor/src/change_hat.c 2010-02-11 23:38:24 +0000 +++ libraries/libapparmor/src/change_hat.c 2010-08-03 17:27:13 +0000 @@ -194,7 +194,7 @@ /* setup command string which is of the form * changehat ^hat1\0hat2\0hat3\0..\0 */ - sprintf(buf, "%s %016x^", cmd, token); + sprintf(buf, "%s %016lx^", cmd, token); pos = buf + strlen(buf); if (subprofiles) { for (hats = subprofiles; *hats; hats++) { === modified file 'libraries/libapparmor/src/grammar.y' --- libraries/libapparmor/src/grammar.y 2009-09-18 21:13:04 +0000 +++ libraries/libapparmor/src/grammar.y 2010-08-03 17:27:13 +0000 @@ -1,6 +1,7 @@ /* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) + * Copyright (c) 2010, Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -96,6 +97,13 @@ %token TOK_TYPE_HINT %token TOK_TYPE_STATUS %token TOK_TYPE_ERROR +%token TOK_TYPE_AA_REJECT +%token TOK_TYPE_AA_AUDIT +%token TOK_TYPE_AA_COMPLAIN +%token TOK_TYPE_AA_HINT +%token TOK_TYPE_AA_STATUS +%token TOK_TYPE_AA_ERROR +%token TOK_TYPE_LSM_AVC %token TOK_OLD_TYPE_APPARMOR %token TOK_OLD_APPARMOR_REJECT %token TOK_OLD_APPARMOR_PERMIT @@ -123,6 +131,7 @@ %token TOK_OLD_FORK %token TOK_OLD_CHILD +%token TOK_KEY_APPARMOR %token TOK_KEY_TYPE %token TOK_KEY_MSG %token TOK_KEY_OPERATION @@ -146,6 +155,7 @@ %token TOK_KEY_ERROR %token TOK_KEY_FSUID %token TOK_KEY_OUID +%token TOK_KEY_COMM %token TOK_SYSLOG_KERNEL @@ -168,13 +178,14 @@ ; new_syntax: - TOK_TYPE_REJECT audit_msg key_list { ret_record->event = AA_RECORD_DENIED; } - | TOK_TYPE_AUDIT audit_msg key_list { ret_record->event = AA_RECORD_AUDIT; } - | TOK_TYPE_COMPLAIN audit_msg key_list { ret_record->event = AA_RECORD_ALLOWED; } - | TOK_TYPE_HINT audit_msg key_list { ret_record->event = AA_RECORD_HINT; } - | TOK_TYPE_STATUS audit_msg key_list { ret_record->event = AA_RECORD_STATUS; } - | TOK_TYPE_ERROR audit_msg key_list { ret_record->event = AA_RECORD_ERROR; } + TOK_TYPE_AA_REJECT audit_msg key_list { ret_record->event = AA_RECORD_DENIED; } + | TOK_TYPE_AA_AUDIT audit_msg key_list { ret_record->event = AA_RECORD_AUDIT; } + | TOK_TYPE_AA_COMPLAIN audit_msg key_list { ret_record->event = AA_RECORD_ALLOWED; } + | TOK_TYPE_AA_HINT audit_msg key_list { ret_record->event = AA_RECORD_HINT; } + | TOK_TYPE_AA_STATUS audit_msg key_list { ret_record->event = AA_RECORD_STATUS; } + | TOK_TYPE_AA_ERROR audit_msg key_list { ret_record->event = AA_RECORD_ERROR; } | TOK_TYPE_UNKNOWN audit_msg key_list { ret_record->event = lookup_aa_event($1); } + | TOK_TYPE_LSM_AVC audit_msg key_list ; other_audit: TOK_TYPE_OTHER audit_msg TOK_MSG_REST @@ -366,7 +377,8 @@ audit_id: TOK_AUDIT TOK_OPEN_PAREN TOK_AUDIT_DIGITS TOK_PERIOD TOK_AUDIT_DIGITS TOK_COLON TOK_AUDIT_DIGITS TOK_CLOSE_PAREN TOK_COLON { - asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7); + if (!asprintf(&ret_record->audit_id, "%s.%s:%s", $3, $5, $7)) + yyerror(scanner, YY_("Out of memory")); ret_record->epoch = atol($3); ret_record->audit_sub_id = atoi($7); free($3); @@ -420,6 +432,18 @@ { ret_record->fsuid = $3;} | TOK_KEY_OUID TOK_EQUALS TOK_DIGITS { ret_record->ouid = $3;} + | TOK_KEY_COMM TOK_EQUALS TOK_QUOTED_STRING + { ret_record->comm = $3;} + | TOK_KEY_APPARMOR TOK_EQUALS apparmor_event + ; + +apparmor_event: + TOK_TYPE_REJECT { ret_record->event = AA_RECORD_DENIED; } + | TOK_TYPE_AUDIT { ret_record->event = AA_RECORD_AUDIT; } + | TOK_TYPE_COMPLAIN { ret_record->event = AA_RECORD_ALLOWED; } + | TOK_TYPE_HINT { ret_record->event = AA_RECORD_HINT; } + | TOK_TYPE_STATUS { ret_record->event = AA_RECORD_STATUS; } + | TOK_TYPE_ERROR { ret_record->event = AA_RECORD_ERROR; } ; key_pid: TOK_KEY_PID TOK_EQUALS TOK_DIGITS { ret_record->pid = $3; } === modified file 'libraries/libapparmor/src/libaalogparse.c' --- libraries/libapparmor/src/libaalogparse.c 2009-09-18 21:13:04 +0000 +++ libraries/libapparmor/src/libaalogparse.c 2010-08-03 17:27:13 +0000 @@ -56,6 +56,8 @@ free(record->denied_mask); if (record->profile != NULL) free(record->profile); + if (record->comm != NULL) + free(record->comm); if (record->name != NULL) free(record->name); if (record->name2 != NULL) @@ -151,7 +153,8 @@ if (current->protocol_name) { ret = strdup(current->protocol_name); } else { - asprintf(&ret, "unknown(%u)", proto); + if (!asprintf(&ret, "unknown(%u)", proto)) + ret = NULL; } return ret; === modified file 'libraries/libapparmor/src/scanner.l' --- libraries/libapparmor/src/scanner.l 2010-02-10 23:13:55 +0000 +++ libraries/libapparmor/src/scanner.l 2010-08-03 17:27:13 +0000 @@ -1,6 +1,7 @@ /* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) + * Copyright (c) 2010, Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -30,6 +31,8 @@ #include +#define YY_NO_INPUT + unsigned int string_buf_alloc = 0; unsigned int string_buf_len = 0; char *string_buf = NULL; @@ -84,12 +87,19 @@ modes ({mode_chars}+)|({mode_chars}+::{mode_chars}*)|(::{mode_chars}*) /* New message types */ -reject_type "APPARMOR_DENIED" -audit_type "APPARMOR_AUDIT" -complain_type "APPARMOR_ALLOWED" -hint_type "APPARMOR_HINT" -status_type "APPARMOR_STATUS" -error_type "APPARMOR_ERROR" +aa_reject_type "APPARMOR_DENIED" +aa_audit_type "APPARMOR_AUDIT" +aa_complain_type "APPARMOR_ALLOWED" +aa_hint_type "APPARMOR_HINT" +aa_status_type "APPARMOR_STATUS" +aa_error_type "APPARMOR_ERROR" +reject_type "\"DENIED\"" +audit_type "\"AUDIT\"" +complain_type "\"ALLOWED\"" +hint_type "\"HINT\"" +status_type "\"STATUS\"" +error_type "\"ERROR\"" +lsm_avc_type "AVC" unknown_type UNKNOWN\[{digits}+\] other_audit_type [[:alnum:]\[\]_-]+ @@ -125,6 +135,7 @@ /* Key tokens */ +key_apparmor "apparmor" key_type "type" key_msg "msg" key_operation "operation" @@ -147,6 +158,7 @@ key_error "error" key_fsuid "fsuid" key_ouid "ouid" +key_comm "comm" audit "audit" /* syslog tokens */ @@ -240,6 +252,13 @@ {hint_type} { BEGIN(INITIAL); return(TOK_TYPE_HINT); } {status_type} { BEGIN(INITIAL); return(TOK_TYPE_STATUS); } {error_type} { BEGIN(INITIAL); return(TOK_TYPE_ERROR); } + {aa_reject_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_REJECT); } + {aa_audit_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_AUDIT); } + {aa_complain_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_COMPLAIN); } + {aa_hint_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_HINT); } + {aa_status_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_STATUS); } + {aa_error_type} { BEGIN(INITIAL); return(TOK_TYPE_AA_ERROR); } + {lsm_avc_type} { BEGIN(INITIAL); return(TOK_TYPE_LSM_AVC); } {unknown_type} { char *yptr = yytext; while (*yptr && *yptr != '[') yptr++; @@ -300,6 +319,7 @@ {key_attribute} { BEGIN(sub_id); return(TOK_KEY_ATTRIBUTE); } } +{key_apparmor} { BEGIN(audit_types); return(TOK_KEY_APPARMOR); } {key_type} { BEGIN(audit_types); return(TOK_KEY_TYPE); } {key_msg} { return(TOK_KEY_MSG); } {key_operation} { return(TOK_KEY_OPERATION); } @@ -321,6 +341,7 @@ {key_error} { return(TOK_KEY_ERROR); } {key_fsuid} { return(TOK_KEY_FSUID); } {key_ouid} { return(TOK_KEY_OUID); } +{key_comm} { return(TOK_KEY_COMM); } {syslog_kernel} { BEGIN(dmesg_timestamp); return(TOK_SYSLOG_KERNEL); } {syslog_month} { yylval->t_str = strdup(yytext); return(TOK_DATE_MONTH); } === modified file 'libraries/libapparmor/swig/perl/Makefile.am' --- libraries/libapparmor/swig/perl/Makefile.am 2009-05-12 21:56:56 +0000 +++ libraries/libapparmor/swig/perl/Makefile.am 2010-05-31 18:58:40 +0000 @@ -1,7 +1,8 @@ EXTRA_DIST =Makefile.PL libapparmor_wrap.c LibAppArmor.pm examples/*.pl + +if HAVE_PERL noinst_DATA =LibAppArmor.so -if HAVE_PERL libapparmor_wrap.c: $(srcdir)/../SWIG/libapparmor.i $(SWIG) -perl -I$(srcdir)/../../src -module LibAppArmor -o $@ $(srcdir)/../SWIG/libapparmor.i @@ -27,4 +28,4 @@ #rm -f Makefile.perl Makefile.perl.old rm -f *.so # *.o -endif \ No newline at end of file +endif === modified file 'libraries/libapparmor/testsuite/test_multi.c' --- libraries/libapparmor/testsuite/test_multi.c 2009-09-18 21:13:04 +0000 +++ libraries/libapparmor/testsuite/test_multi.c 2010-08-03 17:27:13 +0000 @@ -129,6 +129,10 @@ { printf("Name: %s\n", record->name); } + if (record->comm != NULL) + { + printf("Command: %s\n", record->comm); + } if (record->name2 != NULL) { printf("Name2: %s\n", record->name2); === added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_01.in' --- libraries/libapparmor/testsuite/test_multi/avc_audit_01.in 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_audit_01.in 2010-08-03 17:27:13 +0000 @@ -0,0 +1,1 @@ +type=AVC msg=audit(1279948288.415:39): apparmor="DENIED" operation="open" parent=12332 profile="/usr/sbin/cupsd" name="/home/user/.ssh/" pid=12333 comm="ls" requested_mask="r" denied_mask="r" fsuid=0 ouid=1000 === added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_01.out' --- libraries/libapparmor/testsuite/test_multi/avc_audit_01.out 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_audit_01.out 2010-08-03 17:27:13 +0000 @@ -0,0 +1,16 @@ +START +File: test_multi/avc_audit_01.in +Event type: AA_RECORD_DENIED +Audit ID: 1279948288.415:39 +Operation: open +Mask: r +Denied Mask: r +fsuid: 0 +ouid: 1000 +Profile: /usr/sbin/cupsd +Name: /home/user/.ssh/ +Command: ls +Parent: 12332 +PID: 12333 +Epoch: 1279948288 +Audit subid: 39 === added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_02.in' --- libraries/libapparmor/testsuite/test_multi/avc_audit_02.in 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_audit_02.in 2010-08-03 17:27:13 +0000 @@ -0,0 +1,1 @@ +type=AVC msg=audit(1279948227.175:27): apparmor="STATUS" operation="profile_replace" name="/sbin/dhclient3" pid=12291 comm="apparmor_parser" === added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_02.out' --- libraries/libapparmor/testsuite/test_multi/avc_audit_02.out 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_audit_02.out 2010-08-03 17:27:13 +0000 @@ -0,0 +1,10 @@ +START +File: test_multi/avc_audit_02.in +Event type: AA_RECORD_STATUS +Audit ID: 1279948227.175:27 +Operation: profile_replace +Name: /sbin/dhclient3 +Command: apparmor_parser +PID: 12291 +Epoch: 1279948227 +Audit subid: 27 === added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_03.in' --- libraries/libapparmor/testsuite/test_multi/avc_audit_03.in 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_audit_03.in 2010-08-03 17:27:13 +0000 @@ -0,0 +1,1 @@ +type=AVC msg=audit(1279968846.035:77): apparmor="ALLOWED" operation="open" parent=7014 profile="/tmp/cat" name="/etc/passwd" pid=21645 comm="cat" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0 === added file 'libraries/libapparmor/testsuite/test_multi/avc_audit_03.out' --- libraries/libapparmor/testsuite/test_multi/avc_audit_03.out 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_audit_03.out 2010-08-03 17:27:13 +0000 @@ -0,0 +1,16 @@ +START +File: test_multi/avc_audit_03.in +Event type: AA_RECORD_ALLOWED +Audit ID: 1279968846.035:77 +Operation: open +Mask: r +Denied Mask: r +fsuid: 1000 +ouid: 0 +Profile: /tmp/cat +Name: /etc/passwd +Command: cat +Parent: 7014 +PID: 21645 +Epoch: 1279968846 +Audit subid: 77 === added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_01.in' --- libraries/libapparmor/testsuite/test_multi/avc_syslog_01.in 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_syslog_01.in 2010-08-03 17:27:13 +0000 @@ -0,0 +1,1 @@ +Jul 24 12:25:33 spriggan kernel: [42416.178567] type=1400 audit(1279967133.365:54): apparmor="DENIED" operation="open" parent=19650 profile="/usr/sbin/cupsd" name="/boot/" pid=19651 comm="ls" requested_mask="r" denied_mask="r" fsuid=0 ouid=0 === added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_01.out' --- libraries/libapparmor/testsuite/test_multi/avc_syslog_01.out 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_syslog_01.out 2010-08-03 17:27:13 +0000 @@ -0,0 +1,16 @@ +START +File: test_multi/avc_syslog_01.in +Event type: AA_RECORD_DENIED +Audit ID: 1279967133.365:54 +Operation: open +Mask: r +Denied Mask: r +fsuid: 0 +ouid: 0 +Profile: /usr/sbin/cupsd +Name: /boot/ +Command: ls +Parent: 19650 +PID: 19651 +Epoch: 1279967133 +Audit subid: 54 === added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_02.in' --- libraries/libapparmor/testsuite/test_multi/avc_syslog_02.in 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_syslog_02.in 2010-08-03 17:27:13 +0000 @@ -0,0 +1,1 @@ +Jul 24 12:24:41 spriggan kernel: [42364.269117] type=1400 audit(1279967081.455:42): apparmor="STATUS" operation="profile_replace" name="/sbin/dhclient3" pid=19610 comm="apparmor_parser" === added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_02.out' --- libraries/libapparmor/testsuite/test_multi/avc_syslog_02.out 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_syslog_02.out 2010-08-03 17:27:13 +0000 @@ -0,0 +1,10 @@ +START +File: test_multi/avc_syslog_02.in +Event type: AA_RECORD_STATUS +Audit ID: 1279967081.455:42 +Operation: profile_replace +Name: /sbin/dhclient3 +Command: apparmor_parser +PID: 19610 +Epoch: 1279967081 +Audit subid: 42 === added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_03.in' --- libraries/libapparmor/testsuite/test_multi/avc_syslog_03.in 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_syslog_03.in 2010-08-03 17:27:13 +0000 @@ -0,0 +1,1 @@ +Jul 24 12:54:06 spriggan kernel: [44128.842691] type=1400 audit(1279968846.035:77): apparmor="ALLOWED" operation="open" parent=7014 profile="/tmp/cat" name="/etc/passwd" pid=21645 comm="cat" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0 === added file 'libraries/libapparmor/testsuite/test_multi/avc_syslog_03.out' --- libraries/libapparmor/testsuite/test_multi/avc_syslog_03.out 1970-01-01 00:00:00 +0000 +++ libraries/libapparmor/testsuite/test_multi/avc_syslog_03.out 2010-08-03 17:27:13 +0000 @@ -0,0 +1,16 @@ +START +File: test_multi/avc_syslog_03.in +Event type: AA_RECORD_ALLOWED +Audit ID: 1279968846.035:77 +Operation: open +Mask: r +Denied Mask: r +fsuid: 1000 +ouid: 0 +Profile: /tmp/cat +Name: /etc/passwd +Command: cat +Parent: 7014 +PID: 21645 +Epoch: 1279968846 +Audit subid: 77 === modified file 'parser/COPYING.GPL' --- parser/COPYING.GPL 2006-04-11 21:52:54 +0000 +++ parser/COPYING.GPL 2010-08-03 17:27:13 +0000 @@ -1,6 +1,5 @@ This license applies to all source files within the SubDomain parser -package, with the exception of md5.c and the files in the pcre/ -subdirectory, as they are covered under their own respective licenses. +package. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 === modified file 'parser/Makefile' --- parser/Makefile 2009-11-11 18:58:57 +0000 +++ parser/Makefile 2010-08-03 17:27:13 +0000 @@ -82,9 +82,6 @@ AAREDIR= libapparmor_re AAREOBJECTS = ${AAREDIR}/libapparmor_re.a -PCREDIR= pcre -PCREOBJECTS = ${PCREDIR}/pcre.o - LEX_C_FILES = parser_lex.c YACC_C_FILES = parser_yacc.c parser_yacc.h @@ -94,7 +91,7 @@ TEST_OBJECTS = $(filter-out parser_lex.o, \ $(filter-out parser_yacc.o, \ - $(filter-out parser_main.o, ${OBJECTS} ${PCREOBJECTS}))) + $(filter-out parser_main.o, ${OBJECTS}))) ifdef V VERBOSE = 1 @@ -125,14 +122,25 @@ techdoc.txt: techdoc/index.html w3m -dump $< > $@ -all: $(TOOLS) $(MANPAGES) ${HTMLMANPAGES} techdoc.pdf +# targets arranged this way so that people who don't want full docs can +# pick specific targets they want. +main: $(TOOLS) $(Q)make -C po all - $(Q)make -s tests - -apparmor_parser: $(OBJECTS) $(PCREOBJECTS) $(AAREOBJECTS) + +manpages: $(MANPAGES) + +htmlmanpages: $(HTMLMANPAGES) + +pdf: techdoc.pdf + +docs: manpages htmlmanpages pdf + +all: main docs tests + +apparmor_parser: $(OBJECTS) $(AAREOBJECTS) rm -f ./libstdc++.a ln -s `g++ -print-file-name=libstdc++.a` - g++ $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(PCREOBJECTS) $(LIBS) \ + g++ $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \ ${LEXLIB} $(AAREOBJECTS) -static-libgcc -L. parser_yacc.c parser_yacc.h: parser_yacc.y parser.h @@ -191,7 +199,7 @@ af_names.h: /usr/include/bits/socket.h LC_ALL=C sed -n -e '/$(__FILTER)/d' -e "s/^\#define[ \\t]\\+PF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/#ifndef AF_\\1\\n# define AF_\\1 \\2\\n#endif\\nAA_GEN_NET_ENT(\"\\L\\1\", \\UAF_\\1)\\n/p" $< > $@ LC_ALL=C sed -n -e "s/^\#define[ \\t]\\+PF_MAX[ \\t]\\+\\([0-9]\\+\\)[ \\t]\\+.*/#define AA_AF_MAX \\1\n/p" $< >> $@ - cat $@ + # cat $@ cap_names.h: /usr/include/linux/capability.h LC_ALL=C sed -n -e "/CAP_EMPTY_SET/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9xa-f]\\+\\)\\(.*\\)\$$/\{\"\\L\\1\", \\UCAP_\\1\},/p" $< > $@ @@ -205,8 +213,8 @@ tst_misc: parser_misc.c parser.h parser_yacc.h af_names.h cap_names.h $(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT) -tst_regex: parser_regex.c parser.h parser_yacc.h $(PCREOBJECTS) - $(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(PCREOBJECTS) $(BUILD_OUTPUT) +tst_regex: parser_regex.c parser.h parser_yacc.h + $(Q)$(CC) -DUNIT_TEST $(EXTRA_CFLAGS) $(TEST_FLAGS) -o $@ $< $(BUILD_OUTPUT) .SILENT: check .PHONY: check @@ -214,7 +222,7 @@ .SILENT: tests tests: ${TESTS} - for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test} $(BUILD_OUTPUT) ; done + sh -e -c 'for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test} $(BUILD_OUTPUT) ; done' $(Q)make -s -C tst tests .SILENT: check @@ -226,11 +234,6 @@ $(AAREOBJECTS): make -C $(AAREDIR) -.SILENT: $(PCREOBJECTS) -.PHONY: $(PCREOBJECTS) -$(PCREOBJECTS): - make -C $(PCREDIR) "CFLAGS=$(EXTRA_CFLAGS)" - .PHONY: install-rhel4 install-rhel4: install-redhat @@ -293,7 +296,6 @@ rm -f af_names.h rm -f cap_names.h -rm -rf techdoc.{aux,log,pdf,toc,txt} techdoc/ - make -s -C $(PCREDIR) clean make -s -C $(AAREDIR) clean make -s -C po clean === modified file 'parser/README' --- parser/README 2007-04-25 20:50:21 +0000 +++ parser/README 2010-08-03 17:27:13 +0000 @@ -11,11 +11,6 @@ where we will attempt to conform to the RFP vulnerability disclosure protocol: http://www.wiretrip.net/rfp/policy.html -The parser uses the PCRE (Perl Compatible Regular Expression) engine, -which was written by Philip Hazel and is copyright by the University -of Cambridge, England. For more information on the PCRE engine, see - - Thanks. -- The AppArmor development team === modified file 'parser/apparmor_parser.pod' --- parser/apparmor_parser.pod 2010-01-07 18:03:49 +0000 +++ parser/apparmor_parser.pod 2010-08-03 17:27:13 +0000 @@ -80,6 +80,10 @@ Writes a binary (cached) profile to stdout (implies -K and -T). +=item -o file, --ofile file + +Writes a binary (cached) profile to the specified file (implies -K and -T) + =item -b n, --base n Set the base directory for resolving #include directives @@ -149,11 +153,43 @@ 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. Given twice, dumps its interpretation of the profile for checking. +=item -D n, --dump=n + +Debug flag for dumping various structures and passes of policy compilation. +A single dump flag can be specified per --dump option, but the dump flag +can be passed multiple times. Note progress flags tend to also imply +the matching stats flag. + + apparmor_parser --dump=dfa-stats --dump=trans-stats + +Use --help=dump to see a full list of which dump flags are supported + +=item -O n, --optimize=n + +Set the optimization flags used by policy compilation. A sinlge optimization +flag can be toggled per -O option, but the optimize flag can be passed +multiple times. Turning off some phases of the optimization can make +it so that policy can't complete compilation due to size constraints +(it is entirely possible to create a dfa with millions of states that will +take days or longer to compile). + +Note: The parser is set to use a balanced default set of flags, that +will result in resonable compression but not take excessive amounts +of time to complete. + +Use --help=optimize to see a full list of which optimization flags are +supported. + =item -h, --help Give a quick reference guide. === modified file 'parser/libapparmor_re/apparmor_re.h' --- parser/libapparmor_re/apparmor_re.h 2010-01-28 01:20:13 +0000 +++ parser/libapparmor_re/apparmor_re.h 2010-08-03 17:27:13 +0000 @@ -35,6 +35,7 @@ DFA_DUMP_EQUIV_STATS = 1 << 19, DFA_DUMP_MINIMIZE = 1 << 20, DFA_DUMP_UNREACHABLE = 1 << 22, + DFA_DUMP_RULE_EXPR = 1 << 23, } dfaflags_t; #ifdef __cplusplus @@ -48,9 +49,9 @@ aare_ruleset_t *aare_new_ruleset(int reverse); void aare_delete_ruleset(aare_ruleset_t *rules); int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny, - uint32_t perms, uint32_t audit); + uint32_t perms, uint32_t audit, dfaflags_t flags); int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms, - uint32_t audit, int count, char **rulev); + uint32_t audit, int count, char **rulev, dfaflags_t flags); void *aare_create_dfa(aare_ruleset_t *rules, size_t *size, dfaflags_t flags); void aare_reset_matchflags(void); === modified file 'parser/libapparmor_re/regexp.y' --- parser/libapparmor_re/regexp.y 2010-02-01 07:21:00 +0000 +++ parser/libapparmor_re/regexp.y 2010-08-03 17:27:13 +0000 @@ -102,6 +102,7 @@ /* child 0 is left, child 1 is right */ Node *child[2]; + unsigned int label; /* unique number for debug etc */ /** * We need reference counting for AcceptNodes: sharing AcceptNodes * avoids introducing duplicate States with identical accept values. @@ -1208,12 +1209,11 @@ * Assign a consecutive number to each node. This is only needed for * pretty-printing the debug output. */ -map node_label; void label_nodes(Node *root) { int nodes = 0; for (depth_first_traversal i(root); i; i++) - node_label.insert(make_pair(*i, nodes++)); + i->label = nodes++; } /** @@ -1225,7 +1225,7 @@ if (!state.empty()) { State::iterator i = state.begin(); for(;;) { - os << node_label[*i]; + os << (*i)->label; if (++i == state.end()) break; os << ','; @@ -1240,15 +1240,15 @@ */ void dump_syntax_tree(ostream& os, Node *node) { for (depth_first_traversal i(node); i; i++) { - os << node_label[*i] << '\t'; + os << i->label << '\t'; if ((*i)->child[0] == 0) os << **i << '\t' << (*i)->followpos << endl; else { if ((*i)->child[1] == 0) - os << node_label[(*i)->child[0]] << **i; + os << (*i)->child[0]->label << **i; else - os << node_label[(*i)->child[0]] << **i - << node_label[(*i)->child[1]]; + os << (*i)->child[0]->label << **i + << (*i)->child[1]->label; os << '\t' << (*i)->firstpos << (*i)->lastpos << endl; } @@ -1256,22 +1256,17 @@ os << endl; } -/* Comparison operator for sets of . */ -template -class deref_less_than { -public: - deref_less_than() { } - bool operator()(T a, T b) - { - return *a < *b; - } +/* Comparison operator for sets of . + * Do compare pointer comparisom on set of , the pointer comparison + * allows us to determine which Sets of we have seen already from + * new ones when constructing the DFA. + */ +struct deref_less_than { + bool operator()(State * const & lhs, State * const & rhs) const + { return *lhs < *rhs; } }; -/** - * States in the DFA. The pointer comparison allows us to tell sets we - * have seen already from new ones when constructing the DFA. - */ -typedef set > States; +typedef set States; typedef list Partition; /* Transitions in the DFA. */ typedef map Trans; @@ -1370,6 +1365,13 @@ here.cases.insert(*j); } } + + for (depth_first_traversal i(root); i; i++) { + (*i)->firstpos.clear(); + (*i)->lastpos.clear(); + (*i)->followpos.clear(); + } + if (flags & (DFA_DUMP_STATS)) fprintf(stderr, "\033[2KCreated dfa: states %ld\tmatching %d\tnonmatching %d\n", states.size(), match_count, nomatch_count); @@ -1715,7 +1717,9 @@ Trans::iterator j = trans.find(*i); if (j != trans.end()) trans.erase(j); + State *s = *i; states.erase(*i); + delete(s); } } @@ -2097,24 +2101,11 @@ /* if it overflows the next_check array it fits in as we will * resize */ if (c >= next_check.size()) - goto resize; + return true; if (next_check[c].second) return false; } - return true; -resize: - next_check.resize(base + cases.cases.rbegin()->first + 1); - size_t prev = pos; - size_t x = pos; - /* find last free list entry */ - while (x) { - prev = x; - x = free_list[x].second; - } - x = free_list. size(); - free_list.resize(base + cases.cases.rbegin()->first + 1); - init_free_list(free_list, prev, x); return true; } @@ -2126,6 +2117,7 @@ { State *default_state = dfa.nonmatching; size_t base = 0; + int resize; Trans::iterator i = dfa.trans.find(from); if (i == dfa.trans.end()) { @@ -2142,6 +2134,7 @@ goto do_insert; repeat: + resize = 0; /* get the first free entry that won't underflow */ while (x && (x < c)) { prev = x; @@ -2154,15 +2147,24 @@ x = free_list[x].second; } if (!x) { + resize = 256 - cases.begin()->first; + x = free_list.size(); + /* set prev to last free */ + } else if (x + 255 - cases.begin()->first >= next_check.size()) { + resize = (255 - cases.begin()->first - (next_check.size() - 1 - x)); + for (size_t y = x; y; y = free_list[y].second) + prev = y; + } + if (resize) { /* expand next_check and free_list */ - x = free_list.size(); - size_t range = cases.cases.rbegin()->first - cases.begin()->first + 1; - next_check.resize(next_check.size() + range); - free_list.resize(free_list.size() + range); - init_free_list(free_list, prev, x); + size_t old_size = free_list.size(); + next_check.resize(next_check.size() + resize); + free_list.resize(free_list.size() + resize); + init_free_list(free_list, prev, old_size); if (!first_free) - first_free = x; - goto repeat; + first_free = old_size;; + if (x == old_size) + goto repeat; } base = x - c; @@ -2194,8 +2196,9 @@ st.insert(make_pair(i->second, i->first)); } - os << "(accept, default, base):" << endl; + os << "size=" << default_base.size() << " (accept, default, base): {state} -> {default state}" << endl; for (size_t i = 0; i < default_base.size(); i++) { + os << i << ": "; os << "(" << accept[i] << ", " << num[default_base[i].first] << ", " << default_base[i].second << ")"; @@ -2206,7 +2209,7 @@ os << endl; } - os << "(next, check):" << endl; + os << "size=" << next_check.size() << " (next, check): {check state} -> {next state} : offset from base" << endl; for (size_t i = 0; i < next_check.size(); i++) { if (!next_check[i].second) continue; @@ -2565,9 +2568,9 @@ } extern "C" int aare_add_rule(aare_ruleset_t *rules, char *rule, int deny, - uint32_t perms, uint32_t audit) + uint32_t perms, uint32_t audit, dfaflags_t flags) { - return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule); + return aare_add_rule_vec(rules, deny, perms, audit, 1, &rule, flags); } #define FLAGS_WIDTH 2 @@ -2598,7 +2601,8 @@ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny, uint32_t perms, uint32_t audit, - int count, char **rulev) + int count, char **rulev, + dfaflags_t flags) { Node *tree = NULL, *accept; int exact_match; @@ -2715,6 +2719,18 @@ } } + if (flags & DFA_DUMP_RULE_EXPR) { + cerr << "rule: "; + cerr << rulev[0]; + for (int i = 1; i < count; i++) { + cerr << "\\x00"; + cerr << rulev[i]; + } + cerr << " -> "; + tree->dump(cerr); + cerr << "\n\n"; + } + if (rules->root) rules->root = new AltNode(rules->root, new CatNode(tree, accept)); else === modified file 'parser/parser.h' --- parser/parser.h 2010-02-17 20:21:52 +0000 +++ parser/parser.h 2010-08-03 17:27:13 +0000 @@ -4,6 +4,9 @@ * Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) * + * Copyright (c) 2010 + * Canonical, Ltd. (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. @@ -14,12 +17,12 @@ * 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. + * along with this program; if not, contact Novell, Inc. or Canonical + * Ltd. */ #include #include -#include "pcre/internal.h" #include "immunix.h" #include "libapparmor_re/apparmor_re.h" @@ -40,7 +43,6 @@ struct cod_pattern { char *regex; // posix regex - pcre *compiled; // compiled regex, size is compiled->size }; struct cod_entry { @@ -168,17 +170,20 @@ #define OPTION_REMOVE 2 #define OPTION_REPLACE 3 #define OPTION_STDOUT 4 +#define OPTION_OFILE 5 #define AARE_NONE 0 -#define AARE_PCRE 1 #define AARE_DFA 2 +#define BOOL int + #define FLAG_CHANGEHAT_1_4 2 #define FLAG_CHANGEHAT_1_5 3 extern int flag_changehat_version; extern int read_implies_exec; extern dfaflags_t dfaflags; - +extern int preprocess_only; +extern FILE *ofile; #define PATH_CHROOT_REL 0x1 #define PATH_NS_REL 0x2 @@ -280,12 +285,16 @@ extern void free_cod_entries(struct cod_entry *list); /* parser_symtab.c */ +struct set_value {; + char *val; + struct set_value *next; +}; extern int add_boolean_var(const char *var, int boolean); extern int get_boolean_var(const char *var); extern int new_set_var(const char *var, const char *value); extern int add_set_value(const char *var, const char *value); -extern void *get_set_var(const char *var); -extern char *get_next_set_value(void **context); +extern struct set_value *get_set_var(const char *var); +extern char *get_next_set_value(struct set_value **context); extern void dump_symtab(void); extern void dump_expanded_symtab(void); void free_symtabs(void); @@ -312,7 +321,7 @@ extern void add_hat_to_policy(struct codomain *policy, struct codomain *hat); extern void add_entry_to_policy(struct codomain *policy, struct cod_entry *entry); extern void post_process_nt_entries(struct codomain *cod); -extern int post_process_policy(void); +extern int post_process_policy(int debug_only); extern int process_hat_regex(struct codomain *cod); extern int process_hat_variables(struct codomain *cod); extern int post_merge_rules(void); === modified file 'parser/parser_include.c' --- parser/parser_include.c 2009-07-24 12:18:12 +0000 +++ parser/parser_include.c 2010-08-03 17:27:13 +0000 @@ -1,8 +1,8 @@ -/* $Id$ */ - /* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) + * Copyright (c) 2010 + * Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -14,7 +14,7 @@ * 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. + * along with this program; if not, contact Canonical, Ltd. */ /* Handle subdomain includes, as a straight forward preprocessing phase. @@ -270,3 +270,63 @@ *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 = 1; +} + +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); +} === modified file 'parser/parser_include.h' --- parser/parser_include.h 2009-07-24 07:35:39 +0000 +++ parser/parser_include.h 2010-06-05 06:11:20 +0000 @@ -1,8 +1,8 @@ -/* $Id$ */ - /* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) + * Copyright (c) 2010 + * Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -14,13 +14,15 @@ * 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. + * along with this program; if not, contact Canonical, Ltd. */ #ifndef PARSER_INCLUDE_H #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 +31,8 @@ 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 === modified file 'parser/parser_interface.c' --- parser/parser_interface.c 2010-03-09 09:38:12 +0000 +++ parser/parser_interface.c 2010-08-03 17:27:13 +0000 @@ -133,6 +133,9 @@ PERROR(_("%s: Unable to write to stdout\n"), progname); break; + case OPTION_OFILE: + PERROR(_("%s: Unable to write to output file\n"), + progname); default: PERROR(_("%s: ASSERT: Invalid option: %d\n"), progname, option); @@ -155,6 +158,7 @@ cod->name); break; case OPTION_STDOUT: + case OPTION_OFILE: break; default: PERROR(_("%s: ASSERT: Invalid option: %d\n"), @@ -451,57 +455,6 @@ return 1; } -int sd_serialize_pattern(sd_serialize *p, pcre *pat) -{ - if (!sd_write_struct(p, "pcre")) - return 0; - if (!sd_write32(p, pat->size - sizeof(pcre))) - return 0; - if (!sd_write32(p, pat->magic_number)) - return 0; - if (!sd_write32(p, pat->options)) - return 0; - if (!sd_write16(p, pat->top_bracket)) - return 0; - if (!sd_write16(p, pat->top_backref)) - return 0; - if (!sd_write8(p, pat->first_char)) - return 0; - if (!sd_write8(p, pat->req_char)) - return 0; - if (!sd_write8(p, pat->code[0])) - return 0; - if (!sd_write_blob(p, &pat->code[1], pat->size - sizeof(pcre), NULL)) - return 0; - if (!sd_write_structend(p)) - return 0; - - return 1; -} - -int sd_serialize_file_entry(sd_serialize *p, struct cod_entry *file_entry) -{ - PDEBUG("Writing file entry. name '%s'\n", file_entry->name); - if (!sd_write_struct(p, "fe")) - return 0; - if (!sd_write_string(p, file_entry->name, NULL)) - return 0; - if (!sd_write32(p, file_entry->mode)) - return 0; - if (!sd_write32(p, file_entry->pattern_type)) - return 0; - if (file_entry->pattern_type == ePatternRegex) { - if (!sd_write_string(p, file_entry->pat.regex, NULL)) - return 0; - if (!sd_serialize_pattern(p, file_entry->pat.compiled)) - return 0; - } - if (!sd_write_structend(p)) - return 0; - - return 1; -} - int sd_serialize_dfa(sd_serialize *p, void *dfa, size_t size) { if (dfa && !sd_write_aligned_blob(p, dfa, size, "aadfa")) @@ -594,18 +547,6 @@ return count; } -int count_pcre_ents(struct cod_entry *list) -{ - struct cod_entry *entry; - int count = 0; - list_for_each(list, entry) { - if (entry->pattern_type == ePatternRegex) { - count++; - } - } - return count; -} - int sd_serialize_profile(sd_serialize *p, struct codomain *profile, int flattened) { @@ -724,47 +665,8 @@ if (!sd_serialize_xtable(p, profile->exec_table)) return 0; } else { - /* pcre globbing entries */ - if (count_pcre_ents(profile->entries)) { - if (!sd_write_list(p, "pgent")) - return 0; - list_for_each(profile->entries, entry) { - if (entry->pattern_type == ePatternRegex) { - if (!sd_serialize_file_entry(p, entry)) - return 0; - } - } - if (!sd_write_listend(p)) - return 0; - } - - /* simple globbing entries */ - if (count_tailglob_ents(profile->entries)) { - if (!sd_write_list(p, "sgent")) - return 0; - list_for_each(profile->entries, entry) { - if (entry->pattern_type == ePatternTailGlob) { - if (!sd_serialize_file_entry(p, entry)) - return 0; - } - } - if (!sd_write_listend(p)) - return 0; - } - - /* basic file entries */ - if (count_file_ents(profile->entries)) { - if (!sd_write_list(p, "fent")) - return 0; - list_for_each(profile->entries, entry) { - if (entry->pattern_type == ePatternBasic) { - if (!sd_serialize_file_entry(p, entry)) - return 0; - } - } - if (!sd_write_listend(p)) - return 0; - } + PERROR(_("Unknown pattern type\n")); + return 1; } if (profile->hat_table && regex_type != AARE_DFA) { @@ -812,7 +714,7 @@ int cache_fd = -1; int sd_serialize_codomain(int option, struct codomain *cod) { - int fd; + int fd = -1; int error = -ENOMEM, size, wsize; sd_serialize *work_area; char *filename = NULL; @@ -837,22 +739,29 @@ filename = "stdout"; fd = dup(1); break; + case OPTION_OFILE: + fd = dup(fileno(ofile)); + break; default: error = -EINVAL; goto exit; break; } - if (kernel_load && fd < 0) { - PERROR(_("Unable to open %s - %s\n"), filename, - strerror(errno)); + if (fd < 0) { + if (kernel_load) + PERROR(_("Unable to open %s - %s\n"), filename, + strerror(errno)); + else + PERROR(_("Unable to open output file - %s\n"), + strerror(errno)); error = -errno; goto exit; } error = 0; - if (option != OPTION_STDOUT) + if (option != OPTION_STDOUT && option != OPTION_OFILE) free(filename); if (option == OPTION_REMOVE) { @@ -918,7 +827,7 @@ } size = work_area->pos - work_area->buffer; - if (kernel_load || option == OPTION_STDOUT) { + if (kernel_load || option == OPTION_STDOUT || option == OPTION_OFILE) { wsize = write(fd, work_area->buffer, size); if (wsize < 0) { error = -errno; @@ -941,7 +850,7 @@ free_sd_serial(work_area); } - if (kernel_load) close(fd); + close(fd); if (cod->hat_table && regex_type == AARE_DFA && option != OPTION_REMOVE) { if (load_flattened_hats(cod) != 0) @@ -973,7 +882,7 @@ int sd_load_buffer(int option, char *buffer, int size) { - int fd; + int fd = -1; int error = -ENOMEM, wsize, bsize; char *filename = NULL; char *b; === modified file 'parser/parser_lex.l' --- parser/parser_lex.l 2010-03-09 05:49:16 +0000 +++ parser/parser_lex.l 2010-08-03 17:27:13 +0000 @@ -1,8 +1,8 @@ -/* $Id$ */ - /* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) + * Copyright (c) 2010 + * Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -14,7 +14,7 @@ * 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. + * along with this program; if not, contact Canonical, Ltd. */ /* Definitions section */ @@ -49,7 +49,12 @@ #endif #define NPDEBUG(fmt, args...) /* Do nothing */ -int current_lineno = 1; +#define DUMP_PREPROCESS do { if (preprocess_only) ECHO; } while (0) + +#define YY_NO_INPUT + +int current_lineno = 1; +char *current_filename = NULL; struct ignored_suffix_t { char * text; @@ -80,14 +85,19 @@ 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"); } 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 +105,7 @@ 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 +150,9 @@ 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)); } } @@ -227,6 +239,8 @@ } <> { + fclose(yyin); + pop_include_stack(); yypop_buffer_state(); if ( !YY_CURRENT_BUFFER ) yyterminate(); } @@ -240,6 +254,7 @@ * 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); @@ -253,6 +268,7 @@ * 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); @@ -260,6 +276,7 @@ } [^\n] { + DUMP_PREPROCESS; /* Something we didn't expect */ yyerror(_("Found unexpected character: '%s'"), yytext); } @@ -274,6 +291,7 @@ * 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); @@ -287,14 +305,16 @@ * 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); } @@ -302,85 +322,99 @@ { {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; } @@ -393,6 +427,7 @@ * 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); @@ -406,14 +441,16 @@ * 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); } @@ -421,127 +458,147 @@ #include/.*\r?\n { /* include */ PDEBUG("Matched #include\n"); - current_lineno++; BEGIN(INCLUDE); } #.*\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 */ @@ -573,11 +630,13 @@ 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); } === modified file 'parser/parser_main.c' --- parser/parser_main.c 2010-01-28 01:20:13 +0000 +++ parser/parser_main.c 2010-08-03 17:27:13 +0000 @@ -4,6 +4,9 @@ * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) * + * Copyright (c) 2010 + * Canonical, Ltd. (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. @@ -14,7 +17,8 @@ * 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. + * along with this program; if not, contact Novell, Inc. or Canonical, + * Ltd. */ #include @@ -26,6 +30,7 @@ #include #include #include +#include #include #define _(s) gettext(s) @@ -34,6 +39,8 @@ #include #include +#include +#include #include "parser.h" #include "parser_version.h" @@ -47,7 +54,6 @@ #define MATCH_STRING "/sys/kernel/security/" MODULE_NAME "/matching" #define FLAGS_FILE "/sys/kernel/security/" MODULE_NAME "/features" #define MOUNTED_FS "/proc/mounts" -#define PCRE "pattern=pcre" #define AADFA "pattern=aadfa" #define PRIVILEGED_OPS (write_cache || kernel_load) @@ -76,6 +82,8 @@ #else int read_implies_exec = 0; #endif +int preprocess_only = 0; +int skip_mode_force = 0; char *subdomainbase = NULL; char *match_string = NULL; @@ -84,8 +92,7 @@ int perms_create = 0; /* perms contain create flag */ char *profile_namespace = NULL; int flag_changehat_version = FLAG_CHANGEHAT_1_5; - -extern int current_lineno; +FILE *ofile = NULL; /* per-profile settings */ int force_complain = 0; @@ -106,6 +113,7 @@ {"remove", 0, 0, 'R'}, {"names", 0, 0, 'N'}, {"stdout", 0, 0, 'S'}, + {"ofile", 1, 0, 'o'}, {"match-string", 1, 0, 'm'}, {"quiet", 0, 0, 'q'}, {"skip-kernel-load", 0, 0, 'Q'}, @@ -121,6 +129,7 @@ {"Dump", 1, 0, 'D'}, {"optimize", 1, 0, 'O'}, {"Optimize", 1, 0, 'O'}, + {"preprocess", 0, 0, 'p'}, {NULL, 0, 0, 0}, }; @@ -145,6 +154,7 @@ "-B, --binary Input is precompiled profile\n" "-N, --names Dump names of profiles in input.\n" "-S, --stdout Dump compiled profile to stdout\n" + "-o n, --ofile n Write output to file n\n" "-b n, --base n Set base dir and cwd\n" "-I n, --Include n Add n to the search path\n" "-f n, --subdomainfs n Set location of apparmor filesystem\n" @@ -160,9 +170,10 @@ "-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); } @@ -175,6 +186,7 @@ "no option specified Dump variables\n" "variables Dump variables\n" "expanded-variables Dump expanded variables\n" + "rule-exprs Dump rule to expr tree conversions\n" "expr-stats Dump stats on expr tree\n" "expr-tree Dump expression tree\n" "expr-simple Dump simplified expression tree\n" @@ -224,8 +236,10 @@ 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) @@ -244,7 +258,7 @@ 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:po:", long_options, &o)) != -1) { switch (c) { case 0: @@ -258,7 +272,7 @@ break; case 'd': debug++; - skip_cache = 1; + skip_read_cache = 1; break; case 'h': if (!optarg) { @@ -285,6 +299,7 @@ case 'R': count++; option = OPTION_REMOVE; + skip_cache = 1; break; case 'V': display_version(); @@ -297,10 +312,12 @@ set_base_dir(optarg); break; case 'B': - binary_input =1; + binary_input = 1; + skip_cache = 1; break; case 'C': opt_force_complain = 1; + skip_cache = 1; break; case 'N': names_only = 1; @@ -312,17 +329,31 @@ skip_read_cache = 1; kernel_load = 0; break; + case 'o': + count++; + option = OPTION_OFILE; + skip_read_cache = 1; + kernel_load = 0; + ofile = fopen(optarg, "w"); + if (!ofile) { + PERROR("%s: Could not open file %s\n", + progname, optarg); + exit(1); + } + break; case 'f': subdomainbase = strndup(optarg, PATH_MAX); break; case 'D': - skip_cache = 1; + skip_read_cache = 1; if (!optarg) { dump_vars = 1; } else if (strcmp(optarg, "variables") == 0) { dump_vars = 1; } else if (strcmp(optarg, "expanded-variables") == 0) { dump_expanded_vars = 1; + } else if (strcmp(optarg, "rule-exprs") == 0) { + dfaflags |= DFA_DUMP_RULE_EXPR; } else if (strcmp(optarg, "expr-tree") == 0) { dfaflags |= DFA_DUMP_TREE; } else if (strcmp(optarg, "expr-simple") == 0) { @@ -359,7 +390,7 @@ } break; case 'O': - skip_cache = 1; + skip_read_cache = 1; if (strcmp(optarg, "0") == 0) { dfaflags |= DFA_CONTROL_NO_TREE_NORMAL | DFA_CONTROL_NO_TREE_SIMPLE | @@ -435,6 +466,13 @@ case 'Q': kernel_load = 0; break; + case 'p': + count++; + kernel_load = 0; + skip_cache = 1; + preprocess_only = 1; + skip_mode_force = 1; + break; default: display_usage(progname); exit(0); @@ -569,9 +607,6 @@ out: if (match_string) { - if (strstr(match_string, PCRE)) - regex_type = AARE_PCRE; - if (strstr(match_string, AADFA)) regex_type = AARE_DFA; @@ -620,20 +655,6 @@ return; } -/* return 1 --> PCRE should work fine - return 0 --> no PCRE support */ -static int regex_support(void) { - /* no match string, predates (or postdates?) the split matching - module design */ - if (!match_string) - return 1; - - if (regex_type != AARE_NONE) - return 1; - - return 0; -} - int process_binary(int option, char *profilename) { char *buffer = NULL; @@ -695,12 +716,33 @@ return retval; } -void reset_parser(void) +void reset_parser(char *filename) { free_aliases(); free_symtabs(); free_policies(); reset_regex(); + reset_include_stack(filename); +} + +int test_for_dir_mode(const char *basename, const char *linkdir) +{ + int rc = 0; + + if (!skip_mode_force) { + char *target = NULL; + if (asprintf(&target, "%s/%s/%s", basedir, linkdir, basename) < 0) { + perror("asprintf"); + exit(1); + } + + if (access(target, R_OK) == 0) + rc = 1; + + free(target); + } + + return rc; } int process_profile(int option, char *profilename) @@ -727,37 +769,22 @@ if (profilename && option != OPTION_REMOVE) { /* make decisions about disabled or complain-mode profiles */ - char *target = NULL; char *basename = strrchr(profilename, '/'); if (basename) basename++; else basename = profilename; - if (asprintf(&target, "%s/%s/%s", basedir, "disable", basename) < 0) { - perror("asprintf"); - exit(1); - } - - if (access(target, R_OK) == 0) { - if (!conf_quiet) - PERROR("Skipping profile in %s/disable: %s\n", basedir, basename); - free(target); + if (test_for_dir_mode(basename, "disable")) { + if (!conf_quiet) + PERROR("Skipping profile in %s/disable: %s\n", basedir, basename); goto out; } - free(target); - - if (asprintf(&target, "%s/%s/%s", basedir, "force-complain", basename)<0) { - perror("asprintf"); - exit(1); - } - - if (access(target, R_OK) == 0) { - if (!conf_quiet) - PERROR("Warning: found %s in %s/force-complain, forcing complain mode\n", basename, basedir); - force_complain = 1; - } - free(target); + + if (test_for_dir_mode(basename, "force-complain")) { + PERROR("Warning: found %s in %s/force-complain, forcing complain mode\n", basename, basedir); + force_complain = 1; + } if (!force_complain && !skip_cache) { fstat(fileno(yyin), &stat_text); @@ -796,12 +823,15 @@ if (yyin) yyrestart(yyin); - reset_parser(); + reset_parser(profilename); retval = yyparse(); if (retval != 0) goto out; + if (preprocess_only) + goto out; + if (names_only) { dump_policy_names(); goto out; @@ -812,7 +842,7 @@ goto out; } - retval = post_process_policy(); + retval = post_process_policy(debug); if (retval != 0) { PERROR(_("%s: Errors found in file. Aborting.\n"), progname); goto out; @@ -829,10 +859,6 @@ goto out; } - if (!regex_support()) { - die_if_any_regex(); - } - retval = load_policy(option); out: @@ -969,5 +995,8 @@ profilename = NULL; } + if (ofile) + fclose(ofile); + return retval; } === modified file 'parser/parser_misc.c' --- parser/parser_misc.c 2010-03-09 04:38:54 +0000 +++ parser/parser_misc.c 2010-08-03 17:27:13 +0000 @@ -19,6 +19,7 @@ /* assistance routines */ +#include #include #include #include @@ -27,6 +28,7 @@ #define _(s) gettext(s) #include #include +#include #include #include @@ -35,8 +37,10 @@ /* #define DEBUG */ #ifdef DEBUG +#undef PDEBUG #define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args) #else +#undef PDEBUG #define PDEBUG(fmt, args...) /* Do nothing */ #endif #define NPDEBUG(fmt, args...) /* Do nothing */ @@ -102,7 +106,7 @@ }; /* for alpha matches, check for keywords */ -static int get_table_token(const char *name, struct keyword_table *table, +static int get_table_token(const char *name __unused, struct keyword_table *table, const char *keyword) { int i; @@ -142,42 +146,6 @@ return get_table_token("rlimit", rlimit_table, name); } -static struct keyword_table address_family[] = { -/* {"unix", AF_UNIX}, - {"local", AF_LOCAL}, */ - {"inet", AF_INET}, -/* {"ax25", AF_AX25}, - {"ipx", AF_IPX}, - {"appletalk", AF_APPLETALK}, - {"netrom", AF_NETROM}, - {"bridge", AF_BRIDGE}, - {"atmpvc", AF_ATMPVC}, - {"x25", AF_X25}, */ - {"inet6", AF_INET6}, -/* {"rose", AF_ROSE}, - {"decnet", AF_DECnet}, - {"netbeui", AF_NETBEUI}, - {"security", AF_SECURITY}, - {"key", AF_KEY}, - {"netlink", AF_NETLINK}, - {"route", AF_ROUTE}, - {"packet", AF_PACKET}, - {"ash", AF_ASH}, - {"econet", AF_ECONET}, - {"atmsvc", AF_ATMSVC}, - {"sna", AF_SNA}, - {"irda", AF_IRDA}, - {"pppox", AF_PPPOX}, - {"wanpipe", AF_WANPIPE}, - {"llc", AF_LLC}, - {"tipc", AF_TIPC}, - {"bluetooth", AF_BLUETOOTH}, - {"iucv", AF_IUCV}, - {"rxrpc", AF_RXRPC}, */ - /* terminate */ - {NULL, 0} -}; - struct network_tuple { char *family_name; unsigned int family; @@ -462,7 +430,7 @@ } } -static int parse_sub_mode(const char *str_mode, const char *mode_desc) +static int parse_sub_mode(const char *str_mode, const char *mode_desc __unused) { #define IS_DIFF_QUAL(mode, q) (((mode) & AA_MAY_EXEC) && (((mode) & AA_EXEC_TYPE) != ((q) & AA_EXEC_TYPE))) @@ -666,7 +634,6 @@ entry->pattern_type = ePatternInvalid; entry->pat.regex = NULL; - entry->pat.compiled = NULL; entry->next = NULL; @@ -692,7 +659,6 @@ /* 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; @@ -713,8 +679,6 @@ free(list->link_name); if (list->pat.regex) free(list->pat.regex); - if (list->pat.compiled) - free(list->pat.compiled); free(list); } === modified file 'parser/parser_policy.c' --- parser/parser_policy.c 2009-08-20 15:27:12 +0000 +++ parser/parser_policy.c 2010-05-31 18:58:40 +0000 @@ -4,6 +4,9 @@ * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) * + * Copyright (c) 2010 + * Canonical, Ltd. (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. @@ -14,7 +17,8 @@ * 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. + * along with this program; if not, contact Novell, Inc. or Canonical, + * Ltd. */ #include @@ -664,7 +668,7 @@ return ret; } -int post_process_policy(void) +int post_process_policy(int debug_only) { int retval = 0; @@ -696,11 +700,13 @@ return retval; } - retval = post_process_regex(); - if (retval != 0) { - PERROR(_("%s: Errors found during regex postprocess. Aborting.\n"), - progname); - return retval; + if (!debug_only) { + retval = post_process_regex(); + if (retval != 0) { + PERROR(_("%s: Errors found during regex postprocess. Aborting.\n"), + progname); + return retval; + } } return retval; === modified file 'parser/parser_regex.c' --- parser/parser_regex.c 2010-02-12 21:49:58 +0000 +++ parser/parser_regex.c 2010-08-03 17:27:13 +0000 @@ -21,6 +21,7 @@ #include #include #include +#include #define _(s) gettext(s) /* #define DEBUG */ @@ -74,7 +75,6 @@ { char *sptr, *dptr; BOOL seen_slash = 0; - int len; if (!path || (strlen(path) < 2)) return; @@ -106,19 +106,6 @@ } } *dptr = 0; - - if (regex_type != AARE_DFA) { - /* eliminate trailing slashes for versions of apparmor that - * do not use the dfa engine. - * Versions of apparmor which use the dfa engine use the - * trailing / to differentiate between file and directory - * matches - */ - len = strlen(path); - if (len > 2 && path[len -1] == '/') { - path[len - 1] = 0; - } - } } static pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, @@ -154,6 +141,9 @@ sptr = aare; dptr = pcre; + if (dfaflags & DFA_DUMP_RULE_EXPR) + fprintf(stderr, "aare: %s -> ", aare); + if (anchor) /* anchor beginning of regular expression */ *dptr++ = '^'; @@ -406,85 +396,13 @@ out: if (ret == FALSE) ptype = ePatternInvalid; + + if (dfaflags & DFA_DUMP_RULE_EXPR) + fprintf(stderr, "%s\n", pcre); + return ptype; } -static int process_pcre_entry(struct cod_entry *entry) -{ - char tbuf[PATH_MAX + 3]; /* +3 for ^, $ and \0 */ - int ret = TRUE; - pattern_t ptype; - int pos; - if (!entry) /* shouldn't happen */ - return TRUE; - - ptype = convert_aaregex_to_pcre(entry->name, 1, tbuf, PATH_MAX+3, &pos); - if (ptype == ePatternInvalid) - return FALSE; - - entry->pattern_type = ptype; - - /* - * Only use buffer (tbuf) that we built above, if we - * identified a pattern requiring full regex support. - */ - if (ptype == ePatternRegex) { - int pattlen = strlen(tbuf); - - if ((entry->pat.regex = malloc(pattlen + 1))) { - const char *errorreason; - int errpos; - - strcpy(entry->pat.regex, tbuf); - - if ((entry->pat.compiled = - pcre_compile(entry->pat.regex, 0, - &errorreason, &errpos, - NULL))) { - /* NULL out tables, kernel will use a - * private version - */ - entry->pat.compiled->tables = NULL; - } else { - int i; - - PERROR(_("%s: Failed to compile regex '%s' [original: '%s']\n"), - progname, entry->pat.regex, - entry->name); - - PERROR(_("%s: error near "), - progname); - - for (i = 0; i < errpos; i++) { - fputc('.', stderr); - } - - fputc('^', stderr); - fputc('\n', stderr); - - PERROR(_("%s: error reason: '%s'\n"), - progname, errorreason); - - free(entry->pat.regex); - entry->pat.regex = NULL; - - ret = FALSE; - } - } else { - PERROR(_("%s: Failed to compile regex '%s' [original: '%s'] - malloc failed\n"), - progname, entry->pat.regex, entry->name); - - ret = FALSE; - } - } else { - /* not a regex, scan input for any escape characters - * and remove, and reduce double \\ to a single */ - filter_escapes(entry->name); - } /* ptype == ePatternRegex */ - - return ret; -} - static const char *local_name(const char *name) { const char *t; @@ -519,7 +437,7 @@ aare_ruleset_t *rule = aare_new_ruleset(0); if (!rule) return FALSE; - if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0)) { + if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) { aare_delete_ruleset(rule); return FALSE; } @@ -529,7 +447,7 @@ int len; convert_aaregex_to_pcre(alt->name, 0, tbuf, PATH_MAX + 3, &len); - if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0)) { + if (!aare_add_rule(rule, tbuf, 0, AA_MAY_EXEC, 0, dfaflags)) { aare_delete_ruleset(rule); return FALSE; } @@ -538,7 +456,7 @@ cod->xmatch = aare_create_dfa(rule, &cod->xmatch_size, dfaflags); aare_delete_ruleset(rule); - if (!cod->xmatch) + if (!cod->xmatch) return FALSE; } @@ -581,11 +499,11 @@ if (entry->deny && (entry->mode & AA_LINK_BITS)) { if (!aare_add_rule(dfarules, tbuf, entry->deny, entry->mode & ~AA_LINK_BITS, - entry->audit & ~AA_LINK_BITS)) + entry->audit & ~AA_LINK_BITS, dfaflags)) return FALSE; } else if (entry->mode & ~AA_CHANGE_PROFILE) { if (!aare_add_rule(dfarules, tbuf, entry->deny, entry->mode, - entry->audit)) + entry->audit, dfaflags)) return FALSE; } @@ -607,7 +525,7 @@ perms |= LINK_TO_LINK_SUBSET(perms); vec[1] = "/[^/].*"; } - if (!aare_add_rule_vec(dfarules, entry->deny, perms, entry->audit & AA_LINK_BITS, 2, vec)) + if (!aare_add_rule_vec(dfarules, entry->deny, perms, entry->audit & AA_LINK_BITS, 2, vec, dfaflags)) return FALSE; } if (entry->mode & AA_CHANGE_PROFILE) { @@ -618,10 +536,10 @@ ptype = convert_aaregex_to_pcre(entry->namespace, 0, lbuf, PATH_MAX + 8, &pos); vec[0] = lbuf; vec[1] = tbuf; - if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE, 0, 2, vec)) + if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE, 0, 2, vec, dfaflags)) return FALSE; } else { - if (!aare_add_rule(dfarules, tbuf, 0, AA_CHANGE_PROFILE, 0)) + if (!aare_add_rule(dfarules, tbuf, 0, AA_CHANGE_PROFILE, 0, dfaflags)) return FALSE; } } @@ -631,10 +549,10 @@ char *vec[2]; vec[0] = entry->namespace; vec[1] = entry->name; - if (!aare_add_rule_vec(dfarules, 0, mode, 0, 2, vec)) + if (!aare_add_rule_vec(dfarules, 0, mode, 0, 2, vec, dfaflags)) return FALSE; } else { - if (!aare_add_rule(dfarules, entry->name, 0, mode, 0)) + if (!aare_add_rule(dfarules, entry->name, 0, mode, 0, dfaflags)) return FALSE; } } @@ -643,18 +561,13 @@ int post_process_entries(struct codomain *cod) { - int ret = TRUE, rc; + int ret = TRUE; struct cod_entry *entry; int count = 0; list_for_each(cod->entries, entry) { - if (regex_type == AARE_DFA) { - rc = process_dfa_entry(cod->dfarules, entry); - } else { - filter_slashes(entry->name); - rc = process_pcre_entry(entry); - } - if (!rc) + if (regex_type == AARE_DFA && + !process_dfa_entry(cod->dfarules, entry)) ret = FALSE; count++; } @@ -770,7 +683,7 @@ test_string = strdup("///foo//////f//oo////////////////"); filter_slashes(test_string); - MY_TEST(strcmp(test_string, "/foo/f/oo") == 0, "simple tests"); + MY_TEST(strcmp(test_string, "/foo/f/oo/") == 0, "simple tests"); test_string = strdup("/foo/f/oo"); filter_slashes(test_string); @@ -802,13 +715,11 @@ test_string = strdup("/a/"); filter_slashes(test_string); - MY_TEST(strcmp(test_string, "/a") == 0, "simple test for /a/"); + MY_TEST(strcmp(test_string, "/a/") == 0, "simple test for /a/"); return rc; } -int regex_type = AARE_PCRE; - int main(void) { int rc = 0; === modified file 'parser/parser_symtab.c' --- parser/parser_symtab.c 2009-07-24 13:24:53 +0000 +++ parser/parser_symtab.c 2010-08-03 17:27:13 +0000 @@ -23,6 +23,7 @@ #include #include #include +#include #define _(s) gettext(s) #include "immunix.h" @@ -33,11 +34,6 @@ sd_set, }; -struct set_value { - char *val; - struct set_value *next; -}; - struct symtab { char *var_name; enum var_type type; @@ -288,7 +284,7 @@ /* returns a pointer to the value list, which should be used as the * argument to the get_next_set_value() function. */ -void *get_set_var(const char *var) +struct set_value *get_set_var(const char *var) { struct symtab *result; struct set_value *valuelist = NULL; @@ -321,16 +317,17 @@ } /* iterator to walk the list of set values */ -char *get_next_set_value(void **list) +char *get_next_set_value(struct set_value **list) { - struct set_value **valuelist = (struct set_value **) list; + struct set_value *next; char *ret; - if (!valuelist || !(*valuelist)) + if (!list || !(*list)) return NULL; - ret = (*valuelist)->val; - (*valuelist) = (*valuelist)->next; + ret = (*list)->val; + next = (*list)->next; + (*list) = next; return ret; } @@ -429,10 +426,13 @@ for (ref_item = ref->expanded; ref_item; ref_item = ref_item->next) { char *expanded_string; - asprintf(&expanded_string, "%s%s%s", + if (!asprintf(&expanded_string, "%s%s%s", split->prefix ? split->prefix : "", ref_item->val, - split->suffix ? split->suffix : ""); + split->suffix ? split->suffix : "")) { + PERROR("Out of memory\n"); + exit(1); + } add_to_set(&work_list, expanded_string); free(expanded_string); } @@ -569,7 +569,7 @@ { int rc = 0; int retval; - void *retptr; + struct set_value *retptr; struct symtab *a, *b; a = new_symtab_entry("blah"); === modified file 'parser/parser_variable.c' --- parser/parser_variable.c 2009-07-24 23:47:46 +0000 +++ parser/parser_variable.c 2010-08-03 17:27:13 +0000 @@ -17,10 +17,14 @@ * along with this program; if not, contact Novell, Inc. */ +#include +#include #include #include #include #include +#include + #define _(s) gettext(s) /* #define DEBUG */ @@ -124,7 +128,7 @@ static int expand_entry_variables(struct cod_entry *entry) { - void *valuelist; + struct set_value *valuelist; int ret = TRUE; char *value; struct var_string *split_var; === modified file 'parser/parser_yacc.y' --- parser/parser_yacc.y 2010-02-17 20:21:52 +0000 +++ parser/parser_yacc.y 2010-08-03 17:27:13 +0000 @@ -1,9 +1,9 @@ %{ -/* $Id$ */ - /* * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 * NOVELL (All rights reserved) + * Copyright (c) 2010 + * Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -15,7 +15,7 @@ * 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. + * along with this program; if not, contact Canonical, Ltd. */ #define YYERROR_VERBOSE 1 @@ -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; @@ -415,7 +412,7 @@ flagval: TOK_FLAG_ID { - struct flagval fv = {0, 0, 0}; + struct flagval fv = { 0, 0, 0, 0 }; if (strcmp($1, "debug") == 0) { yyerror(_("Profile flag 'debug' is no longer valid.")); } else if (strcmp($1, "complain") == 0) { @@ -1109,10 +1106,15 @@ 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); } === removed directory 'parser/pcre' === removed file 'parser/pcre/Makefile' --- parser/pcre/Makefile 2007-03-30 16:30:15 +0000 +++ parser/pcre/Makefile 1970-01-01 00:00:00 +0000 @@ -1,7 +0,0 @@ -all: pcre.o - -pcre.o: pcre.c - $(CC) -c $(CFLAGS) $< - -clean: - rm -f pcre.o === removed file 'parser/pcre/chartables.c' --- parser/pcre/chartables.c 2006-04-11 21:52:54 +0000 +++ parser/pcre/chartables.c 1970-01-01 00:00:00 +0000 @@ -1,183 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* This file is automatically written by the dftables auxiliary -program. If you edit it by hand, you might like to edit the Makefile to -prevent its ever being regenerated. - -This file is #included in the compilation of pcre.c to build the default -character tables which are used when no tables are passed to the compile -function. */ - -static unsigned char pcre_default_tables[] = { - -/* This table is a lower casing table. */ - - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 97, 98, 99,100,101,102,103, - 104,105,106,107,108,109,110,111, - 112,113,114,115,116,117,118,119, - 120,121,122, 91, 92, 93, 94, 95, - 96, 97, 98, 99,100,101,102,103, - 104,105,106,107,108,109,110,111, - 112,113,114,115,116,117,118,119, - 120,121,122,123,124,125,126,127, - 128,129,130,131,132,133,134,135, - 136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151, - 152,153,154,155,156,157,158,159, - 160,161,162,163,164,165,166,167, - 168,169,170,171,172,173,174,175, - 176,177,178,179,180,181,182,183, - 184,185,186,187,188,189,190,191, - 192,193,194,195,196,197,198,199, - 200,201,202,203,204,205,206,207, - 208,209,210,211,212,213,214,215, - 216,217,218,219,220,221,222,223, - 224,225,226,227,228,229,230,231, - 232,233,234,235,236,237,238,239, - 240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255, - -/* This table is a case flipping table. */ - - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 97, 98, 99,100,101,102,103, - 104,105,106,107,108,109,110,111, - 112,113,114,115,116,117,118,119, - 120,121,122, 91, 92, 93, 94, 95, - 96, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90,123,124,125,126,127, - 128,129,130,131,132,133,134,135, - 136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151, - 152,153,154,155,156,157,158,159, - 160,161,162,163,164,165,166,167, - 168,169,170,171,172,173,174,175, - 176,177,178,179,180,181,182,183, - 184,185,186,187,188,189,190,191, - 192,193,194,195,196,197,198,199, - 200,201,202,203,204,205,206,207, - 208,209,210,211,212,213,214,215, - 216,217,218,219,220,221,222,223, - 224,225,226,227,228,229,230,231, - 232,233,234,235,236,237,238,239, - 240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,255, - -/* This table contains bit maps for various character classes. -Each map is 32 bytes long and the bits run from the least -significant end of each byte. The classes that have their own -maps are: space, xdigit, digit, upper, lower, word, graph -print, punct, and cntrl. Other classes are built from combinations. */ - - 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, - 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, - 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, - 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - - 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - -/* This table identifies various classes of character by individual bits: - 0x01 white space character - 0x02 letter - 0x04 decimal digit - 0x08 hexadecimal digit - 0x10 alphanumeric or '_' - 0x80 regular expression metacharacter or binary zero -*/ - - 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ - 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ - 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ - 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ - 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ - 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ - 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ - 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ - 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ - 0x12,0x12,0x12,0x80,0x00,0x00,0x80,0x10, /* X - _ */ - 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ - 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ - 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ - 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ - -/* End of chartables.c */ === removed file 'parser/pcre/config.h' --- parser/pcre/config.h 2006-04-11 21:52:54 +0000 +++ parser/pcre/config.h 1970-01-01 00:00:00 +0000 @@ -1,48 +0,0 @@ -/* config.h. Generated automatically by configure. */ - -/* On Unix systems config.in is converted by configure into config.h. PCRE is -written in Standard C, but there are a few non-standard things it can cope -with, allowing it to run on SunOS4 and other "close to standard" systems. - -On a non-Unix system you should just copy this file into config.h, and set up -the macros the way you need them. You should normally change the definitions of -HAVE_STRERROR and HAVE_MEMMOVE to 1. Unfortunately, because of the way autoconf -works, these cannot be made the defaults. If your system has bcopy() and not -memmove(), change the definition of HAVE_BCOPY instead of HAVE_MEMMOVE. If your -system has neither bcopy() nor memmove(), leave them both as 0; an emulation -function will be used. */ - -/* Define to empty if the keyword does not work. */ - -/* #undef const */ - -/* Define to `unsigned' if doesn't define size_t. */ - -/* #undef size_t */ - -/* The following two definitions are mainly for the benefit of SunOS4, which -doesn't have the strerror() or memmove() functions that should be present in -all Standard C libraries. The macros HAVE_STRERROR and HAVE_MEMMOVE should -normally be defined with the value 1 for other systems, but unfortunately we -can't make this the default because "configure" files generated by autoconf -will only change 0 to 1; they won't change 1 to 0 if the functions are not -found. */ - -#define HAVE_STRERROR 1 -#define HAVE_MEMMOVE 1 - -/* There are some non-Unix systems that don't even have bcopy(). If this macro -is false, an emulation is used. If HAVE_MEMMOVE is set to 1, the value of -HAVE_BCOPY is not relevant. */ - -#define HAVE_BCOPY 1 - -/* The value of NEWLINE determines the newline character. The default is to -leave it up to the compiler, but some sites want to force a particular value. -On Unix systems, "configure" can be used to override this default. */ - -#ifndef NEWLINE -#define NEWLINE '\n' -#endif - -/* End */ === removed file 'parser/pcre/internal.h' --- parser/pcre/internal.h 2006-04-11 21:52:54 +0000 +++ parser/pcre/internal.h 1970-01-01 00:00:00 +0000 @@ -1,412 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - - -/* This is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. See -the file Tech.Notes for some information on the internals. - -Written by: Philip Hazel - - Copyright (c) 1997-2001 University of Cambridge - ------------------------------------------------------------------------------ -Permission is granted to anyone to use this software for any purpose on any -computer system, and to redistribute it freely, subject to the following -restrictions: - -1. This software 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. - -2. The origin of this software must not be misrepresented, either by - explicit claim or by omission. - -3. Altered versions must be plainly marked as such, and must not be - misrepresented as being the original software. - -4. If PCRE is embedded in any software that is released under the GNU - General Purpose Licence (GPL), then the terms of that licence shall - supersede any condition above with which it is incompatible. ------------------------------------------------------------------------------ -*/ - -/* This header contains definitions that are shared between the different -modules, but which are not relevant to the outside. */ - -/* Get the definitions provided by running "configure" */ - -#include "config.h" - -/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), -define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY -is set. Otherwise, include an emulating function for those systems that have -neither (there some non-Unix environments where this is the case). This assumes -that all calls to memmove are moving strings upwards in store, which is the -case in PCRE. */ - -#if ! HAVE_MEMMOVE -#undef memmove /* some systems may have a macro */ -#if HAVE_BCOPY -#define memmove(a, b, c) bcopy(b, a, c) -#else -void * -pcre_memmove(unsigned char *dest, const unsigned char *src, size_t n) -{ -int i; -dest += n; -src += n; -for (i = 0; i < n; ++i) *(--dest) = *(--src); -} -#define memmove(a, b, c) pcre_memmove(a, b, c) -#endif -#endif - -/* Standard C headers plus the external interface definition */ - -#include -#include -#include -#include -#include -#include -#include "pcre.h" - -/* In case there is no definition of offsetof() provided - though any proper -Standard C system should have one. */ - -#ifndef offsetof -#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) -#endif - -/* These are the public options that can change during matching. */ - -#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) - -/* Private options flags start at the most significant end of the four bytes, -but skip the top bit so we can use ints for convenience without getting tangled -with negative values. The public options defined in pcre.h start at the least -significant end. Make sure they don't overlap, though now that we have expanded -to four bytes there is plenty of space. */ - -#define PCRE_FIRSTSET 0x40000000 /* first_char is set */ -#define PCRE_REQCHSET 0x20000000 /* req_char is set */ -#define PCRE_STARTLINE 0x10000000 /* start after \n for multiline */ -#define PCRE_INGROUP 0x08000000 /* compiling inside a group */ -#define PCRE_ICHANGED 0x04000000 /* i option changes within regex */ - -/* Options for the "extra" block produced by pcre_study(). */ - -#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ - -/* Masks for identifying the public options which are permitted at compile -time, run time or study time, respectively. */ - -#define PUBLIC_OPTIONS \ - (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ - PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8) - -#define PUBLIC_EXEC_OPTIONS \ - (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY) - -#define PUBLIC_STUDY_OPTIONS 0 /* None defined */ - -/* Magic number to provide a small check against being handed junk. */ - -#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ - -/* Miscellaneous definitions */ - -typedef int BOOL; - -#define FALSE 0 -#define TRUE 1 - -/* Escape items that are just an encoding of a particular data value. Note that -ESC_N is defined as yet another macro, which is set in config.h to either \n -(the default) or \r (which some people want). */ - -#ifndef ESC_E -#define ESC_E 27 -#endif - -#ifndef ESC_F -#define ESC_F '\f' -#endif - -#ifndef ESC_N -#define ESC_N NEWLINE -#endif - -#ifndef ESC_R -#define ESC_R '\r' -#endif - -#ifndef ESC_T -#define ESC_T '\t' -#endif - -/* These are escaped items that aren't just an encoding of a particular data -value such as \n. They must have non-zero values, as check_escape() returns -their negation. Also, they must appear in the same order as in the opcode -definitions below, up to ESC_z. The final one must be ESC_REF as subsequent -values are used for \1, \2, \3, etc. There is a test in the code for an escape -greater than ESC_b and less than ESC_Z to detect the types that may be -repeated. If any new escapes are put in-between that don't consume a character, -that code will have to change. */ - -enum { ESC_A = 1, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, ESC_W, ESC_w, - ESC_Z, ESC_z, ESC_REF }; - -/* Opcode table: OP_BRA must be last, as all values >= it are used for brackets -that extract substrings. Starting from 1 (i.e. after OP_END), the values up to -OP_EOD must correspond in order to the list of escapes immediately above. */ - -enum { - OP_END, /* End of pattern */ - - /* Values corresponding to backslashed metacharacters */ - - OP_SOD, /* Start of data: \A */ - OP_NOT_WORD_BOUNDARY, /* \B */ - OP_WORD_BOUNDARY, /* \b */ - OP_NOT_DIGIT, /* \D */ - OP_DIGIT, /* \d */ - OP_NOT_WHITESPACE, /* \S */ - OP_WHITESPACE, /* \s */ - OP_NOT_WORDCHAR, /* \W */ - OP_WORDCHAR, /* \w */ - OP_EODN, /* End of data or \n at end of data: \Z. */ - OP_EOD, /* End of data: \z */ - - OP_OPT, /* Set runtime options */ - OP_CIRC, /* Start of line - varies with multiline switch */ - OP_DOLL, /* End of line - varies with multiline switch */ - OP_ANY, /* Match any character */ - OP_CHARS, /* Match string of characters */ - OP_NOT, /* Match anything but the following char */ - - OP_STAR, /* The maximizing and minimizing versions of */ - OP_MINSTAR, /* all these opcodes must come in pairs, with */ - OP_PLUS, /* the minimizing one second. */ - OP_MINPLUS, /* This first set applies to single characters */ - OP_QUERY, - OP_MINQUERY, - OP_UPTO, /* From 0 to n matches */ - OP_MINUPTO, - OP_EXACT, /* Exactly n matches */ - - OP_NOTSTAR, /* The maximizing and minimizing versions of */ - OP_NOTMINSTAR, /* all these opcodes must come in pairs, with */ - OP_NOTPLUS, /* the minimizing one second. */ - OP_NOTMINPLUS, /* This first set applies to "not" single characters */ - OP_NOTQUERY, - OP_NOTMINQUERY, - OP_NOTUPTO, /* From 0 to n matches */ - OP_NOTMINUPTO, - OP_NOTEXACT, /* Exactly n matches */ - - OP_TYPESTAR, /* The maximizing and minimizing versions of */ - OP_TYPEMINSTAR, /* all these opcodes must come in pairs, with */ - OP_TYPEPLUS, /* the minimizing one second. These codes must */ - OP_TYPEMINPLUS, /* be in exactly the same order as those above. */ - OP_TYPEQUERY, /* This set applies to character types such as \d */ - OP_TYPEMINQUERY, - OP_TYPEUPTO, /* From 0 to n matches */ - OP_TYPEMINUPTO, - OP_TYPEEXACT, /* Exactly n matches */ - - OP_CRSTAR, /* The maximizing and minimizing versions of */ - OP_CRMINSTAR, /* all these opcodes must come in pairs, with */ - OP_CRPLUS, /* the minimizing one second. These codes must */ - OP_CRMINPLUS, /* be in exactly the same order as those above. */ - OP_CRQUERY, /* These are for character classes and back refs */ - OP_CRMINQUERY, - OP_CRRANGE, /* These are different to the three seta above. */ - OP_CRMINRANGE, - - OP_CLASS, /* Match a character class */ - OP_REF, /* Match a back reference */ - OP_RECURSE, /* Match this pattern recursively */ - - OP_ALT, /* Start of alternation */ - OP_KET, /* End of group that doesn't have an unbounded repeat */ - OP_KETRMAX, /* These two must remain together and in this */ - OP_KETRMIN, /* order. They are for groups the repeat for ever. */ - - /* The assertions must come before ONCE and COND */ - - OP_ASSERT, /* Positive lookahead */ - OP_ASSERT_NOT, /* Negative lookahead */ - OP_ASSERTBACK, /* Positive lookbehind */ - OP_ASSERTBACK_NOT, /* Negative lookbehind */ - OP_REVERSE, /* Move pointer back - used in lookbehind assertions */ - - /* ONCE and COND must come after the assertions, with ONCE first, as there's - a test for >= ONCE for a subpattern that isn't an assertion. */ - - OP_ONCE, /* Once matched, don't back up into the subpattern */ - OP_COND, /* Conditional group */ - OP_CREF, /* Used to hold an extraction string number (cond ref) */ - - OP_BRAZERO, /* These two must remain together and in this */ - OP_BRAMINZERO, /* order. */ - - OP_BRANUMBER, /* Used for extracting brackets whose number is greater - than can fit into an opcode. */ - - OP_BRA /* This and greater values are used for brackets that - extract substrings up to a basic limit. After that, - use is made of OP_BRANUMBER. */ -}; - -/* The highest extraction number before we have to start using additional -bytes. (Originally PCRE didn't have support for extraction counts highter than -this number.) The value is limited by the number of opcodes left after OP_BRA, -i.e. 255 - OP_BRA. We actually set it a bit lower to leave room for additional -opcodes. */ - -#define EXTRACT_BASIC_MAX 150 - -/* The texts of compile-time error messages are defined as macros here so that -they can be accessed by the POSIX wrapper and converted into error codes. Yes, -I could have used error codes in the first place, but didn't feel like changing -just to accommodate the POSIX wrapper. */ - -#define ERR1 "\\ at end of pattern" -#define ERR2 "\\c at end of pattern" -#define ERR3 "unrecognized character follows \\" -#define ERR4 "numbers out of order in {} quantifier" -#define ERR5 "number too big in {} quantifier" -#define ERR6 "missing terminating ] for character class" -#define ERR7 "invalid escape sequence in character class" -#define ERR8 "range out of order in character class" -#define ERR9 "nothing to repeat" -#define ERR10 "operand of unlimited repeat could match the empty string" -#define ERR11 "internal error: unexpected repeat" -#define ERR12 "unrecognized character after (?" -#define ERR13 "unused error" -#define ERR14 "missing )" -#define ERR15 "back reference to non-existent subpattern" -#define ERR16 "erroffset passed as NULL" -#define ERR17 "unknown option bit(s) set" -#define ERR18 "missing ) after comment" -#define ERR19 "parentheses nested too deeply" -#define ERR20 "regular expression too large" -#define ERR21 "failed to get memory" -#define ERR22 "unmatched parentheses" -#define ERR23 "internal error: code overflow" -#define ERR24 "unrecognized character after (?<" -#define ERR25 "lookbehind assertion is not fixed length" -#define ERR26 "malformed number after (?(" -#define ERR27 "conditional group contains more than two branches" -#define ERR28 "assertion expected after (?(" -#define ERR29 "(?p must be followed by )" -#define ERR30 "unknown POSIX class name" -#define ERR31 "POSIX collating elements are not supported" -#define ERR32 "this version of PCRE is not compiled with PCRE_UTF8 support" -#define ERR33 "characters with values > 255 are not yet supported in classes" -#define ERR34 "character value in \\x{...} sequence is too large" -#define ERR35 "invalid condition (?(0)" - -/* All character handling must be done as unsigned characters. Otherwise there -are problems with top-bit-set characters and functions such as isspace(). -However, we leave the interface to the outside world as char *, because that -should make things easier for callers. We define a short type for unsigned char -to save lots of typing. I tried "uchar", but it causes problems on Digital -Unix, where it is defined in sys/types, so use "uschar" instead. */ - -typedef unsigned char uschar; - -/* The real format of the start of the pcre block; the actual code vector -runs on as long as necessary after the end. */ - -typedef struct real_pcre { - unsigned long int magic_number; - size_t size; - const unsigned char *tables; - unsigned long int options; - unsigned short int top_bracket; - unsigned short int top_backref; - uschar first_char; - uschar req_char; - uschar code[1]; -} real_pcre; - -/* The real format of the extra block returned by pcre_study(). */ - -typedef struct real_pcre_extra { - uschar options; - uschar start_bits[32]; -} real_pcre_extra; - - -/* Structure for passing "static" information around between the functions -doing the compiling, so that they are thread-safe. */ - -typedef struct compile_data { - const uschar *lcc; /* Points to lower casing table */ - const uschar *fcc; /* Points to case-flipping table */ - const uschar *cbits; /* Points to character type table */ - const uschar *ctypes; /* Points to table of type maps */ -} compile_data; - -/* Structure for passing "static" information around between the functions -doing the matching, so that they are thread-safe. */ - -typedef struct match_data { - int errorcode; /* As it says */ - int *offset_vector; /* Offset vector */ - int offset_end; /* One past the end */ - int offset_max; /* The maximum usable for return data */ - const uschar *lcc; /* Points to lower casing table */ - const uschar *ctypes; /* Points to table of type maps */ - BOOL offset_overflow; /* Set if too many extractions */ - BOOL notbol; /* NOTBOL flag */ - BOOL noteol; /* NOTEOL flag */ - BOOL utf8; /* UTF8 flag */ - BOOL endonly; /* Dollar not before final \n */ - BOOL notempty; /* Empty string match not wanted */ - const uschar *start_pattern; /* For use when recursing */ - const uschar *start_subject; /* Start of the subject string */ - const uschar *end_subject; /* End of the subject string */ - const uschar *start_match; /* Start of this match attempt */ - const uschar *end_match_ptr; /* Subject position at end match */ - int end_offset_top; /* Highwater mark at end of match */ -} match_data; - -/* Bit definitions for entries in the pcre_ctypes table. */ - -#define ctype_space 0x01 -#define ctype_letter 0x02 -#define ctype_digit 0x04 -#define ctype_xdigit 0x08 -#define ctype_word 0x10 /* alphameric or '_' */ -#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ - -/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set -of bits for a class map. Some classes are built by combining these tables. */ - -#define cbit_space 0 /* [:space:] or \s */ -#define cbit_xdigit 32 /* [:xdigit:] */ -#define cbit_digit 64 /* [:digit:] or \d */ -#define cbit_upper 96 /* [:upper:] */ -#define cbit_lower 128 /* [:lower:] */ -#define cbit_word 160 /* [:word:] or \w */ -#define cbit_graph 192 /* [:graph:] */ -#define cbit_print 224 /* [:print:] */ -#define cbit_punct 256 /* [:punct:] */ -#define cbit_cntrl 288 /* [:cntrl:] */ -#define cbit_length 320 /* Length of the cbits table */ - -/* Offsets of the various tables from the base tables pointer, and -total length. */ - -#define lcc_offset 0 -#define fcc_offset 256 -#define cbits_offset 512 -#define ctypes_offset (cbits_offset + cbit_length) -#define tables_length (ctypes_offset + 256) - -/* End of internal.h */ === removed file 'parser/pcre/pcre.c' --- parser/pcre/pcre.c 2006-04-11 21:52:54 +0000 +++ parser/pcre/pcre.c 1970-01-01 00:00:00 +0000 @@ -1,5187 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* -This is a library of functions to support regular expressions whose syntax -and semantics are as close as possible to those of the Perl 5 language. See -the file Tech.Notes for some information on the internals. - -Written by: Philip Hazel - - Copyright (c) 1997-2001 University of Cambridge - ------------------------------------------------------------------------------ -Permission is granted to anyone to use this software for any purpose on any -computer system, and to redistribute it freely, subject to the following -restrictions: - -1. This software 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. - -2. The origin of this software must not be misrepresented, either by - explicit claim or by omission. - -3. Altered versions must be plainly marked as such, and must not be - misrepresented as being the original software. - -4. If PCRE is embedded in any software that is released under the GNU - General Purpose Licence (GPL), then the terms of that licence shall - supersede any condition above with which it is incompatible. ------------------------------------------------------------------------------ -*/ - - -/* Define DEBUG to get debugging output on stdout. */ - -/* #define DEBUG */ - -/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef -inline, and there are *still* stupid compilers about that don't like indented -pre-processor statements. I suppose it's only been 10 years... */ - -#ifdef DEBUG -#define DPRINTF(p) printf p -#else -#define DPRINTF(p) /*nothing*/ -#endif - -/* Include the internals header, which itself includes Standard C headers plus -the external pcre header. */ - -#include "internal.h" - - -/* Allow compilation as C++ source code, should anybody want to do that. */ - -#ifdef __cplusplus -#define class pcre_class -#endif - - -/* Maximum number of items on the nested bracket stacks at compile time. This -applies to the nesting of all kinds of parentheses. It does not limit -un-nested, non-capturing parentheses. This number can be made bigger if -necessary - it is used to dimension one int and one unsigned char vector at -compile time. */ - -#define BRASTACK_SIZE 200 - - -/* The number of bytes in a literal character string above which we can't add -any more is different when UTF-8 characters may be encountered. */ - -#ifdef SUPPORT_UTF8 -#define MAXLIT 250 -#else -#define MAXLIT 255 -#endif - - -/* Min and max values for the common repeats; for the maxima, 0 => infinity */ - -static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; -static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; - -/* Text forms of OP_ values and things, for debugging (not all used) */ - -#ifdef DEBUG -static const char *OP_names[] = { - "End", "\\A", "\\B", "\\b", "\\D", "\\d", - "\\S", "\\s", "\\W", "\\w", "\\Z", "\\z", - "Opt", "^", "$", "Any", "chars", "not", - "*", "*?", "+", "+?", "?", "??", "{", "{", "{", - "*", "*?", "+", "+?", "?", "??", "{", "{", "{", - "*", "*?", "+", "+?", "?", "??", "{", "{", "{", - "*", "*?", "+", "+?", "?", "??", "{", "{", - "class", "Ref", "Recurse", - "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", - "AssertB", "AssertB not", "Reverse", "Once", "Cond", "Cref", - "Brazero", "Braminzero", "Branumber", "Bra" -}; -#endif - -/* Table for handling escaped characters in the range '0'-'z'. Positive returns -are simple data values; negative values are for special things like \d and so -on. Zero means further processing is needed (for things like \x), or the escape -is invalid. */ - -static const short int escapes[] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ - 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ - '@', -ESC_A, -ESC_B, 0, -ESC_D, 0, 0, 0, /* @ - G */ - 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ - 0, 0, 0, -ESC_S, 0, 0, 0, -ESC_W, /* P - W */ - 0, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ - '`', 7, -ESC_b, 0, -ESC_d, ESC_E, ESC_F, 0, /* ` - g */ - 0, 0, 0, 0, 0, 0, ESC_N, 0, /* h - o */ - 0, 0, ESC_R, -ESC_s, ESC_T, 0, 0, -ESC_w, /* p - w */ - 0, 0, -ESC_z /* x - z */ -}; - -/* Tables of names of POSIX character classes and their lengths. The list is -terminated by a zero length entry. The first three must be alpha, upper, lower, -as this is assumed for handling case independence. */ - -static const char *posix_names[] = { - "alpha", "lower", "upper", - "alnum", "ascii", "cntrl", "digit", "graph", - "print", "punct", "space", "word", "xdigit" }; - -static const uschar posix_name_lengths[] = { - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; - -/* Table of class bit maps for each POSIX class; up to three may be combined -to form the class. */ - -static const int posix_class_maps[] = { - cbit_lower, cbit_upper, -1, /* alpha */ - cbit_lower, -1, -1, /* lower */ - cbit_upper, -1, -1, /* upper */ - cbit_digit, cbit_lower, cbit_upper, /* alnum */ - cbit_print, cbit_cntrl, -1, /* ascii */ - cbit_cntrl, -1, -1, /* cntrl */ - cbit_digit, -1, -1, /* digit */ - cbit_graph, -1, -1, /* graph */ - cbit_print, -1, -1, /* print */ - cbit_punct, -1, -1, /* punct */ - cbit_space, -1, -1, /* space */ - cbit_word, -1, -1, /* word */ - cbit_xdigit,-1, -1 /* xdigit */ -}; - - -/* Definition to allow mutual recursion */ - -static BOOL - compile_regex(int, int, int *, uschar **, const uschar **, const char **, - BOOL, int, int *, int *, compile_data *); - -/* Structure for building a chain of data that actually lives on the -stack, for holding the values of the subject pointer at the start of each -subpattern, so as to detect when an empty string has been matched by a -subpattern - to break infinite loops. */ - -typedef struct eptrblock { - struct eptrblock *prev; - const uschar *saved_eptr; -} eptrblock; - -/* Flag bits for the match() function */ - -#define match_condassert 0x01 /* Called to check a condition assertion */ -#define match_isgroup 0x02 /* Set if start of bracketed group */ - - - -/************************************************* -* Global variables * -*************************************************/ - -/* PCRE is thread-clean and doesn't use any global variables in the normal -sense. However, it calls memory allocation and free functions via the two -indirections below, which are can be changed by the caller, but are shared -between all threads. */ - -void *(*pcre_malloc)(size_t) = malloc; -void (*pcre_free)(void *) = free; - - - -/************************************************* -* Macros and tables for character handling * -*************************************************/ - -/* When UTF-8 encoding is being used, a character is no longer just a single -byte. The macros for character handling generate simple sequences when used in -byte-mode, and more complicated ones for UTF-8 characters. */ - -#ifndef SUPPORT_UTF8 -#define GETCHARINC(c, eptr) c = *eptr++; -#define GETCHARLEN(c, eptr, len) c = *eptr; -#define BACKCHAR(eptr) - -#else /* SUPPORT_UTF8 */ - -/* Get the next UTF-8 character, advancing the pointer */ - -#define GETCHARINC(c, eptr) \ - c = *eptr++; \ - if (md->utf8 && (c & 0xc0) == 0xc0) \ - { \ - int a = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ - int s = 6*a; \ - c = (c & utf8_table3[a]) << s; \ - while (a-- > 0) \ - { \ - s -= 6; \ - c |= (*eptr++ & 0x3f) << s; \ - } \ - } - -/* Get the next UTF-8 character, not advancing the pointer, setting length */ - -#define GETCHARLEN(c, eptr, len) \ - c = *eptr; \ - len = 1; \ - if (md->utf8 && (c & 0xc0) == 0xc0) \ - { \ - int i; \ - int a = utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ - int s = 6*a; \ - c = (c & utf8_table3[a]) << s; \ - for (i = 1; i <= a; i++) \ - { \ - s -= 6; \ - c |= (eptr[i] & 0x3f) << s; \ - } \ - len += a; \ - } - -/* If the pointer is not at the start of a character, move it back until -it is. */ - -#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr--; - -#endif - - - -/************************************************* -* Default character tables * -*************************************************/ - -/* A default set of character tables is included in the PCRE binary. Its source -is built by the maketables auxiliary program, which uses the default C ctypes -functions, and put in the file chartables.c. These tables are used by PCRE -whenever the caller of pcre_compile() does not provide an alternate set of -tables. */ - -#include "chartables.c" - - - -#ifdef SUPPORT_UTF8 -/************************************************* -* Tables for UTF-8 support * -*************************************************/ - -/* These are the breakpoints for different numbers of bytes in a UTF-8 -character. */ - -static int utf8_table1[] = { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; - -/* These are the indicator bits and the mask for the data bits to set in the -first byte of a character, indexed by the number of additional bytes. */ - -static int utf8_table2[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; -static int utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; - -/* Table of the number of extra characters, indexed by the first character -masked with 0x3f. The highest number for a valid UTF-8 character is in fact -0x3d. */ - -static uschar utf8_table4[] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; - - -/************************************************* -* Convert character value to UTF-8 * -*************************************************/ - -/* This function takes an integer value in the range 0 - 0x7fffffff -and encodes it as a UTF-8 character in 0 to 6 bytes. - -Arguments: - cvalue the character value - buffer pointer to buffer for result - at least 6 bytes long - -Returns: number of characters placed in the buffer -*/ - -static int -ord2utf8(int cvalue, uschar *buffer) -{ -register int i, j; -for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++) - if (cvalue <= utf8_table1[i]) break; -buffer += i; -for (j = i; j > 0; j--) - { - *buffer-- = 0x80 | (cvalue & 0x3f); - cvalue >>= 6; - } -*buffer = utf8_table2[i] | cvalue; -return i + 1; -} -#endif - - - -/************************************************* -* Return version string * -*************************************************/ - -#define STRING(a) # a -#define XSTRING(s) STRING(s) - -const char * -pcre_version(void) -{ -return XSTRING(PCRE_MAJOR) "." XSTRING(PCRE_MINOR) " " XSTRING(PCRE_DATE); -} - - - - -/************************************************* -* (Obsolete) Return info about compiled pattern * -*************************************************/ - -/* This is the original "info" function. It picks potentially useful data out -of the private structure, but its interface was too rigid. It remains for -backwards compatibility. The public options are passed back in an int - though -the re->options field has been expanded to a long int, all the public options -at the low end of it, and so even on 16-bit systems this will still be OK. -Therefore, I haven't changed the API for pcre_info(). - -Arguments: - external_re points to compiled code - optptr where to pass back the options - first_char where to pass back the first character, - or -1 if multiline and all branches start ^, - or -2 otherwise - -Returns: number of capturing subpatterns - or negative values on error -*/ - -int -pcre_info(const pcre *external_re, int *optptr, int *first_char) -{ -const real_pcre *re = (const real_pcre *)external_re; -if (re == NULL) return PCRE_ERROR_NULL; -if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; -if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS); -if (first_char != NULL) - *first_char = ((re->options & PCRE_FIRSTSET) != 0)? re->first_char : - ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; -return re->top_bracket; -} - - - -/************************************************* -* Return info about compiled pattern * -*************************************************/ - -/* This is a newer "info" function which has an extensible interface so -that additional items can be added compatibly. - -Arguments: - external_re points to compiled code - external_study points to study data, or NULL - what what information is required - where where to put the information - -Returns: 0 if data returned, negative on error -*/ - -int -pcre_fullinfo(const pcre *external_re, const pcre_extra *study_data, int what, - void *where) -{ -const real_pcre *re = (const real_pcre *)external_re; -const real_pcre_extra *study = (const real_pcre_extra *)study_data; - -if (re == NULL || where == NULL) return PCRE_ERROR_NULL; -if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; - -switch (what) - { - case PCRE_INFO_OPTIONS: - *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS; - break; - - case PCRE_INFO_SIZE: - *((size_t *)where) = re->size; - break; - - case PCRE_INFO_CAPTURECOUNT: - *((int *)where) = re->top_bracket; - break; - - case PCRE_INFO_BACKREFMAX: - *((int *)where) = re->top_backref; - break; - - case PCRE_INFO_FIRSTCHAR: - *((int *)where) = - ((re->options & PCRE_FIRSTSET) != 0)? re->first_char : - ((re->options & PCRE_STARTLINE) != 0)? -1 : -2; - break; - - case PCRE_INFO_FIRSTTABLE: - *((const uschar **)where) = - (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)? - study->start_bits : NULL; - break; - - case PCRE_INFO_LASTLITERAL: - *((int *)where) = - ((re->options & PCRE_REQCHSET) != 0)? re->req_char : -1; - break; - - default: return PCRE_ERROR_BADOPTION; - } - -return 0; -} - - - -#ifdef DEBUG -/************************************************* -* Debugging function to print chars * -*************************************************/ - -/* Print a sequence of chars in printable format, stopping at the end of the -subject if the requested. - -Arguments: - p points to characters - length number to print - is_subject TRUE if printing from within md->start_subject - md pointer to matching data block, if is_subject is TRUE - -Returns: nothing -*/ - -static void -pchars(const uschar *p, int length, BOOL is_subject, match_data *md) -{ -int c; -if (is_subject && length > md->end_subject - p) length = md->end_subject - p; -while (length-- > 0) - if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); -} -#endif - - - - -/************************************************* -* Handle escapes * -*************************************************/ - -/* This function is called when a \ has been encountered. It either returns a -positive value for a simple escape such as \n, or a negative value which -encodes one of the more complicated things such as \d. When UTF-8 is enabled, -a positive value greater than 255 may be returned. On entry, ptr is pointing at -the \. On exit, it is on the final character of the escape sequence. - -Arguments: - ptrptr points to the pattern position pointer - errorptr points to the pointer to the error message - bracount number of previous extracting brackets - options the options bits - isclass TRUE if inside a character class - cd pointer to char tables block - -Returns: zero or positive => a data character - negative => a special escape sequence - on error, errorptr is set -*/ - -static int -check_escape(const uschar **ptrptr, const char **errorptr, int bracount, - int options, BOOL isclass, compile_data *cd) -{ -const uschar *ptr = *ptrptr; -int c, i; - -/* If backslash is at the end of the pattern, it's an error. */ - -c = *(++ptr); -if (c == 0) *errorptr = ERR1; - -/* Digits or letters may have special meaning; all others are literals. */ - -else if (c < '0' || c > 'z') {} - -/* Do an initial lookup in a table. A non-zero result is something that can be -returned immediately. Otherwise further processing may be required. */ - -else if ((i = escapes[c - '0']) != 0) c = i; - -/* Escapes that need further processing, or are illegal. */ - -else - { - const uschar *oldptr; - switch (c) - { - /* The handling of escape sequences consisting of a string of digits - starting with one that is not zero is not straightforward. By experiment, - the way Perl works seems to be as follows: - - Outside a character class, the digits are read as a decimal number. If the - number is less than 10, or if there are that many previous extracting - left brackets, then it is a back reference. Otherwise, up to three octal - digits are read to form an escaped byte. Thus \123 is likely to be octal - 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal - value is greater than 377, the least significant 8 bits are taken. Inside a - character class, \ followed by a digit is always an octal number. */ - - case '1': case '2': case '3': case '4': case '5': - case '6': case '7': case '8': case '9': - - if (!isclass) - { - oldptr = ptr; - c -= '0'; - while ((cd->ctypes[ptr[1]] & ctype_digit) != 0) - c = c * 10 + *(++ptr) - '0'; - if (c < 10 || c <= bracount) - { - c = -(ESC_REF + c); - break; - } - ptr = oldptr; /* Put the pointer back and fall through */ - } - - /* Handle an octal number following \. If the first digit is 8 or 9, Perl - generates a binary zero byte and treats the digit as a following literal. - Thus we have to pull back the pointer by one. */ - - if ((c = *ptr) >= '8') - { - ptr--; - c = 0; - break; - } - - /* \0 always starts an octal number, but we may drop through to here with a - larger first octal digit. */ - - case '0': - c -= '0'; - while(i++ < 2 && (cd->ctypes[ptr[1]] & ctype_digit) != 0 && - ptr[1] != '8' && ptr[1] != '9') - c = c * 8 + *(++ptr) - '0'; - c &= 255; /* Take least significant 8 bits */ - break; - - /* \x is complicated when UTF-8 is enabled. \x{ddd} is a character number - which can be greater than 0xff, but only if the ddd are hex digits. */ - - case 'x': -#ifdef SUPPORT_UTF8 - if (ptr[1] == '{' && (options & PCRE_UTF8) != 0) - { - const uschar *pt = ptr + 2; - register int count = 0; - c = 0; - while ((cd->ctypes[*pt] & ctype_xdigit) != 0) - { - count++; - c = c * 16 + cd->lcc[*pt] - - (((cd->ctypes[*pt] & ctype_digit) != 0)? '0' : 'W'); - pt++; - } - if (*pt == '}') - { - if (c < 0 || count > 8) *errorptr = ERR34; - ptr = pt; - break; - } - /* If the sequence of hex digits does not end with '}', then we don't - recognize this construct; fall through to the normal \x handling. */ - } -#endif - - /* Read just a single hex char */ - - c = 0; - while (i++ < 2 && (cd->ctypes[ptr[1]] & ctype_xdigit) != 0) - { - ptr++; - c = c * 16 + cd->lcc[*ptr] - - (((cd->ctypes[*ptr] & ctype_digit) != 0)? '0' : 'W'); - } - break; - - /* Other special escapes not starting with a digit are straightforward */ - - case 'c': - c = *(++ptr); - if (c == 0) - { - *errorptr = ERR2; - return 0; - } - - /* A letter is upper-cased; then the 0x40 bit is flipped */ - - if (c >= 'a' && c <= 'z') c = cd->fcc[c]; - c ^= 0x40; - break; - - /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any - other alphameric following \ is an error if PCRE_EXTRA was set; otherwise, - for Perl compatibility, it is a literal. This code looks a bit odd, but - there used to be some cases other than the default, and there may be again - in future, so I haven't "optimized" it. */ - - default: - if ((options & PCRE_EXTRA) != 0) switch(c) - { - default: - *errorptr = ERR3; - break; - } - break; - } - } - -*ptrptr = ptr; -return c; -} - - - -/************************************************* -* Check for counted repeat * -*************************************************/ - -/* This function is called when a '{' is encountered in a place where it might -start a quantifier. It looks ahead to see if it really is a quantifier or not. -It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} -where the ddds are digits. - -Arguments: - p pointer to the first char after '{' - cd pointer to char tables block - -Returns: TRUE or FALSE -*/ - -static BOOL -is_counted_repeat(const uschar *p, compile_data *cd) -{ -if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE; -while ((cd->ctypes[*p] & ctype_digit) != 0) p++; -if (*p == '}') return TRUE; - -if (*p++ != ',') return FALSE; -if (*p == '}') return TRUE; - -if ((cd->ctypes[*p++] & ctype_digit) == 0) return FALSE; -while ((cd->ctypes[*p] & ctype_digit) != 0) p++; -return (*p == '}'); -} - - - -/************************************************* -* Read repeat counts * -*************************************************/ - -/* Read an item of the form {n,m} and return the values. This is called only -after is_counted_repeat() has confirmed that a repeat-count quantifier exists, -so the syntax is guaranteed to be correct, but we need to check the values. - -Arguments: - p pointer to first char after '{' - minp pointer to int for min - maxp pointer to int for max - returned as -1 if no max - errorptr points to pointer to error message - cd pointer to character tables clock - -Returns: pointer to '}' on success; - current ptr on error, with errorptr set -*/ - -static const uschar * -read_repeat_counts(const uschar *p, int *minp, int *maxp, - const char **errorptr, compile_data *cd) -{ -int min = 0; -int max = -1; - -/* Read the minimum value and do a paranoid check: a negative value indicates -an integer overflow. */ - -while ((cd->ctypes[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0'; - -if (min < 0 || min > 65535) { - *errorptr = ERR5; - return p; -} -/* Read the maximum value if there is one, and again do a paranoid on its size. -Also, max must not be less than min. */ - -if (*p == '}') max = min; else - { - if (*(++p) != '}') - { - max = 0; - while((cd->ctypes[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0'; - if (max < 0 || max > 65535) - { - *errorptr = ERR5; - return p; - } - if (max < min) - { - *errorptr = ERR4; - return p; - } - } - } - -/* Fill in the required variables, and pass back the pointer to the terminating -'}'. */ - -*minp = min; -*maxp = max; -return p; -} - - - -/************************************************* -* Find the fixed length of a pattern * -*************************************************/ - -/* Scan a pattern and compute the fixed length of subject that will match it, -if the length is fixed. This is needed for dealing with backward assertions. - -Arguments: - code points to the start of the pattern (the bracket) - options the compiling options - -Returns: the fixed length, or -1 if there is no fixed length -*/ - -static int -find_fixedlength(uschar *code, int options) -{ -int length = -1; - -register int branchlength = 0; -register uschar *cc = code + 3; - -/* Scan along the opcodes for this branch. If we get to the end of the -branch, check the length against that of the other branches. */ - -for (;;) - { - int d; - register int op = *cc; - if (op >= OP_BRA) op = OP_BRA; - - switch (op) - { - case OP_BRA: - case OP_ONCE: - case OP_COND: - d = find_fixedlength(cc, options); - if (d < 0) return -1; - branchlength += d; - do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); - cc += 3; - break; - - /* Reached end of a branch; if it's a ket it is the end of a nested - call. If it's ALT it is an alternation in a nested call. If it is - END it's the end of the outer call. All can be handled by the same code. */ - - case OP_ALT: - case OP_KET: - case OP_KETRMAX: - case OP_KETRMIN: - case OP_END: - if (length < 0) length = branchlength; - else if (length != branchlength) return -1; - if (*cc != OP_ALT) return length; - cc += 3; - branchlength = 0; - break; - - /* Skip over assertive subpatterns */ - - case OP_ASSERT: - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - do cc += (cc[1] << 8) + cc[2]; while (*cc == OP_ALT); - cc += 3; - break; - - /* Skip over things that don't match chars */ - - case OP_REVERSE: - case OP_BRANUMBER: - case OP_CREF: - cc++; - /* Fall through */ - - case OP_OPT: - cc++; - /* Fall through */ - - case OP_SOD: - case OP_EOD: - case OP_EODN: - case OP_CIRC: - case OP_DOLL: - case OP_NOT_WORD_BOUNDARY: - case OP_WORD_BOUNDARY: - cc++; - break; - - /* Handle char strings. In UTF-8 mode we must count characters, not bytes. - This requires a scan of the string, unfortunately. We assume valid UTF-8 - strings, so all we do is reduce the length by one for byte whose bits are - 10xxxxxx. */ - - case OP_CHARS: - branchlength += *(++cc); -#ifdef SUPPORT_UTF8 - for (d = 1; d <= *cc; d++) - if ((cc[d] & 0xc0) == 0x80) branchlength--; -#endif - cc += *cc + 1; - break; - - /* Handle exact repetitions */ - - case OP_EXACT: - case OP_TYPEEXACT: - branchlength += (cc[1] << 8) + cc[2]; - cc += 4; - break; - - /* Handle single-char matchers */ - - case OP_NOT_DIGIT: - case OP_DIGIT: - case OP_NOT_WHITESPACE: - case OP_WHITESPACE: - case OP_NOT_WORDCHAR: - case OP_WORDCHAR: - case OP_ANY: - branchlength++; - cc++; - break; - - - /* Check a class for variable quantification */ - - case OP_CLASS: - cc += 33; - - switch (*cc) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRQUERY: - case OP_CRMINQUERY: - return -1; - - case OP_CRRANGE: - case OP_CRMINRANGE: - if ((cc[1] << 8) + cc[2] != (cc[3] << 8) + cc[4]) return -1; - branchlength += (cc[1] << 8) + cc[2]; - cc += 5; - break; - - default: - branchlength++; - } - break; - - /* Anything else is variable length */ - - default: - return -1; - } - } -/* Control never gets here */ -} - - - - -/************************************************* -* Check for POSIX class syntax * -*************************************************/ - -/* This function is called when the sequence "[:" or "[." or "[=" is -encountered in a character class. It checks whether this is followed by an -optional ^ and then a sequence of letters, terminated by a matching ":]" or -".]" or "=]". - -Argument: - ptr pointer to the initial [ - endptr where to return the end pointer - cd pointer to compile data - -Returns: TRUE or FALSE -*/ - -static BOOL -check_posix_syntax(const uschar *ptr, const uschar **endptr, compile_data *cd) -{ -int terminator; /* Don't combine these lines; the Solaris cc */ -terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ -if (*(++ptr) == '^') ptr++; -while ((cd->ctypes[*ptr] & ctype_letter) != 0) ptr++; -if (*ptr == terminator && ptr[1] == ']') - { - *endptr = ptr; - return TRUE; - } -return FALSE; -} - - - - -/************************************************* -* Check POSIX class name * -*************************************************/ - -/* This function is called to check the name given in a POSIX-style class entry -such as [:alnum:]. - -Arguments: - ptr points to the first letter - len the length of the name - -Returns: a value representing the name, or -1 if unknown -*/ - -static int -check_posix_name(const uschar *ptr, int len) -{ -register int yield = 0; -while (posix_name_lengths[yield] != 0) - { - if (len == posix_name_lengths[yield] && - strncmp((const char *)ptr, posix_names[yield], len) == 0) return yield; - yield++; - } -return -1; -} - - - - -/************************************************* -* Compile one branch * -*************************************************/ - -/* Scan the pattern, compiling it into the code vector. - -Arguments: - options the option bits - brackets points to number of extracting brackets used - code points to the pointer to the current code point - ptrptr points to the current pattern pointer - errorptr points to pointer to error message - optchanged set to the value of the last OP_OPT item compiled - reqchar set to the last literal character required, else -1 - countlits set to count of mandatory literal characters - cd contains pointers to tables - -Returns: TRUE on success - FALSE, with *errorptr set on error -*/ - -static BOOL -compile_branch(int options, int *brackets, uschar **codeptr, - const uschar **ptrptr, const char **errorptr, int *optchanged, - int *reqchar, int *countlits, compile_data *cd) -{ -int repeat_type, op_type; -int repeat_min, repeat_max; -int bravalue, length; -int greedy_default, greedy_non_default; -int prevreqchar; -int condcount = 0; -int subcountlits = 0; -register int c; -register uschar *code = *codeptr; -uschar *tempcode; -const uschar *ptr = *ptrptr; -const uschar *tempptr; -uschar *previous = NULL; -uschar class[32]; - -/* Set up the default and non-default settings for greediness */ - -greedy_default = ((options & PCRE_UNGREEDY) != 0); -greedy_non_default = greedy_default ^ 1; - -/* Initialize no required char, and count of literals */ - -*reqchar = prevreqchar = -1; -*countlits = 0; - -/* Switch on next character until the end of the branch */ - -for (;; ptr++) - { - BOOL negate_class; - int class_charcount; - int class_lastchar; - int newoptions; - int skipbytes; - int subreqchar; - - c = *ptr; - if ((options & PCRE_EXTENDED) != 0) - { - if ((cd->ctypes[c] & ctype_space) != 0) continue; - if (c == '#') - { - /* The space before the ; is to avoid a warning on a silly compiler - on the Macintosh. */ - while ((c = *(++ptr)) != 0 && c != NEWLINE) ; - continue; - } - } - - switch(c) - { - /* The branch terminates at end of string, |, or ). */ - - case 0: - case '|': - case ')': - *codeptr = code; - *ptrptr = ptr; - return TRUE; - - /* Handle single-character metacharacters */ - - case '^': - previous = NULL; - *code++ = OP_CIRC; - break; - - case '$': - previous = NULL; - *code++ = OP_DOLL; - break; - - case '.': - previous = code; - *code++ = OP_ANY; - break; - - /* Character classes. These always build a 32-byte bitmap of the permitted - characters, except in the special case where there is only one character. - For negated classes, we build the map as usual, then invert it at the end. - */ - - case '[': - previous = code; - *code++ = OP_CLASS; - - /* If the first character is '^', set the negation flag and skip it. */ - - if ((c = *(++ptr)) == '^') - { - negate_class = TRUE; - c = *(++ptr); - } - else negate_class = FALSE; - - /* Keep a count of chars so that we can optimize the case of just a single - character. */ - - class_charcount = 0; - class_lastchar = -1; - - /* Initialize the 32-char bit map to all zeros. We have to build the - map in a temporary bit of store, in case the class contains only 1 - character, because in that case the compiled code doesn't use the - bit map. */ - - memset(class, 0, 32 * sizeof(uschar)); - - /* Process characters until ] is reached. By writing this as a "do" it - means that an initial ] is taken as a data character. */ - - do - { - if (c == 0) - { - *errorptr = ERR6; - goto FAILED; - } - - /* Handle POSIX class names. Perl allows a negation extension of the - form [:^name]. A square bracket that doesn't match the syntax is - treated as a literal. We also recognize the POSIX constructions - [.ch.] and [=ch=] ("collating elements") and fault them, as Perl - 5.6 does. */ - - if (c == '[' && - (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && - check_posix_syntax(ptr, &tempptr, cd)) - { - BOOL local_negate = FALSE; - int posix_class, i; - register const uschar *cbits = cd->cbits; - - if (ptr[1] != ':') - { - *errorptr = ERR31; - goto FAILED; - } - - ptr += 2; - if (*ptr == '^') - { - local_negate = TRUE; - ptr++; - } - - posix_class = check_posix_name(ptr, tempptr - ptr); - if (posix_class < 0) - { - *errorptr = ERR30; - goto FAILED; - } - - /* If matching is caseless, upper and lower are converted to - alpha. This relies on the fact that the class table starts with - alpha, lower, upper as the first 3 entries. */ - - if ((options & PCRE_CASELESS) != 0 && posix_class <= 2) - posix_class = 0; - - /* Or into the map we are building up to 3 of the static class - tables, or their negations. */ - - posix_class *= 3; - for (i = 0; i < 3; i++) - { - int taboffset = posix_class_maps[posix_class + i]; - if (taboffset < 0) break; - if (local_negate) - for (c = 0; c < 32; c++) class[c] |= ~cbits[c+taboffset]; - else - for (c = 0; c < 32; c++) class[c] |= cbits[c+taboffset]; - } - - ptr = tempptr + 1; - class_charcount = 10; /* Set > 1; assumes more than 1 per class */ - continue; - } - - /* Backslash may introduce a single character, or it may introduce one - of the specials, which just set a flag. Escaped items are checked for - validity in the pre-compiling pass. The sequence \b is a special case. - Inside a class (and only there) it is treated as backspace. Elsewhere - it marks a word boundary. Other escapes have preset maps ready to - or into the one we are building. We assume they have more than one - character in them, so set class_count bigger than one. */ - - if (c == '\\') - { - c = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); - if (-c == ESC_b) c = '\b'; - else if (c < 0) - { - register const uschar *cbits = cd->cbits; - class_charcount = 10; - switch (-c) - { - case ESC_d: - for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_digit]; - continue; - - case ESC_D: - for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_digit]; - continue; - - case ESC_w: - for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_word]; - continue; - - case ESC_W: - for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_word]; - continue; - - case ESC_s: - for (c = 0; c < 32; c++) class[c] |= cbits[c+cbit_space]; - continue; - - case ESC_S: - for (c = 0; c < 32; c++) class[c] |= ~cbits[c+cbit_space]; - continue; - - default: - *errorptr = ERR7; - goto FAILED; - } - } - - /* Fall through if single character, but don't at present allow - chars > 255 in UTF-8 mode. */ - -#ifdef SUPPORT_UTF8 - if (c > 255) - { - *errorptr = ERR33; - goto FAILED; - } -#endif - } - - /* A single character may be followed by '-' to form a range. However, - Perl does not permit ']' to be the end of the range. A '-' character - here is treated as a literal. */ - - if (ptr[1] == '-' && ptr[2] != ']') - { - int d; - ptr += 2; - d = *ptr; - - if (d == 0) - { - *errorptr = ERR6; - goto FAILED; - } - - /* The second part of a range can be a single-character escape, but - not any of the other escapes. Perl 5.6 treats a hyphen as a literal - in such circumstances. */ - - if (d == '\\') - { - const uschar *oldptr = ptr; - d = check_escape(&ptr, errorptr, *brackets, options, TRUE, cd); - -#ifdef SUPPORT_UTF8 - if (d > 255) - { - *errorptr = ERR33; - goto FAILED; - } -#endif - /* \b is backslash; any other special means the '-' was literal */ - - if (d < 0) - { - if (d == -ESC_b) d = '\b'; else - { - ptr = oldptr - 2; - goto SINGLE_CHARACTER; /* A few lines below */ - } - } - } - - if (d < c) - { - *errorptr = ERR8; - goto FAILED; - } - - for (; c <= d; c++) - { - class[c/8] |= (1 << (c&7)); - if ((options & PCRE_CASELESS) != 0) - { - int uc = cd->fcc[c]; /* flip case */ - class[uc/8] |= (1 << (uc&7)); - } - class_charcount++; /* in case a one-char range */ - class_lastchar = c; - } - continue; /* Go get the next char in the class */ - } - - /* Handle a lone single character - we can get here for a normal - non-escape char, or after \ that introduces a single character. */ - - SINGLE_CHARACTER: - - class [c/8] |= (1 << (c&7)); - if ((options & PCRE_CASELESS) != 0) - { - c = cd->fcc[c]; /* flip case */ - class[c/8] |= (1 << (c&7)); - } - class_charcount++; - class_lastchar = c; - } - - /* Loop until ']' reached; the check for end of string happens inside the - loop. This "while" is the end of the "do" above. */ - - while ((c = *(++ptr)) != ']'); - - /* If class_charcount is 1 and class_lastchar is not negative, we saw - precisely one character. This doesn't need the whole 32-byte bit map. - We turn it into a 1-character OP_CHAR if it's positive, or OP_NOT if - it's negative. */ - - if (class_charcount == 1 && class_lastchar >= 0) - { - if (negate_class) - { - code[-1] = OP_NOT; - } - else - { - code[-1] = OP_CHARS; - *code++ = 1; - } - *code++ = class_lastchar; - } - - /* Otherwise, negate the 32-byte map if necessary, and copy it into - the code vector. */ - - else - { - if (negate_class) - for (c = 0; c < 32; c++) code[c] = ~class[c]; - else - memcpy(code, class, 32); - code += 32; - } - break; - - /* Various kinds of repeat */ - - case '{': - if (!is_counted_repeat(ptr+1, cd)) goto NORMAL_CHAR; - ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorptr, cd); - if (*errorptr != NULL) goto FAILED; - goto REPEAT; - - case '*': - repeat_min = 0; - repeat_max = -1; - goto REPEAT; - - case '+': - repeat_min = 1; - repeat_max = -1; - goto REPEAT; - - case '?': - repeat_min = 0; - repeat_max = 1; - - REPEAT: - if (previous == NULL) - { - *errorptr = ERR9; - goto FAILED; - } - - /* If the next character is '?' this is a minimizing repeat, by default, - but if PCRE_UNGREEDY is set, it works the other way round. Advance to the - next character. */ - - if (ptr[1] == '?') - { repeat_type = greedy_non_default; ptr++; } - else repeat_type = greedy_default; - - /* If previous was a string of characters, chop off the last one and use it - as the subject of the repeat. If there was only one character, we can - abolish the previous item altogether. A repeat with a zero minimum wipes - out any reqchar setting, backing up to the previous value. We must also - adjust the countlits value. */ - - if (*previous == OP_CHARS) - { - int len = previous[1]; - - if (repeat_min == 0) *reqchar = prevreqchar; - *countlits += repeat_min - 1; - - if (len == 1) - { - c = previous[2]; - code = previous; - } - else - { - c = previous[len+1]; - previous[1]--; - code--; - } - op_type = 0; /* Use single-char op codes */ - goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ - } - - /* If previous was a single negated character ([^a] or similar), we use - one of the special opcodes, replacing it. The code is shared with single- - character repeats by adding a suitable offset into repeat_type. */ - - else if ((int)*previous == OP_NOT) - { - op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ - c = previous[1]; - code = previous; - goto OUTPUT_SINGLE_REPEAT; - } - - /* If previous was a character type match (\d or similar), abolish it and - create a suitable repeat item. The code is shared with single-character - repeats by adding a suitable offset into repeat_type. */ - - else if ((int)*previous < OP_EODN || *previous == OP_ANY) - { - op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ - c = *previous; - code = previous; - - OUTPUT_SINGLE_REPEAT: - - /* If the maximum is zero then the minimum must also be zero; Perl allows - this case, so we do too - by simply omitting the item altogether. */ - - if (repeat_max == 0) goto END_REPEAT; - - /* Combine the op_type with the repeat_type */ - - repeat_type += op_type; - - /* A minimum of zero is handled either as the special case * or ?, or as - an UPTO, with the maximum given. */ - - if (repeat_min == 0) - { - if (repeat_max == -1) *code++ = OP_STAR + repeat_type; - else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; - else - { - *code++ = OP_UPTO + repeat_type; - *code++ = repeat_max >> 8; - *code++ = (repeat_max & 255); - } - } - - /* The case {1,} is handled as the special case + */ - - else if (repeat_min == 1 && repeat_max == -1) - *code++ = OP_PLUS + repeat_type; - - /* The case {n,n} is just an EXACT, while the general case {n,m} is - handled as an EXACT followed by an UPTO. An EXACT of 1 is optimized. */ - - else - { - if (repeat_min != 1) - { - *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ - *code++ = repeat_min >> 8; - *code++ = (repeat_min & 255); - } - - /* If the mininum is 1 and the previous item was a character string, - we either have to put back the item that got cancelled if the string - length was 1, or add the character back onto the end of a longer - string. For a character type nothing need be done; it will just get - put back naturally. Note that the final character is always going to - get added below. */ - - else if (*previous == OP_CHARS) - { - if (code == previous) code += 2; else previous[1]++; - } - - /* For a single negated character we also have to put back the - item that got cancelled. */ - - else if (*previous == OP_NOT) code++; - - /* If the maximum is unlimited, insert an OP_STAR. */ - - if (repeat_max < 0) - { - *code++ = c; - *code++ = OP_STAR + repeat_type; - } - - /* Else insert an UPTO if the max is greater than the min. */ - - else if (repeat_max != repeat_min) - { - *code++ = c; - repeat_max -= repeat_min; - *code++ = OP_UPTO + repeat_type; - *code++ = repeat_max >> 8; - *code++ = (repeat_max & 255); - } - } - - /* The character or character type itself comes last in all cases. */ - - *code++ = c; - } - - /* If previous was a character class or a back reference, we put the repeat - stuff after it, but just skip the item if the repeat was {0,0}. */ - - else if (*previous == OP_CLASS || *previous == OP_REF) - { - if (repeat_max == 0) - { - code = previous; - goto END_REPEAT; - } - if (repeat_min == 0 && repeat_max == -1) - *code++ = OP_CRSTAR + repeat_type; - else if (repeat_min == 1 && repeat_max == -1) - *code++ = OP_CRPLUS + repeat_type; - else if (repeat_min == 0 && repeat_max == 1) - *code++ = OP_CRQUERY + repeat_type; - else - { - *code++ = OP_CRRANGE + repeat_type; - *code++ = repeat_min >> 8; - *code++ = repeat_min & 255; - if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ - *code++ = repeat_max >> 8; - *code++ = repeat_max & 255; - } - } - - /* If previous was a bracket group, we may have to replicate it in certain - cases. */ - - else if ((int)*previous >= OP_BRA || (int)*previous == OP_ONCE || - (int)*previous == OP_COND) - { - register int i; - int ketoffset = 0; - int len = code - previous; - uschar *bralink = NULL; - - /* If the maximum repeat count is unlimited, find the end of the bracket - by scanning through from the start, and compute the offset back to it - from the current code pointer. There may be an OP_OPT setting following - the final KET, so we can't find the end just by going back from the code - pointer. */ - - if (repeat_max == -1) - { - register uschar *ket = previous; - do ket += (ket[1] << 8) + ket[2]; while (*ket != OP_KET); - ketoffset = code - ket; - } - - /* The case of a zero minimum is special because of the need to stick - OP_BRAZERO in front of it, and because the group appears once in the - data, whereas in other cases it appears the minimum number of times. For - this reason, it is simplest to treat this case separately, as otherwise - the code gets far too messy. There are several special subcases when the - minimum is zero. */ - - if (repeat_min == 0) - { - /* If we set up a required char from the bracket, we must back off - to the previous value and reset the countlits value too. */ - - if (subcountlits > 0) - { - *reqchar = prevreqchar; - *countlits -= subcountlits; - } - - /* If the maximum is also zero, we just omit the group from the output - altogether. */ - - if (repeat_max == 0) - { - code = previous; - goto END_REPEAT; - } - - /* If the maximum is 1 or unlimited, we just have to stick in the - BRAZERO and do no more at this point. */ - - if (repeat_max <= 1) - { - memmove(previous+1, previous, len); - code++; - *previous++ = OP_BRAZERO + repeat_type; - } - - /* If the maximum is greater than 1 and limited, we have to replicate - in a nested fashion, sticking OP_BRAZERO before each set of brackets. - The first one has to be handled carefully because it's the original - copy, which has to be moved up. The remainder can be handled by code - that is common with the non-zero minimum case below. We just have to - adjust the value or repeat_max, since one less copy is required. */ - - else - { - int offset; - memmove(previous+4, previous, len); - code += 4; - *previous++ = OP_BRAZERO + repeat_type; - *previous++ = OP_BRA; - - /* We chain together the bracket offset fields that have to be - filled in later when the ends of the brackets are reached. */ - - offset = (bralink == NULL)? 0 : previous - bralink; - bralink = previous; - *previous++ = offset >> 8; - *previous++ = offset & 255; - } - - repeat_max--; - } - - /* If the minimum is greater than zero, replicate the group as many - times as necessary, and adjust the maximum to the number of subsequent - copies that we need. */ - - else - { - for (i = 1; i < repeat_min; i++) - { - memcpy(code, previous, len); - code += len; - } - if (repeat_max > 0) repeat_max -= repeat_min; - } - - /* This code is common to both the zero and non-zero minimum cases. If - the maximum is limited, it replicates the group in a nested fashion, - remembering the bracket starts on a stack. In the case of a zero minimum, - the first one was set up above. In all cases the repeat_max now specifies - the number of additional copies needed. */ - - if (repeat_max >= 0) - { - for (i = repeat_max - 1; i >= 0; i--) - { - *code++ = OP_BRAZERO + repeat_type; - - /* All but the final copy start a new nesting, maintaining the - chain of brackets outstanding. */ - - if (i != 0) - { - int offset; - *code++ = OP_BRA; - offset = (bralink == NULL)? 0 : code - bralink; - bralink = code; - *code++ = offset >> 8; - *code++ = offset & 255; - } - - memcpy(code, previous, len); - code += len; - } - - /* Now chain through the pending brackets, and fill in their length - fields (which are holding the chain links pro tem). */ - - while (bralink != NULL) - { - int oldlinkoffset; - int offset = code - bralink + 1; - uschar *bra = code - offset; - oldlinkoffset = (bra[1] << 8) + bra[2]; - bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; - *code++ = OP_KET; - *code++ = bra[1] = offset >> 8; - *code++ = bra[2] = (offset & 255); - } - } - - /* If the maximum is unlimited, set a repeater in the final copy. We - can't just offset backwards from the current code point, because we - don't know if there's been an options resetting after the ket. The - correct offset was computed above. */ - - else code[-ketoffset] = OP_KETRMAX + repeat_type; - } - - /* Else there's some kind of shambles */ - - else - { - *errorptr = ERR11; - goto FAILED; - } - - /* In all case we no longer have a previous item. */ - - END_REPEAT: - previous = NULL; - break; - - - /* Start of nested bracket sub-expression, or comment or lookahead or - lookbehind or option setting or condition. First deal with special things - that can come after a bracket; all are introduced by ?, and the appearance - of any of them means that this is not a referencing group. They were - checked for validity in the first pass over the string, so we don't have to - check for syntax errors here. */ - - case '(': - newoptions = options; - skipbytes = 0; - - if (*(++ptr) == '?') - { - int set, unset; - int *optset; - - switch (*(++ptr)) - { - case '#': /* Comment; skip to ket */ - ptr++; - while (*ptr != ')') ptr++; - continue; - - case ':': /* Non-extracting bracket */ - bravalue = OP_BRA; - ptr++; - break; - - case '(': - bravalue = OP_COND; /* Conditional group */ - if ((cd->ctypes[*(++ptr)] & ctype_digit) != 0) - { - int condref = *ptr - '0'; - while (*(++ptr) != ')') condref = condref*10 + *ptr - '0'; - if (condref == 0) - { - *errorptr = ERR35; - goto FAILED; - } - ptr++; - code[3] = OP_CREF; - code[4] = condref >> 8; - code[5] = condref & 255; - skipbytes = 3; - } - else ptr--; - break; - - case '=': /* Positive lookahead */ - bravalue = OP_ASSERT; - ptr++; - break; - - case '!': /* Negative lookahead */ - bravalue = OP_ASSERT_NOT; - ptr++; - break; - - case '<': /* Lookbehinds */ - switch (*(++ptr)) - { - case '=': /* Positive lookbehind */ - bravalue = OP_ASSERTBACK; - ptr++; - break; - - case '!': /* Negative lookbehind */ - bravalue = OP_ASSERTBACK_NOT; - ptr++; - break; - - default: /* Syntax error */ - *errorptr = ERR24; - goto FAILED; - } - break; - - case '>': /* One-time brackets */ - bravalue = OP_ONCE; - ptr++; - break; - - case 'R': /* Pattern recursion */ - *code++ = OP_RECURSE; - ptr++; - continue; - - default: /* Option setting */ - set = unset = 0; - optset = &set; - - while (*ptr != ')' && *ptr != ':') - { - switch (*ptr++) - { - case '-': optset = &unset; break; - - case 'i': *optset |= PCRE_CASELESS; break; - case 'm': *optset |= PCRE_MULTILINE; break; - case 's': *optset |= PCRE_DOTALL; break; - case 'x': *optset |= PCRE_EXTENDED; break; - case 'U': *optset |= PCRE_UNGREEDY; break; - case 'X': *optset |= PCRE_EXTRA; break; - - default: - *errorptr = ERR12; - goto FAILED; - } - } - - /* Set up the changed option bits, but don't change anything yet. */ - - newoptions = (options | set) & (~unset); - - /* If the options ended with ')' this is not the start of a nested - group with option changes, so the options change at this level. At top - level there is nothing else to be done (the options will in fact have - been set from the start of compiling as a result of the first pass) but - at an inner level we must compile code to change the ims options if - necessary, and pass the new setting back so that it can be put at the - start of any following branches, and when this group ends, a resetting - item can be compiled. */ - - if (*ptr == ')') - { - if ((options & PCRE_INGROUP) != 0 && - (options & PCRE_IMS) != (newoptions & PCRE_IMS)) - { - *code++ = OP_OPT; - *code++ = *optchanged = newoptions & PCRE_IMS; - } - options = newoptions; /* Change options at this level */ - previous = NULL; /* This item can't be repeated */ - continue; /* It is complete */ - } - - /* If the options ended with ':' we are heading into a nested group - with possible change of options. Such groups are non-capturing and are - not assertions of any kind. All we need to do is skip over the ':'; - the newoptions value is handled below. */ - - bravalue = OP_BRA; - ptr++; - } - } - - /* Else we have a referencing group; adjust the opcode. If the bracket - number is greater than EXTRACT_BASIC_MAX, we set the opcode one higher, and - arrange for the true number to follow later, in an OP_BRANUMBER item. */ - - else - { - if (++(*brackets) > EXTRACT_BASIC_MAX) - { - bravalue = OP_BRA + EXTRACT_BASIC_MAX + 1; - code[3] = OP_BRANUMBER; - code[4] = *brackets >> 8; - code[5] = *brackets & 255; - skipbytes = 3; - } - else bravalue = OP_BRA + *brackets; - } - - /* Process nested bracketed re. Assertions may not be repeated, but other - kinds can be. We copy code into a non-register variable in order to be able - to pass its address because some compilers complain otherwise. Pass in a - new setting for the ims options if they have changed. */ - - previous = (bravalue >= OP_ONCE)? code : NULL; - *code = bravalue; - tempcode = code; - - if (!compile_regex( - options | PCRE_INGROUP, /* Set for all nested groups */ - ((options & PCRE_IMS) != (newoptions & PCRE_IMS))? - newoptions & PCRE_IMS : -1, /* Pass ims options if changed */ - brackets, /* Extracting bracket count */ - &tempcode, /* Where to put code (updated) */ - &ptr, /* Input pointer (updated) */ - errorptr, /* Where to put an error message */ - (bravalue == OP_ASSERTBACK || - bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ - skipbytes, /* Skip over OP_COND/OP_BRANUMBER */ - &subreqchar, /* For possible last char */ - &subcountlits, /* For literal count */ - cd)) /* Tables block */ - goto FAILED; - - /* At the end of compiling, code is still pointing to the start of the - group, while tempcode has been updated to point past the end of the group - and any option resetting that may follow it. The pattern pointer (ptr) - is on the bracket. */ - - /* If this is a conditional bracket, check that there are no more than - two branches in the group. */ - - else if (bravalue == OP_COND) - { - uschar *tc = code; - condcount = 0; - - do { - condcount++; - tc += (tc[1] << 8) | tc[2]; - } - while (*tc != OP_KET); - - if (condcount > 2) - { - *errorptr = ERR27; - goto FAILED; - } - } - - /* Handle updating of the required character. If the subpattern didn't - set one, leave it as it was. Otherwise, update it for normal brackets of - all kinds, forward assertions, and conditions with two branches. Don't - update the literal count for forward assertions, however. If the bracket - is followed by a quantifier with zero repeat, we have to back off. Hence - the definition of prevreqchar and subcountlits outside the main loop so - that they can be accessed for the back off. */ - - if (subreqchar > 0 && - (bravalue >= OP_BRA || bravalue == OP_ONCE || bravalue == OP_ASSERT || - (bravalue == OP_COND && condcount == 2))) - { - prevreqchar = *reqchar; - *reqchar = subreqchar; - if (bravalue != OP_ASSERT) *countlits += subcountlits; - } - - /* Now update the main code pointer to the end of the group. */ - - code = tempcode; - - /* Error if hit end of pattern */ - - if (*ptr != ')') - { - *errorptr = ERR14; - goto FAILED; - } - break; - - /* Check \ for being a real metacharacter; if not, fall through and handle - it as a data character at the start of a string. Escape items are checked - for validity in the pre-compiling pass. */ - - case '\\': - tempptr = ptr; - c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); - - /* Handle metacharacters introduced by \. For ones like \d, the ESC_ values - are arranged to be the negation of the corresponding OP_values. For the - back references, the values are ESC_REF plus the reference number. Only - back references and those types that consume a character may be repeated. - We can test for values between ESC_b and ESC_Z for the latter; this may - have to change if any new ones are ever created. */ - - if (c < 0) - { - if (-c >= ESC_REF) - { - int number = -c - ESC_REF; - previous = code; - *code++ = OP_REF; - *code++ = number >> 8; - *code++ = number & 255; - } - else - { - previous = (-c > ESC_b && -c < ESC_Z)? code : NULL; - *code++ = -c; - } - continue; - } - - /* Data character: reset and fall through */ - - ptr = tempptr; - c = '\\'; - - /* Handle a run of data characters until a metacharacter is encountered. - The first character is guaranteed not to be whitespace or # when the - extended flag is set. */ - - NORMAL_CHAR: - default: - previous = code; - *code = OP_CHARS; - code += 2; - length = 0; - - do - { - if ((options & PCRE_EXTENDED) != 0) - { - if ((cd->ctypes[c] & ctype_space) != 0) continue; - if (c == '#') - { - /* The space before the ; is to avoid a warning on a silly compiler - on the Macintosh. */ - while ((c = *(++ptr)) != 0 && c != NEWLINE) ; - if (c == 0) break; - continue; - } - } - - /* Backslash may introduce a data char or a metacharacter. Escaped items - are checked for validity in the pre-compiling pass. Stop the string - before a metaitem. */ - - if (c == '\\') - { - tempptr = ptr; - c = check_escape(&ptr, errorptr, *brackets, options, FALSE, cd); - if (c < 0) { ptr = tempptr; break; } - - /* If a character is > 127 in UTF-8 mode, we have to turn it into - two or more characters in the UTF-8 encoding. */ - -#ifdef SUPPORT_UTF8 - if (c > 127 && (options & PCRE_UTF8) != 0) - { - uschar buffer[8]; - int len = ord2utf8(c, buffer); - for (c = 0; c < len; c++) *code++ = buffer[c]; - length += len; - continue; - } -#endif - } - - /* Ordinary character or single-char escape */ - - *code++ = c; - length++; - } - - /* This "while" is the end of the "do" above. */ - - while (length < MAXLIT && (cd->ctypes[c = *(++ptr)] & ctype_meta) == 0); - - /* Update the last character and the count of literals */ - - prevreqchar = (length > 1)? code[-2] : *reqchar; - *reqchar = code[-1]; - *countlits += length; - - /* Compute the length and set it in the data vector, and advance to - the next state. */ - - previous[1] = length; - if (length < MAXLIT) ptr--; - break; - } - } /* end of big loop */ - -/* Control never reaches here by falling through, only by a goto for all the -error states. Pass back the position in the pattern so that it can be displayed -to the user for diagnosing the error. */ - -FAILED: -*ptrptr = ptr; -return FALSE; -} - - - - -/************************************************* -* Compile sequence of alternatives * -*************************************************/ - -/* On entry, ptr is pointing past the bracket character, but on return -it points to the closing bracket, or vertical bar, or end of string. -The code variable is pointing at the byte into which the BRA operator has been -stored. If the ims options are changed at the start (for a (?ims: group) or -during any branch, we need to insert an OP_OPT item at the start of every -following branch to ensure they get set correctly at run time, and also pass -the new options into every subsequent branch compile. - -Argument: - options the option bits - optchanged new ims options to set as if (?ims) were at the start, or -1 - for no change - brackets -> int containing the number of extracting brackets used - codeptr -> the address of the current code pointer - ptrptr -> the address of the current pattern pointer - errorptr -> pointer to error message - lookbehind TRUE if this is a lookbehind assertion - skipbytes skip this many bytes at start (for OP_COND, OP_BRANUMBER) - reqchar -> place to put the last required character, or a negative number - countlits -> place to put the shortest literal count of any branch - cd points to the data block with tables pointers - -Returns: TRUE on success -*/ - -static BOOL -compile_regex(int options, int optchanged, int *brackets, uschar **codeptr, - const uschar **ptrptr, const char **errorptr, BOOL lookbehind, int skipbytes, - int *reqchar, int *countlits, compile_data *cd) -{ -const uschar *ptr = *ptrptr; -uschar *code = *codeptr; -uschar *last_branch = code; -uschar *start_bracket = code; -uschar *reverse_count = NULL; -int oldoptions = options & PCRE_IMS; -int branchreqchar, branchcountlits; - -*reqchar = -1; -*countlits = INT_MAX; -code += 3 + skipbytes; - -/* Loop for each alternative branch */ - -for (;;) - { - int length; - - /* Handle change of options */ - - if (optchanged >= 0) - { - *code++ = OP_OPT; - *code++ = optchanged; - options = (options & ~PCRE_IMS) | optchanged; - } - - /* Set up dummy OP_REVERSE if lookbehind assertion */ - - if (lookbehind) - { - *code++ = OP_REVERSE; - reverse_count = code; - *code++ = 0; - *code++ = 0; - } - - /* Now compile the branch */ - - if (!compile_branch(options, brackets, &code, &ptr, errorptr, &optchanged, - &branchreqchar, &branchcountlits, cd)) - { - *ptrptr = ptr; - return FALSE; - } - - /* Fill in the length of the last branch */ - - length = code - last_branch; - last_branch[1] = length >> 8; - last_branch[2] = length & 255; - - /* Save the last required character if all branches have the same; a current - value of -1 means unset, while -2 means "previous branch had no last required - char". */ - - if (*reqchar != -2) - { - if (branchreqchar >= 0) - { - if (*reqchar == -1) *reqchar = branchreqchar; - else if (*reqchar != branchreqchar) *reqchar = -2; - } - else *reqchar = -2; - } - - /* Keep the shortest literal count */ - - if (branchcountlits < *countlits) *countlits = branchcountlits; - DPRINTF(("literal count = %d min=%d\n", branchcountlits, *countlits)); - - /* If lookbehind, check that this branch matches a fixed-length string, - and put the length into the OP_REVERSE item. Temporarily mark the end of - the branch with OP_END. */ - - if (lookbehind) - { - *code = OP_END; - length = find_fixedlength(last_branch, options); - DPRINTF(("fixed length = %d\n", length)); - if (length < 0) - { - *errorptr = ERR25; - *ptrptr = ptr; - return FALSE; - } - reverse_count[0] = (length >> 8); - reverse_count[1] = length & 255; - } - - /* Reached end of expression, either ')' or end of pattern. Insert a - terminating ket and the length of the whole bracketed item, and return, - leaving the pointer at the terminating char. If any of the ims options - were changed inside the group, compile a resetting op-code following. */ - - if (*ptr != '|') - { - length = code - start_bracket; - *code++ = OP_KET; - *code++ = length >> 8; - *code++ = length & 255; - if (optchanged >= 0) - { - *code++ = OP_OPT; - *code++ = oldoptions; - } - *codeptr = code; - *ptrptr = ptr; - return TRUE; - } - - /* Another branch follows; insert an "or" node and advance the pointer. */ - - *code = OP_ALT; - last_branch = code; - code += 3; - ptr++; - } -/* Control never reaches here */ -} - - - - -/************************************************* -* Find first significant op code * -*************************************************/ - -/* This is called by several functions that scan a compiled expression looking -for a fixed first character, or an anchoring op code etc. It skips over things -that do not influence this. For one application, a change of caseless option is -important. - -Arguments: - code pointer to the start of the group - options pointer to external options - optbit the option bit whose changing is significant, or - zero if none are - optstop TRUE to return on option change, otherwise change the options - value and continue - -Returns: pointer to the first significant opcode -*/ - -static const uschar* -first_significant_code(const uschar *code, int *options, int optbit, - BOOL optstop) -{ -for (;;) - { - switch ((int)*code) - { - case OP_OPT: - if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit)) - { - if (optstop) return code; - *options = (int)code[1]; - } - code += 2; - break; - - case OP_CREF: - case OP_BRANUMBER: - code += 3; - break; - - case OP_WORD_BOUNDARY: - case OP_NOT_WORD_BOUNDARY: - code++; - break; - - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - do code += (code[1] << 8) + code[2]; while (*code == OP_ALT); - code += 3; - break; - - default: - return code; - } - } -/* Control never reaches here */ -} - - - - -/************************************************* -* Check for anchored expression * -*************************************************/ - -/* Try to find out if this is an anchored regular expression. Consider each -alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket -all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then -it's anchored. However, if this is a multiline pattern, then only OP_SOD -counts, since OP_CIRC can match in the middle. - -A branch is also implicitly anchored if it starts with .* and DOTALL is set, -because that will try the rest of the pattern at all possible matching points, -so there is no point trying them again. - -Arguments: - code points to start of expression (the bracket) - options points to the options setting - -Returns: TRUE or FALSE -*/ - -static BOOL -is_anchored(register const uschar *code, int *options) -{ -do { - const uschar *scode = first_significant_code(code + 3, options, - PCRE_MULTILINE, FALSE); - register int op = *scode; - if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) - { if (!is_anchored(scode, options)) return FALSE; } - else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR) && - (*options & PCRE_DOTALL) != 0) - { if (scode[1] != OP_ANY) return FALSE; } - else if (op != OP_SOD && - ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) - return FALSE; - code += (code[1] << 8) + code[2]; - } -while (*code == OP_ALT); -return TRUE; -} - - - -/************************************************* -* Check for starting with ^ or .* * -*************************************************/ - -/* This is called to find out if every branch starts with ^ or .* so that -"first char" processing can be done to speed things up in multiline -matching and for non-DOTALL patterns that start with .* (which must start at -the beginning or after \n). - -Argument: points to start of expression (the bracket) -Returns: TRUE or FALSE -*/ - -static BOOL -is_startline(const uschar *code) -{ -do { - const uschar *scode = first_significant_code(code + 3, NULL, 0, FALSE); - register int op = *scode; - if (op >= OP_BRA || op == OP_ASSERT || op == OP_ONCE || op == OP_COND) - { if (!is_startline(scode)) return FALSE; } - else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR) - { if (scode[1] != OP_ANY) return FALSE; } - else if (op != OP_CIRC) return FALSE; - code += (code[1] << 8) + code[2]; - } -while (*code == OP_ALT); -return TRUE; -} - - - -/************************************************* -* Check for fixed first char * -*************************************************/ - -/* Try to find out if there is a fixed first character. This is called for -unanchored expressions, as it speeds up their processing quite considerably. -Consider each alternative branch. If they all start with the same char, or with -a bracket all of whose alternatives start with the same char (recurse ad lib), -then we return that char, otherwise -1. - -Arguments: - code points to start of expression (the bracket) - options pointer to the options (used to check casing changes) - -Returns: -1 or the fixed first char -*/ - -static int -find_firstchar(const uschar *code, int *options) -{ -register int c = -1; -do { - int d; - const uschar *scode = first_significant_code(code + 3, options, - PCRE_CASELESS, TRUE); - register int op = *scode; - - if (op >= OP_BRA) op = OP_BRA; - - switch(op) - { - default: - return -1; - - case OP_BRA: - case OP_ASSERT: - case OP_ONCE: - case OP_COND: - if ((d = find_firstchar(scode, options)) < 0) return -1; - if (c < 0) c = d; else if (c != d) return -1; - break; - - case OP_EXACT: /* Fall through */ - scode++; - - case OP_CHARS: /* Fall through */ - scode++; - - case OP_PLUS: - case OP_MINPLUS: - if (c < 0) c = scode[1]; else if (c != scode[1]) return -1; - break; - } - - code += (code[1] << 8) + code[2]; - } -while (*code == OP_ALT); -return c; -} - - - - - -/************************************************* -* Compile a Regular Expression * -*************************************************/ - -/* This function takes a string and returns a pointer to a block of store -holding a compiled version of the expression. - -Arguments: - pattern the regular expression - options various option bits - errorptr pointer to pointer to error text - erroroffset ptr offset in pattern where error was detected - tables pointer to character tables or NULL - -Returns: pointer to compiled data block, or NULL on error, - with errorptr and erroroffset set -*/ - -pcre * -pcre_compile(const char *pattern, int options, const char **errorptr, - int *erroroffset, const unsigned char *tables) -{ -real_pcre *re; -int length = 3; /* For initial BRA plus length */ -int runlength; -int c, reqchar, countlits; -int bracount = 0; -int top_backref = 0; -int branch_extra = 0; -int branch_newextra; -unsigned int brastackptr = 0; -size_t size; -uschar *code; -const uschar *ptr; -compile_data compile_block; -int brastack[BRASTACK_SIZE]; -uschar bralenstack[BRASTACK_SIZE]; - -#ifdef DEBUG -uschar *code_base, *code_end; -#endif - -/* Can't support UTF8 unless PCRE has been compiled to include the code. */ - -#ifndef SUPPORT_UTF8 -if ((options & PCRE_UTF8) != 0) - { - *errorptr = ERR32; - return NULL; - } -#endif - -/* We can't pass back an error message if errorptr is NULL; I guess the best we -can do is just return NULL. */ - -if (errorptr == NULL) return NULL; -*errorptr = NULL; - -/* However, we can give a message for this error */ - -if (erroroffset == NULL) - { - *errorptr = ERR16; - return NULL; - } -*erroroffset = 0; - -if ((options & ~PUBLIC_OPTIONS) != 0) - { - *errorptr = ERR17; - return NULL; - } - -/* Set up pointers to the individual character tables */ - -if (tables == NULL) tables = pcre_default_tables; -compile_block.lcc = tables + lcc_offset; -compile_block.fcc = tables + fcc_offset; -compile_block.cbits = tables + cbits_offset; -compile_block.ctypes = tables + ctypes_offset; - -/* Reflect pattern for debugging output */ - -DPRINTF(("------------------------------------------------------------------\n")); -DPRINTF(("%s\n", pattern)); - -/* The first thing to do is to make a pass over the pattern to compute the -amount of store required to hold the compiled code. This does not have to be -perfect as long as errors are overestimates. At the same time we can detect any -internal flag settings. Make an attempt to correct for any counted white space -if an "extended" flag setting appears late in the pattern. We can't be so -clever for #-comments. */ - -ptr = (const uschar *)(pattern - 1); -while ((c = *(++ptr)) != 0) - { - int min, max; - int class_charcount; - int bracket_length; - - if ((options & PCRE_EXTENDED) != 0) - { - if ((compile_block.ctypes[c] & ctype_space) != 0) continue; - if (c == '#') - { - /* The space before the ; is to avoid a warning on a silly compiler - on the Macintosh. */ - while ((c = *(++ptr)) != 0 && c != NEWLINE) ; - continue; - } - } - - switch(c) - { - /* A backslashed item may be an escaped "normal" character or a - character type. For a "normal" character, put the pointers and - character back so that tests for whitespace etc. in the input - are done correctly. */ - - case '\\': - { - const uschar *save_ptr = ptr; - c = check_escape(&ptr, errorptr, bracount, options, FALSE, &compile_block); - if (*errorptr != NULL) goto PCRE_ERROR_RETURN; - if (c >= 0) - { - ptr = save_ptr; - c = '\\'; - goto NORMAL_CHAR; - } - } - length++; - - /* A back reference needs an additional 2 bytes, plus either one or 5 - bytes for a repeat. We also need to keep the value of the highest - back reference. */ - - if (c <= -ESC_REF) - { - int refnum = -c - ESC_REF; - if (refnum > top_backref) top_backref = refnum; - length += 2; /* For single back reference */ - if (ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) - { - ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); - if (*errorptr != NULL) goto PCRE_ERROR_RETURN; - if ((min == 0 && (max == 1 || max == -1)) || - (min == 1 && max == -1)) - length++; - else length += 5; - if (ptr[1] == '?') ptr++; - } - } - continue; - - case '^': - case '.': - case '$': - case '*': /* These repeats won't be after brackets; */ - case '+': /* those are handled separately */ - case '?': - length++; - continue; - - /* This covers the cases of repeats after a single char, metachar, class, - or back reference. */ - - case '{': - if (!is_counted_repeat(ptr+1, &compile_block)) goto NORMAL_CHAR; - ptr = read_repeat_counts(ptr+1, &min, &max, errorptr, &compile_block); - if (*errorptr != NULL) goto PCRE_ERROR_RETURN; - if ((min == 0 && (max == 1 || max == -1)) || - (min == 1 && max == -1)) - length++; - else - { - length--; /* Uncount the original char or metachar */ - if (min == 1) length++; else if (min > 0) length += 4; - if (max > 0) length += 4; else length += 2; - } - if (ptr[1] == '?') ptr++; - continue; - - /* An alternation contains an offset to the next branch or ket. If any ims - options changed in the previous branch(es), and/or if we are in a - lookbehind assertion, extra space will be needed at the start of the - branch. This is handled by branch_extra. */ - - case '|': - length += 3 + branch_extra; - continue; - - /* A character class uses 33 characters. Don't worry about character types - that aren't allowed in classes - they'll get picked up during the compile. - A character class that contains only one character uses 2 or 3 bytes, - depending on whether it is negated or not. Notice this where we can. */ - - case '[': - class_charcount = 0; - if (*(++ptr) == '^') ptr++; - do - { - if (*ptr == '\\') - { - int ch = check_escape(&ptr, errorptr, bracount, options, TRUE, - &compile_block); - if (*errorptr != NULL) goto PCRE_ERROR_RETURN; - if (-ch == ESC_b) class_charcount++; else class_charcount = 10; - } - else class_charcount++; - ptr++; - } - while (*ptr != 0 && *ptr != ']'); - - /* Repeats for negated single chars are handled by the general code */ - - if (class_charcount == 1) length += 3; else - { - length += 33; - - /* A repeat needs either 1 or 5 bytes. */ - - if (*ptr != 0 && ptr[1] == '{' && is_counted_repeat(ptr+2, &compile_block)) - { - ptr = read_repeat_counts(ptr+2, &min, &max, errorptr, &compile_block); - if (*errorptr != NULL) goto PCRE_ERROR_RETURN; - if ((min == 0 && (max == 1 || max == -1)) || - (min == 1 && max == -1)) - length++; - else length += 5; - if (ptr[1] == '?') ptr++; - } - } - continue; - - /* Brackets may be genuine groups or special things */ - - case '(': - branch_newextra = 0; - bracket_length = 3; - - /* Handle special forms of bracket, which all start (? */ - - if (ptr[1] == '?') - { - int set, unset; - int *optset; - - switch (c = ptr[2]) - { - /* Skip over comments entirely */ - case '#': - ptr += 3; - while (*ptr != 0 && *ptr != ')') ptr++; - if (*ptr == 0) - { - *errorptr = ERR18; - goto PCRE_ERROR_RETURN; - } - continue; - - /* Non-referencing groups and lookaheads just move the pointer on, and - then behave like a non-special bracket, except that they don't increment - the count of extracting brackets. Ditto for the "once only" bracket, - which is in Perl from version 5.005. */ - - case ':': - case '=': - case '!': - case '>': - ptr += 2; - break; - - /* A recursive call to the regex is an extension, to provide the - facility which can be obtained by $(?p{perl-code}) in Perl 5.6. */ - - case 'R': - if (ptr[3] != ')') - { - *errorptr = ERR29; - goto PCRE_ERROR_RETURN; - } - ptr += 3; - length += 1; - break; - - /* Lookbehinds are in Perl from version 5.005 */ - - case '<': - if (ptr[3] == '=' || ptr[3] == '!') - { - ptr += 3; - branch_newextra = 3; - length += 3; /* For the first branch */ - break; - } - *errorptr = ERR24; - goto PCRE_ERROR_RETURN; - - /* Conditionals are in Perl from version 5.005. The bracket must either - be followed by a number (for bracket reference) or by an assertion - group. */ - - case '(': - if ((compile_block.ctypes[ptr[3]] & ctype_digit) != 0) - { - ptr += 4; - length += 3; - while ((compile_block.ctypes[*ptr] & ctype_digit) != 0) ptr++; - if (*ptr != ')') - { - *errorptr = ERR26; - goto PCRE_ERROR_RETURN; - } - } - else /* An assertion must follow */ - { - ptr++; /* Can treat like ':' as far as spacing is concerned */ - if (ptr[2] != '?' || - (ptr[3] != '=' && ptr[3] != '!' && ptr[3] != '<') ) - { - ptr += 2; /* To get right offset in message */ - *errorptr = ERR28; - goto PCRE_ERROR_RETURN; - } - } - break; - - /* Else loop checking valid options until ) is met. Anything else is an - error. If we are without any brackets, i.e. at top level, the settings - act as if specified in the options, so massage the options immediately. - This is for backward compatibility with Perl 5.004. */ - - default: - set = unset = 0; - optset = &set; - ptr += 2; - - for (;; ptr++) - { - c = *ptr; - switch (c) - { - case 'i': - *optset |= PCRE_CASELESS; - continue; - - case 'm': - *optset |= PCRE_MULTILINE; - continue; - - case 's': - *optset |= PCRE_DOTALL; - continue; - - case 'x': - *optset |= PCRE_EXTENDED; - continue; - - case 'X': - *optset |= PCRE_EXTRA; - continue; - - case 'U': - *optset |= PCRE_UNGREEDY; - continue; - - case '-': - optset = &unset; - continue; - - /* A termination by ')' indicates an options-setting-only item; - this is global at top level; otherwise nothing is done here and - it is handled during the compiling process on a per-bracket-group - basis. */ - - case ')': - if (brastackptr == 0) - { - options = (options | set) & (~unset); - set = unset = 0; /* To save length */ - } - /* Fall through */ - - /* A termination by ':' indicates the start of a nested group with - the given options set. This is again handled at compile time, but - we must allow for compiled space if any of the ims options are - set. We also have to allow for resetting space at the end of - the group, which is why 4 is added to the length and not just 2. - If there are several changes of options within the same group, this - will lead to an over-estimate on the length, but this shouldn't - matter very much. We also have to allow for resetting options at - the start of any alternations, which we do by setting - branch_newextra to 2. Finally, we record whether the case-dependent - flag ever changes within the regex. This is used by the "required - character" code. */ - - case ':': - if (((set|unset) & PCRE_IMS) != 0) - { - length += 4; - branch_newextra = 2; - if (((set|unset) & PCRE_CASELESS) != 0) options |= PCRE_ICHANGED; - } - goto END_OPTIONS; - - /* Unrecognized option character */ - - default: - *errorptr = ERR12; - goto PCRE_ERROR_RETURN; - } - } - - /* If we hit a closing bracket, that's it - this is a freestanding - option-setting. We need to ensure that branch_extra is updated if - necessary. The only values branch_newextra can have here are 0 or 2. - If the value is 2, then branch_extra must either be 2 or 5, depending - on whether this is a lookbehind group or not. */ - - END_OPTIONS: - if (c == ')') - { - if (branch_newextra == 2 && (branch_extra == 0 || branch_extra == 3)) - branch_extra += branch_newextra; - continue; - } - - /* If options were terminated by ':' control comes here. Fall through - to handle the group below. */ - } - } - - /* Extracting brackets must be counted so we can process escapes in a - Perlish way. If the number exceeds EXTRACT_BASIC_MAX we are going to - need an additional 3 bytes of store per extracting bracket. */ - - else - { - bracount++; - if (bracount > EXTRACT_BASIC_MAX) bracket_length += 3; - } - - /* Save length for computing whole length at end if there's a repeat that - requires duplication of the group. Also save the current value of - branch_extra, and start the new group with the new value. If non-zero, this - will either be 2 for a (?imsx: group, or 3 for a lookbehind assertion. */ - - if (brastackptr >= sizeof(brastack)/sizeof(int)) - { - *errorptr = ERR19; - goto PCRE_ERROR_RETURN; - } - - bralenstack[brastackptr] = branch_extra; - branch_extra = branch_newextra; - - brastack[brastackptr++] = length; - length += bracket_length; - continue; - - /* Handle ket. Look for subsequent max/min; for certain sets of values we - have to replicate this bracket up to that many times. If brastackptr is - 0 this is an unmatched bracket which will generate an error, but take care - not to try to access brastack[-1] when computing the length and restoring - the branch_extra value. */ - - case ')': - length += 3; - { - int minval = 1; - int maxval = 1; - int duplength; - - if (brastackptr > 0) - { - duplength = length - brastack[--brastackptr]; - branch_extra = bralenstack[brastackptr]; - } - else duplength = 0; - - /* Leave ptr at the final char; for read_repeat_counts this happens - automatically; for the others we need an increment. */ - - if ((c = ptr[1]) == '{' && is_counted_repeat(ptr+2, &compile_block)) - { - ptr = read_repeat_counts(ptr+2, &minval, &maxval, errorptr, - &compile_block); - if (*errorptr != NULL) goto PCRE_ERROR_RETURN; - } - else if (c == '*') { minval = 0; maxval = -1; ptr++; } - else if (c == '+') { maxval = -1; ptr++; } - else if (c == '?') { minval = 0; ptr++; } - - /* If the minimum is zero, we have to allow for an OP_BRAZERO before the - group, and if the maximum is greater than zero, we have to replicate - maxval-1 times; each replication acquires an OP_BRAZERO plus a nesting - bracket set - hence the 7. */ - - if (minval == 0) - { - length++; - if (maxval > 0) length += (maxval - 1) * (duplength + 7); - } - - /* When the minimum is greater than zero, 1 we have to replicate up to - minval-1 times, with no additions required in the copies. Then, if - there is a limited maximum we have to replicate up to maxval-1 times - allowing for a BRAZERO item before each optional copy and nesting - brackets for all but one of the optional copies. */ - - else - { - length += (minval - 1) * duplength; - if (maxval > minval) /* Need this test as maxval=-1 means no limit */ - length += (maxval - minval) * (duplength + 7) - 6; - } - } - continue; - - /* Non-special character. For a run of such characters the length required - is the number of characters + 2, except that the maximum run length is 255. - We won't get a skipped space or a non-data escape or the start of a # - comment as the first character, so the length can't be zero. */ - - NORMAL_CHAR: - default: - length += 2; - runlength = 0; - do - { - if ((options & PCRE_EXTENDED) != 0) - { - if ((compile_block.ctypes[c] & ctype_space) != 0) continue; - if (c == '#') - { - /* The space before the ; is to avoid a warning on a silly compiler - on the Macintosh. */ - while ((c = *(++ptr)) != 0 && c != NEWLINE) ; - continue; - } - } - - /* Backslash may introduce a data char or a metacharacter; stop the - string before the latter. */ - - if (c == '\\') - { - const uschar *saveptr = ptr; - c = check_escape(&ptr, errorptr, bracount, options, FALSE, - &compile_block); - if (*errorptr != NULL) goto PCRE_ERROR_RETURN; - if (c < 0) { ptr = saveptr; break; } - -#ifdef SUPPORT_UTF8 - if (c > 127 && (options & PCRE_UTF8) != 0) - { - int i; - for (i = 0; i < sizeof(utf8_table1)/sizeof(int); i++) - if (c <= utf8_table1[i]) break; - runlength += i; - } -#endif - } - - /* Ordinary character or single-char escape */ - - runlength++; - } - - /* This "while" is the end of the "do" above. */ - - while (runlength < MAXLIT && - (compile_block.ctypes[c = *(++ptr)] & ctype_meta) == 0); - - ptr--; - length += runlength; - continue; - } - } - -length += 4; /* For final KET and END */ - -if (length > 65539) - { - *errorptr = ERR20; - return NULL; - } - -/* Compute the size of data block needed and get it, either from malloc or -externally provided function. We specify "code[0]" in the offsetof() expression -rather than just "code", because it has been reported that one broken compiler -fails on "code" because it is also an independent variable. It should make no -difference to the value of the offsetof(). */ - -size = length + offsetof(real_pcre, code[0]); -re = (real_pcre *)(pcre_malloc)(size); - -if (re == NULL) - { - *errorptr = ERR21; - return NULL; - } - -/* size of re is sloppily computed, memset to get consistent output */ -memset(re, 0, size); - -/* Put in the magic number, and save the size, options, and table pointer */ - -re->magic_number = MAGIC_NUMBER; -re->size = size; -re->options = options; -re->tables = tables; - -/* Set up a starting, non-extracting bracket, then compile the expression. On -error, *errorptr will be set non-NULL, so we don't need to look at the result -of the function here. */ - -ptr = (const uschar *)pattern; -code = re->code; -*code = OP_BRA; -bracount = 0; -(void)compile_regex(options, -1, &bracount, &code, &ptr, errorptr, FALSE, 0, - &reqchar, &countlits, &compile_block); -re->top_bracket = bracount; -re->top_backref = top_backref; - -/* If not reached end of pattern on success, there's an excess bracket. */ - -if (*errorptr == NULL && *ptr != 0) *errorptr = ERR22; - -/* Fill in the terminating state and check for disastrous overflow, but -if debugging, leave the test till after things are printed out. */ - -*code++ = OP_END; - -#ifndef DEBUG -if (code - re->code > length) *errorptr = ERR23; -#endif - -/* Give an error if there's back reference to a non-existent capturing -subpattern. */ - -if (top_backref > re->top_bracket) *errorptr = ERR15; - -/* Failed to compile */ - -if (*errorptr != NULL) - { - (pcre_free)(re); - PCRE_ERROR_RETURN: - *erroroffset = ptr - (const uschar *)pattern; - return NULL; - } - -/* If the anchored option was not passed, set flag if we can determine that the -pattern is anchored by virtue of ^ characters or \A or anything else (such as -starting with .* when DOTALL is set). - -Otherwise, see if we can determine what the first character has to be, because -that speeds up unanchored matches no end. If not, see if we can set the -PCRE_STARTLINE flag. This is helpful for multiline matches when all branches -start with ^. and also when all branches start with .* for non-DOTALL matches. -*/ - -if ((options & PCRE_ANCHORED) == 0) - { - int temp_options = options; - if (is_anchored(re->code, &temp_options)) - re->options |= PCRE_ANCHORED; - else - { - int ch = find_firstchar(re->code, &temp_options); - if (ch >= 0) - { - re->first_char = ch; - re->options |= PCRE_FIRSTSET; - } - else if (is_startline(re->code)) - re->options |= PCRE_STARTLINE; - } - } - -/* Save the last required character if there are at least two literal -characters on all paths, or if there is no first character setting. */ - -if (reqchar >= 0 && (countlits > 1 || (re->options & PCRE_FIRSTSET) == 0)) - { - re->req_char = reqchar; - re->options |= PCRE_REQCHSET; - } - -/* Print out the compiled data for debugging */ - -#ifdef DEBUG - -printf("Length = %d top_bracket = %d top_backref = %d\n", - length, re->top_bracket, re->top_backref); - -if (re->options != 0) - { - printf("%s%s%s%s%s%s%s%s%s\n", - ((re->options & PCRE_ANCHORED) != 0)? "anchored " : "", - ((re->options & PCRE_CASELESS) != 0)? "caseless " : "", - ((re->options & PCRE_ICHANGED) != 0)? "case state changed " : "", - ((re->options & PCRE_EXTENDED) != 0)? "extended " : "", - ((re->options & PCRE_MULTILINE) != 0)? "multiline " : "", - ((re->options & PCRE_DOTALL) != 0)? "dotall " : "", - ((re->options & PCRE_DOLLAR_ENDONLY) != 0)? "endonly " : "", - ((re->options & PCRE_EXTRA) != 0)? "extra " : "", - ((re->options & PCRE_UNGREEDY) != 0)? "ungreedy " : ""); - } - -if ((re->options & PCRE_FIRSTSET) != 0) - { - if (isprint(re->first_char)) printf("First char = %c\n", re->first_char); - else printf("First char = \\x%02x\n", re->first_char); - } - -if ((re->options & PCRE_REQCHSET) != 0) - { - if (isprint(re->req_char)) printf("Req char = %c\n", re->req_char); - else printf("Req char = \\x%02x\n", re->req_char); - } - -code_end = code; -code_base = code = re->code; - -while (code < code_end) - { - int charlength; - - printf("%3d ", code - code_base); - - if (*code >= OP_BRA) - { - if (*code - OP_BRA > EXTRACT_BASIC_MAX) - printf("%3d Bra extra", (code[1] << 8) + code[2]); - else - printf("%3d Bra %d", (code[1] << 8) + code[2], *code - OP_BRA); - code += 2; - } - - else switch(*code) - { - case OP_OPT: - printf(" %.2x %s", code[1], OP_names[*code]); - code++; - break; - - case OP_CHARS: - charlength = *(++code); - printf("%3d ", charlength); - while (charlength-- > 0) - if (isprint(c = *(++code))) printf("%c", c); else printf("\\x%02x", c); - break; - - case OP_KETRMAX: - case OP_KETRMIN: - case OP_ALT: - case OP_KET: - case OP_ASSERT: - case OP_ASSERT_NOT: - case OP_ASSERTBACK: - case OP_ASSERTBACK_NOT: - case OP_ONCE: - case OP_REVERSE: - case OP_BRANUMBER: - case OP_COND: - case OP_CREF: - printf("%3d %s", (code[1] << 8) + code[2], OP_names[*code]); - code += 2; - break; - - case OP_STAR: - case OP_MINSTAR: - case OP_PLUS: - case OP_MINPLUS: - case OP_QUERY: - case OP_MINQUERY: - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - if (*code >= OP_TYPESTAR) - printf(" %s", OP_names[code[1]]); - else if (isprint(c = code[1])) printf(" %c", c); - else printf(" \\x%02x", c); - printf("%s", OP_names[*code++]); - break; - - case OP_EXACT: - case OP_UPTO: - case OP_MINUPTO: - if (isprint(c = code[3])) printf(" %c{", c); - else printf(" \\x%02x{", c); - if (*code != OP_EXACT) printf("0,"); - printf("%d}", (code[1] << 8) + code[2]); - if (*code == OP_MINUPTO) printf("?"); - code += 3; - break; - - case OP_TYPEEXACT: - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - printf(" %s{", OP_names[code[3]]); - if (*code != OP_TYPEEXACT) printf(","); - printf("%d}", (code[1] << 8) + code[2]); - if (*code == OP_TYPEMINUPTO) printf("?"); - code += 3; - break; - - case OP_NOT: - if (isprint(c = *(++code))) printf(" [^%c]", c); - else printf(" [^\\x%02x]", c); - break; - - case OP_NOTSTAR: - case OP_NOTMINSTAR: - case OP_NOTPLUS: - case OP_NOTMINPLUS: - case OP_NOTQUERY: - case OP_NOTMINQUERY: - if (isprint(c = code[1])) printf(" [^%c]", c); - else printf(" [^\\x%02x]", c); - printf("%s", OP_names[*code++]); - break; - - case OP_NOTEXACT: - case OP_NOTUPTO: - case OP_NOTMINUPTO: - if (isprint(c = code[3])) printf(" [^%c]{", c); - else printf(" [^\\x%02x]{", c); - if (*code != OP_NOTEXACT) printf(","); - printf("%d}", (code[1] << 8) + code[2]); - if (*code == OP_NOTMINUPTO) printf("?"); - code += 3; - break; - - case OP_REF: - printf(" \\%d", (code[1] << 8) | code[2]); - code += 3; - goto CLASS_REF_REPEAT; - - case OP_CLASS: - { - int i, min, max; - code++; - printf(" ["); - - for (i = 0; i < 256; i++) - { - if ((code[i/8] & (1 << (i&7))) != 0) - { - int j; - for (j = i+1; j < 256; j++) - if ((code[j/8] & (1 << (j&7))) == 0) break; - if (i == '-' || i == ']') printf("\\"); - if (isprint(i)) printf("%c", i); else printf("\\x%02x", i); - if (--j > i) - { - printf("-"); - if (j == '-' || j == ']') printf("\\"); - if (isprint(j)) printf("%c", j); else printf("\\x%02x", j); - } - i = j; - } - } - printf("]"); - code += 32; - - CLASS_REF_REPEAT: - - switch(*code) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - printf("%s", OP_names[*code]); - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - min = (code[1] << 8) + code[2]; - max = (code[3] << 8) + code[4]; - if (max == 0) printf("{%d,}", min); - else printf("{%d,%d}", min, max); - if (*code == OP_CRMINRANGE) printf("?"); - code += 4; - break; - - default: - code--; - } - } - break; - - /* Anything else is just a one-node item */ - - default: - printf(" %s", OP_names[*code]); - break; - } - - code++; - printf("\n"); - } -printf("------------------------------------------------------------------\n"); - -/* This check is done here in the debugging case so that the code that -was compiled can be seen. */ - -if (code - re->code > length) - { - *errorptr = ERR23; - (pcre_free)(re); - *erroroffset = ptr - (uschar *)pattern; - return NULL; - } -#endif - -return (pcre *)re; -} - - - -/************************************************* -* Match a back-reference * -*************************************************/ - -/* If a back reference hasn't been set, the length that is passed is greater -than the number of characters left in the string, so the match fails. - -Arguments: - offset index into the offset vector - eptr points into the subject - length length to be matched - md points to match data block - ims the ims flags - -Returns: TRUE if matched -*/ - -static BOOL -match_ref(int offset, register const uschar *eptr, int length, match_data *md, - unsigned long int ims) -{ -const uschar *p = md->start_subject + md->offset_vector[offset]; - -#ifdef DEBUG -if (eptr >= md->end_subject) - printf("matching subject "); -else - { - printf("matching subject "); - pchars(eptr, length, TRUE, md); - } -printf(" against backref "); -pchars(p, length, FALSE, md); -printf("\n"); -#endif - -/* Always fail if not enough characters left */ - -if (length > md->end_subject - eptr) return FALSE; - -/* Separate the caselesss case for speed */ - -if ((ims & PCRE_CASELESS) != 0) - { - while (length-- > 0) - if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; - } -else - { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } - -return TRUE; -} - - - -/************************************************* -* Match from current position * -*************************************************/ - -/* On entry ecode points to the first opcode, and eptr to the first character -in the subject string, while eptrb holds the value of eptr at the start of the -last bracketed group - used for breaking infinite loops matching zero-length -strings. - -Arguments: - eptr pointer in subject - ecode position in code - offset_top current top pointer - md pointer to "static" info for the match - ims current /i, /m, and /s options - eptrb pointer to chain of blocks containing eptr at start of - brackets - for testing for empty matches - flags can contain - match_condassert - this is an assertion condition - match_isgroup - this is the start of a bracketed group - -Returns: TRUE if matched -*/ - -static BOOL -match(register const uschar *eptr, register const uschar *ecode, - int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb, - int flags) -{ -unsigned long int original_ims = ims; /* Save for resetting on ')' */ -eptrblock newptrb; - -/* At the start of a bracketed group, add the current subject pointer to the -stack of such pointers, to be re-instated at the end of the group when we hit -the closing ket. When match() is called in other circumstances, we don't add to -the stack. */ - -if ((flags & match_isgroup) != 0) - { - newptrb.prev = eptrb; - newptrb.saved_eptr = eptr; - eptrb = &newptrb; - } - -/* Now start processing the operations. */ - -for (;;) - { - int op = (int)*ecode; - int min, max, ctype; - register int i; - register int c; - BOOL minimize = FALSE; - - /* Opening capturing bracket. If there is space in the offset vector, save - the current subject position in the working slot at the top of the vector. We - mustn't change the current values of the data slot, because they may be set - from a previous iteration of this group, and be referred to by a reference - inside the group. - - If the bracket fails to match, we need to restore this value and also the - values of the final offsets, in case they were set by a previous iteration of - the same bracket. - - If there isn't enough space in the offset vector, treat this as if it were a - non-capturing bracket. Don't worry about setting the flag for the error case - here; that is handled in the code for KET. */ - - if (op > OP_BRA) - { - int offset; - int number = op - OP_BRA; - - /* For extended extraction brackets (large number), we have to fish out the - number from a dummy opcode at the start. */ - - if (number > EXTRACT_BASIC_MAX) number = (ecode[4] << 8) | ecode[5]; - offset = number << 1; - -#ifdef DEBUG - printf("start bracket %d subject=", number); - pchars(eptr, 16, TRUE, md); - printf("\n"); -#endif - - if (offset < md->offset_max) - { - int save_offset1 = md->offset_vector[offset]; - int save_offset2 = md->offset_vector[offset+1]; - int save_offset3 = md->offset_vector[md->offset_end - number]; - - DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); - md->offset_vector[md->offset_end - number] = eptr - md->start_subject; - - do - { - if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup)) - return TRUE; - ecode += (ecode[1] << 8) + ecode[2]; - } - while (*ecode == OP_ALT); - - DPRINTF(("bracket %d failed\n", number)); - - md->offset_vector[offset] = save_offset1; - md->offset_vector[offset+1] = save_offset2; - md->offset_vector[md->offset_end - number] = save_offset3; - - return FALSE; - } - - /* Insufficient room for saving captured contents */ - - else op = OP_BRA; - } - - /* Other types of node can be handled by a switch */ - - switch(op) - { - case OP_BRA: /* Non-capturing bracket: optimized */ - DPRINTF(("start bracket 0\n")); - do - { - if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup)) - return TRUE; - ecode += (ecode[1] << 8) + ecode[2]; - } - while (*ecode == OP_ALT); - DPRINTF(("bracket 0 failed\n")); - return FALSE; - - /* Conditional group: compilation checked that there are no more than - two branches. If the condition is false, skipping the first branch takes us - past the end if there is only one branch, but that's OK because that is - exactly what going to the ket would do. */ - - case OP_COND: - if (ecode[3] == OP_CREF) /* Condition is extraction test */ - { - int offset = (ecode[4] << 9) | (ecode[5] << 1); /* Doubled ref number */ - return match(eptr, - ecode + ((offset < offset_top && md->offset_vector[offset] >= 0)? - 6 : 3 + (ecode[1] << 8) + ecode[2]), - offset_top, md, ims, eptrb, match_isgroup); - } - - /* The condition is an assertion. Call match() to evaluate it - setting - the final argument TRUE causes it to stop at the end of an assertion. */ - - else - { - if (match(eptr, ecode+3, offset_top, md, ims, NULL, - match_condassert | match_isgroup)) - { - ecode += 3 + (ecode[4] << 8) + ecode[5]; - while (*ecode == OP_ALT) ecode += (ecode[1] << 8) + ecode[2]; - } - else ecode += (ecode[1] << 8) + ecode[2]; - return match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup); - } - /* Control never reaches here */ - - /* Skip over conditional reference or large extraction number data if - encountered. */ - - case OP_CREF: - case OP_BRANUMBER: - ecode += 3; - break; - - /* End of the pattern. If PCRE_NOTEMPTY is set, fail if we have matched - an empty string - recursion will then try other alternatives, if any. */ - - case OP_END: - if (md->notempty && eptr == md->start_match) return FALSE; - md->end_match_ptr = eptr; /* Record where we ended */ - md->end_offset_top = offset_top; /* and how many extracts were taken */ - return TRUE; - - /* Change option settings */ - - case OP_OPT: - ims = ecode[1]; - ecode += 2; - DPRINTF(("ims set to %02lx\n", ims)); - break; - - /* Assertion brackets. Check the alternative branches in turn - the - matching won't pass the KET for an assertion. If any one branch matches, - the assertion is true. Lookbehind assertions have an OP_REVERSE item at the - start of each branch to move the current point backwards, so the code at - this level is identical to the lookahead case. */ - - case OP_ASSERT: - case OP_ASSERTBACK: - do - { - if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup)) break; - ecode += (ecode[1] << 8) + ecode[2]; - } - while (*ecode == OP_ALT); - if (*ecode == OP_KET) return FALSE; - - /* If checking an assertion for a condition, return TRUE. */ - - if ((flags & match_condassert) != 0) return TRUE; - - /* Continue from after the assertion, updating the offsets high water - mark, since extracts may have been taken during the assertion. */ - - do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); - ecode += 3; - offset_top = md->end_offset_top; - continue; - - /* Negative assertion: all branches must fail to match */ - - case OP_ASSERT_NOT: - case OP_ASSERTBACK_NOT: - do - { - if (match(eptr, ecode+3, offset_top, md, ims, NULL, match_isgroup)) - return FALSE; - ecode += (ecode[1] << 8) + ecode[2]; - } - while (*ecode == OP_ALT); - - if ((flags & match_condassert) != 0) return TRUE; - - ecode += 3; - continue; - - /* Move the subject pointer back. This occurs only at the start of - each branch of a lookbehind assertion. If we are too close to the start to - move back, this match function fails. When working with UTF-8 we move - back a number of characters, not bytes. */ - - case OP_REVERSE: -#ifdef SUPPORT_UTF8 - c = (ecode[1] << 8) + ecode[2]; - for (i = 0; i < c; i++) - { - eptr--; - BACKCHAR(eptr) - } -#else - eptr -= (ecode[1] << 8) + ecode[2]; -#endif - - if (eptr < md->start_subject) return FALSE; - ecode += 3; - break; - - /* Recursion matches the current regex, nested. If there are any capturing - brackets started but not finished, we have to save their starting points - and reinstate them after the recursion. However, we don't know how many - such there are (offset_top records the completed total) so we just have - to save all the potential data. There may be up to 99 such values, which - is a bit large to put on the stack, but using malloc for small numbers - seems expensive. As a compromise, the stack is used when there are fewer - than 16 values to store; otherwise malloc is used. A problem is what to do - if the malloc fails ... there is no way of returning to the top level with - an error. Save the top 15 values on the stack, and accept that the rest - may be wrong. */ - - case OP_RECURSE: - { - BOOL rc; - int *save; - int stacksave[15]; - - c = md->offset_max; - - if (c < 16) save = stacksave; else - { - save = (int *)(pcre_malloc)((c+1) * sizeof(int)); - if (save == NULL) - { - save = stacksave; - c = 15; - } - } - - for (i = 1; i <= c; i++) - save[i] = md->offset_vector[md->offset_end - i]; - rc = match(eptr, md->start_pattern, offset_top, md, ims, eptrb, - match_isgroup); - for (i = 1; i <= c; i++) - md->offset_vector[md->offset_end - i] = save[i]; - if (save != stacksave) (pcre_free)(save); - if (!rc) return FALSE; - - /* In case the recursion has set more capturing values, save the final - number, then move along the subject till after the recursive match, - and advance one byte in the pattern code. */ - - offset_top = md->end_offset_top; - eptr = md->end_match_ptr; - ecode++; - } - break; - - /* "Once" brackets are like assertion brackets except that after a match, - the point in the subject string is not moved back. Thus there can never be - a move back into the brackets. Check the alternative branches in turn - the - matching won't pass the KET for this kind of subpattern. If any one branch - matches, we carry on as at the end of a normal bracket, leaving the subject - pointer. */ - - case OP_ONCE: - { - const uschar *prev = ecode; - const uschar *saved_eptr = eptr; - - do - { - if (match(eptr, ecode+3, offset_top, md, ims, eptrb, match_isgroup)) - break; - ecode += (ecode[1] << 8) + ecode[2]; - } - while (*ecode == OP_ALT); - - /* If hit the end of the group (which could be repeated), fail */ - - if (*ecode != OP_ONCE && *ecode != OP_ALT) return FALSE; - - /* Continue as from after the assertion, updating the offsets high water - mark, since extracts may have been taken. */ - - do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); - - offset_top = md->end_offset_top; - eptr = md->end_match_ptr; - - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. If there is an options reset, it will get obeyed in the normal - course of events. */ - - if (*ecode == OP_KET || eptr == saved_eptr) - { - ecode += 3; - break; - } - - /* The repeating kets try the rest of the pattern or restart from the - preceding bracket, in the appropriate order. We need to reset any options - that changed within the bracket before re-running it, so check the next - opcode. */ - - if (ecode[3] == OP_OPT) - { - ims = (ims & ~PCRE_IMS) | ecode[4]; - DPRINTF(("ims set to %02lx at group repeat\n", ims)); - } - - if (*ecode == OP_KETRMIN) - { - if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) || - match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup)) - return TRUE; - } - else /* OP_KETRMAX */ - { - if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) || - match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE; - } - } - return FALSE; - - /* An alternation is the end of a branch; scan along to find the end of the - bracketed group and go to there. */ - - case OP_ALT: - do ecode += (ecode[1] << 8) + ecode[2]; while (*ecode == OP_ALT); - break; - - /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating - that it may occur zero times. It may repeat infinitely, or not at all - - i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper - repeat limits are compiled as a number of copies, with the optional ones - preceded by BRAZERO or BRAMINZERO. */ - - case OP_BRAZERO: - { - const uschar *next = ecode+1; - if (match(eptr, next, offset_top, md, ims, eptrb, match_isgroup)) - return TRUE; - do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); - ecode = next + 3; - } - break; - - case OP_BRAMINZERO: - { - const uschar *next = ecode+1; - do next += (next[1] << 8) + next[2]; while (*next == OP_ALT); - if (match(eptr, next+3, offset_top, md, ims, eptrb, match_isgroup)) - return TRUE; - ecode++; - } - break; - - /* End of a group, repeated or non-repeating. If we are at the end of - an assertion "group", stop matching and return TRUE, but record the - current high water mark for use by positive assertions. Do this also - for the "once" (not-backup up) groups. */ - - case OP_KET: - case OP_KETRMIN: - case OP_KETRMAX: - { - const uschar *prev = ecode - (ecode[1] << 8) - ecode[2]; - const uschar *saved_eptr = eptrb->saved_eptr; - - eptrb = eptrb->prev; /* Back up the stack of bracket start pointers */ - - if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || - *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || - *prev == OP_ONCE) - { - md->end_match_ptr = eptr; /* For ONCE */ - md->end_offset_top = offset_top; - return TRUE; - } - - /* In all other cases except a conditional group we have to check the - group number back at the start and if necessary complete handling an - extraction by setting the offsets and bumping the high water mark. */ - - if (*prev != OP_COND) - { - int offset; - int number = *prev - OP_BRA; - - /* For extended extraction brackets (large number), we have to fish out - the number from a dummy opcode at the start. */ - - if (number > EXTRACT_BASIC_MAX) number = (prev[4] << 8) | prev[5]; - offset = number << 1; - -#ifdef DEBUG - printf("end bracket %d", number); - printf("\n"); -#endif - - if (number > 0) - { - if (offset >= md->offset_max) md->offset_overflow = TRUE; else - { - md->offset_vector[offset] = - md->offset_vector[md->offset_end - number]; - md->offset_vector[offset+1] = eptr - md->start_subject; - if (offset_top <= offset) offset_top = offset + 2; - } - } - } - - /* Reset the value of the ims flags, in case they got changed during - the group. */ - - ims = original_ims; - DPRINTF(("ims reset to %02lx\n", ims)); - - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. If there is an options reset, it will get obeyed in the normal - course of events. */ - - if (*ecode == OP_KET || eptr == saved_eptr) - { - ecode += 3; - break; - } - - /* The repeating kets try the rest of the pattern or restart from the - preceding bracket, in the appropriate order. */ - - if (*ecode == OP_KETRMIN) - { - if (match(eptr, ecode+3, offset_top, md, ims, eptrb, 0) || - match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup)) - return TRUE; - } - else /* OP_KETRMAX */ - { - if (match(eptr, prev, offset_top, md, ims, eptrb, match_isgroup) || - match(eptr, ecode+3, offset_top, md, ims, eptrb, 0)) return TRUE; - } - } - return FALSE; - - /* Start of subject unless notbol, or after internal newline if multiline */ - - case OP_CIRC: - if (md->notbol && eptr == md->start_subject) return FALSE; - if ((ims & PCRE_MULTILINE) != 0) - { - if (eptr != md->start_subject && eptr[-1] != NEWLINE) return FALSE; - ecode++; - break; - } - /* ... else fall through */ - - /* Start of subject assertion */ - - case OP_SOD: - if (eptr != md->start_subject) return FALSE; - ecode++; - break; - - /* Assert before internal newline if multiline, or before a terminating - newline unless endonly is set, else end of subject unless noteol is set. */ - - case OP_DOLL: - if ((ims & PCRE_MULTILINE) != 0) - { - if (eptr < md->end_subject) { if (*eptr != NEWLINE) return FALSE; } - else { if (md->noteol) return FALSE; } - ecode++; - break; - } - else - { - if (md->noteol) return FALSE; - if (!md->endonly) - { - if (eptr < md->end_subject - 1 || - (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE; - - ecode++; - break; - } - } - /* ... else fall through */ - - /* End of subject assertion (\z) */ - - case OP_EOD: - if (eptr < md->end_subject) return FALSE; - ecode++; - break; - - /* End of subject or ending \n assertion (\Z) */ - - case OP_EODN: - if (eptr < md->end_subject - 1 || - (eptr == md->end_subject - 1 && *eptr != NEWLINE)) return FALSE; - ecode++; - break; - - /* Word boundary assertions */ - - case OP_NOT_WORD_BOUNDARY: - case OP_WORD_BOUNDARY: - { - BOOL prev_is_word = (eptr != md->start_subject) && - ((md->ctypes[eptr[-1]] & ctype_word) != 0); - BOOL cur_is_word = (eptr < md->end_subject) && - ((md->ctypes[*eptr] & ctype_word) != 0); - if ((*ecode++ == OP_WORD_BOUNDARY)? - cur_is_word == prev_is_word : cur_is_word != prev_is_word) - return FALSE; - } - break; - - /* Match a single character type; inline for speed */ - - case OP_ANY: - if ((ims & PCRE_DOTALL) == 0 && eptr < md->end_subject && *eptr == NEWLINE) - return FALSE; - if (eptr++ >= md->end_subject) return FALSE; -#ifdef SUPPORT_UTF8 - if (md->utf8) - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; -#endif - ecode++; - break; - - case OP_NOT_DIGIT: - if (eptr >= md->end_subject || - (md->ctypes[*eptr++] & ctype_digit) != 0) - return FALSE; - ecode++; - break; - - case OP_DIGIT: - if (eptr >= md->end_subject || - (md->ctypes[*eptr++] & ctype_digit) == 0) - return FALSE; - ecode++; - break; - - case OP_NOT_WHITESPACE: - if (eptr >= md->end_subject || - (md->ctypes[*eptr++] & ctype_space) != 0) - return FALSE; - ecode++; - break; - - case OP_WHITESPACE: - if (eptr >= md->end_subject || - (md->ctypes[*eptr++] & ctype_space) == 0) - return FALSE; - ecode++; - break; - - case OP_NOT_WORDCHAR: - if (eptr >= md->end_subject || - (md->ctypes[*eptr++] & ctype_word) != 0) - return FALSE; - ecode++; - break; - - case OP_WORDCHAR: - if (eptr >= md->end_subject || - (md->ctypes[*eptr++] & ctype_word) == 0) - return FALSE; - ecode++; - break; - - /* Match a back reference, possibly repeatedly. Look past the end of the - item to see if there is repeat information following. The code is similar - to that for character classes, but repeated for efficiency. Then obey - similar code to character type repeats - written out again for speed. - However, if the referenced string is the empty string, always treat - it as matched, any number of times (otherwise there could be infinite - loops). */ - - case OP_REF: - { - int length; - int offset = (ecode[1] << 9) | (ecode[2] << 1); /* Doubled ref number */ - ecode += 3; /* Advance past item */ - - /* If the reference is unset, set the length to be longer than the amount - of subject left; this ensures that every attempt at a match fails. We - can't just fail here, because of the possibility of quantifiers with zero - minima. */ - - length = (offset >= offset_top || md->offset_vector[offset] < 0)? - md->end_subject - eptr + 1 : - md->offset_vector[offset+1] - md->offset_vector[offset]; - - /* Set up for repetition, or handle the non-repeated case */ - - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - c = *ecode++ - OP_CRSTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*ecode == OP_CRMINRANGE); - min = (ecode[1] << 8) + ecode[2]; - max = (ecode[3] << 8) + ecode[4]; - if (max == 0) max = INT_MAX; - ecode += 5; - break; - - default: /* No repeat follows */ - if (!match_ref(offset, eptr, length, md, ims)) return FALSE; - eptr += length; - continue; /* With the main loop */ - } - - /* If the length of the reference is zero, just continue with the - main loop. */ - - if (length == 0) continue; - - /* First, ensure the minimum number of matches are present. We get back - the length of the reference string explicitly rather than passing the - address of eptr, so that eptr can be a register variable. */ - - for (i = 1; i <= min; i++) - { - if (!match_ref(offset, eptr, length, md, ims)) return FALSE; - eptr += length; - } - - /* If min = max, continue at the same level without recursion. - They are not both allowed to be zero. */ - - if (min == max) continue; - - /* If minimizing, keep trying and advancing the pointer */ - - if (minimize) - { - for (i = min;; i++) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - if (i >= max || !match_ref(offset, eptr, length, md, ims)) - return FALSE; - eptr += length; - } - /* Control never gets here */ - } - - /* If maximizing, find the longest string and work backwards */ - - else - { - const uschar *pp = eptr; - for (i = min; i < max; i++) - { - if (!match_ref(offset, eptr, length, md, ims)) break; - eptr += length; - } - while (eptr >= pp) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - eptr -= length; - } - return FALSE; - } - } - /* Control never gets here */ - - - - /* Match a character class, possibly repeatedly. Look past the end of the - item to see if there is repeat information following. Then obey similar - code to character type repeats - written out again for speed. */ - - case OP_CLASS: - { - const uschar *data = ecode + 1; /* Save for matching */ - ecode += 33; /* Advance past the item */ - - switch (*ecode) - { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - c = *ecode++ - OP_CRSTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*ecode == OP_CRMINRANGE); - min = (ecode[1] << 8) + ecode[2]; - max = (ecode[3] << 8) + ecode[4]; - if (max == 0) max = INT_MAX; - ecode += 5; - break; - - default: /* No repeat follows */ - min = max = 1; - break; - } - - /* First, ensure the minimum number of matches are present. */ - - for (i = 1; i <= min; i++) - { - if (eptr >= md->end_subject) return FALSE; - GETCHARINC(c, eptr) /* Get character; increment eptr */ - -#ifdef SUPPORT_UTF8 - /* We do not yet support class members > 255 */ - if (c > 255) return FALSE; -#endif - - if ((data[c/8] & (1 << (c&7))) != 0) continue; - return FALSE; - } - - /* If max == min we can continue with the main loop without the - need to recurse. */ - - if (min == max) continue; - - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ - - if (minimize) - { - for (i = min;; i++) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - if (i >= max || eptr >= md->end_subject) return FALSE; - GETCHARINC(c, eptr) /* Get character; increment eptr */ - -#ifdef SUPPORT_UTF8 - /* We do not yet support class members > 255 */ - if (c > 255) return FALSE; -#endif - if ((data[c/8] & (1 << (c&7))) != 0) continue; - return FALSE; - } - /* Control never gets here */ - } - - /* If maximizing, find the longest possible run, then work backwards. */ - - else - { - const uschar *pp = eptr; - int len = 1; - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject) break; - GETCHARLEN(c, eptr, len) /* Get character, set length if UTF-8 */ - -#ifdef SUPPORT_UTF8 - /* We do not yet support class members > 255 */ - if (c > 255) break; -#endif - if ((data[c/8] & (1 << (c&7))) == 0) break; - eptr += len; - } - - while (eptr >= pp) - { - if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - -#ifdef SUPPORT_UTF8 - BACKCHAR(eptr) -#endif - } - return FALSE; - } - } - /* Control never gets here */ - - /* Match a run of characters */ - - case OP_CHARS: - { - register int length = ecode[1]; - ecode += 2; - -#ifdef DEBUG /* Sigh. Some compilers never learn. */ - if (eptr >= md->end_subject) - printf("matching subject against pattern "); - else - { - printf("matching subject "); - pchars(eptr, length, TRUE, md); - printf(" against pattern "); - } - pchars(ecode, length, FALSE, md); - printf("\n"); -#endif - - if (length > md->end_subject - eptr) return FALSE; - if ((ims & PCRE_CASELESS) != 0) - { - while (length-- > 0) - if (md->lcc[*ecode++] != md->lcc[*eptr++]) - return FALSE; - } - else - { - while (length-- > 0) if (*ecode++ != *eptr++) return FALSE; - } - } - break; - - /* Match a single character repeatedly; different opcodes share code. */ - - case OP_EXACT: - min = max = (ecode[1] << 8) + ecode[2]; - ecode += 3; - goto REPEATCHAR; - - case OP_UPTO: - case OP_MINUPTO: - min = 0; - max = (ecode[1] << 8) + ecode[2]; - minimize = *ecode == OP_MINUPTO; - ecode += 3; - goto REPEATCHAR; - - case OP_STAR: - case OP_MINSTAR: - case OP_PLUS: - case OP_MINPLUS: - case OP_QUERY: - case OP_MINQUERY: - c = *ecode++ - OP_STAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single-character matches. We can give - up quickly if there are fewer than the minimum number of characters left in - the subject. */ - - REPEATCHAR: - if (min > md->end_subject - eptr) return FALSE; - c = *ecode++; - - /* The code is duplicated for the caseless and caseful cases, for speed, - since matching characters is likely to be quite common. First, ensure the - minimum number of matches are present. If min = max, continue at the same - level without recursing. Otherwise, if minimizing, keep trying the rest of - the expression and advancing one matching character if failing, up to the - maximum. Alternatively, if maximizing, find the maximum number of - characters and work backwards. */ - - DPRINTF(("matching %c{%d,%d} against subject %.*s\n", c, min, max, - max, eptr)); - - if ((ims & PCRE_CASELESS) != 0) - { - c = md->lcc[c]; - for (i = 1; i <= min; i++) - if (c != md->lcc[*eptr++]) return FALSE; - if (min == max) continue; - if (minimize) - { - for (i = min;; i++) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - if (i >= max || eptr >= md->end_subject || - c != md->lcc[*eptr++]) - return FALSE; - } - /* Control never gets here */ - } - else - { - const uschar *pp = eptr; - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || c != md->lcc[*eptr]) break; - eptr++; - } - while (eptr >= pp) - if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - return FALSE; - } - /* Control never gets here */ - } - - /* Caseful comparisons */ - - else - { - for (i = 1; i <= min; i++) if (c != *eptr++) return FALSE; - if (min == max) continue; - if (minimize) - { - for (i = min;; i++) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - if (i >= max || eptr >= md->end_subject || c != *eptr++) return FALSE; - } - /* Control never gets here */ - } - else - { - const uschar *pp = eptr; - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || c != *eptr) break; - eptr++; - } - while (eptr >= pp) - if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - return FALSE; - } - } - /* Control never gets here */ - - /* Match a negated single character */ - - case OP_NOT: - if (eptr >= md->end_subject) return FALSE; - ecode++; - if ((ims & PCRE_CASELESS) != 0) - { - if (md->lcc[*ecode++] == md->lcc[*eptr++]) return FALSE; - } - else - { - if (*ecode++ == *eptr++) return FALSE; - } - break; - - /* Match a negated single character repeatedly. This is almost a repeat of - the code for a repeated single character, but I haven't found a nice way of - commoning these up that doesn't require a test of the positive/negative - option for each character match. Maybe that wouldn't add very much to the - time taken, but character matching *is* what this is all about... */ - - case OP_NOTEXACT: - min = max = (ecode[1] << 8) + ecode[2]; - ecode += 3; - goto REPEATNOTCHAR; - - case OP_NOTUPTO: - case OP_NOTMINUPTO: - min = 0; - max = (ecode[1] << 8) + ecode[2]; - minimize = *ecode == OP_NOTMINUPTO; - ecode += 3; - goto REPEATNOTCHAR; - - case OP_NOTSTAR: - case OP_NOTMINSTAR: - case OP_NOTPLUS: - case OP_NOTMINPLUS: - case OP_NOTQUERY: - case OP_NOTMINQUERY: - c = *ecode++ - OP_NOTSTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single-character matches. We can give - up quickly if there are fewer than the minimum number of characters left in - the subject. */ - - REPEATNOTCHAR: - if (min > md->end_subject - eptr) return FALSE; - c = *ecode++; - - /* The code is duplicated for the caseless and caseful cases, for speed, - since matching characters is likely to be quite common. First, ensure the - minimum number of matches are present. If min = max, continue at the same - level without recursing. Otherwise, if minimizing, keep trying the rest of - the expression and advancing one matching character if failing, up to the - maximum. Alternatively, if maximizing, find the maximum number of - characters and work backwards. */ - - DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", c, min, max, - max, eptr)); - - if ((ims & PCRE_CASELESS) != 0) - { - c = md->lcc[c]; - for (i = 1; i <= min; i++) - if (c == md->lcc[*eptr++]) return FALSE; - if (min == max) continue; - if (minimize) - { - for (i = min;; i++) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - if (i >= max || eptr >= md->end_subject || - c == md->lcc[*eptr++]) - return FALSE; - } - /* Control never gets here */ - } - else - { - const uschar *pp = eptr; - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || c == md->lcc[*eptr]) break; - eptr++; - } - while (eptr >= pp) - if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - return FALSE; - } - /* Control never gets here */ - } - - /* Caseful comparisons */ - - else - { - for (i = 1; i <= min; i++) if (c == *eptr++) return FALSE; - if (min == max) continue; - if (minimize) - { - for (i = min;; i++) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - if (i >= max || eptr >= md->end_subject || c == *eptr++) return FALSE; - } - /* Control never gets here */ - } - else - { - const uschar *pp = eptr; - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || c == *eptr) break; - eptr++; - } - while (eptr >= pp) - if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; - return FALSE; - } - } - /* Control never gets here */ - - /* Match a single character type repeatedly; several different opcodes - share code. This is very similar to the code for single characters, but we - repeat it in the interests of efficiency. */ - - case OP_TYPEEXACT: - min = max = (ecode[1] << 8) + ecode[2]; - minimize = TRUE; - ecode += 3; - goto REPEATTYPE; - - case OP_TYPEUPTO: - case OP_TYPEMINUPTO: - min = 0; - max = (ecode[1] << 8) + ecode[2]; - minimize = *ecode == OP_TYPEMINUPTO; - ecode += 3; - goto REPEATTYPE; - - case OP_TYPESTAR: - case OP_TYPEMINSTAR: - case OP_TYPEPLUS: - case OP_TYPEMINPLUS: - case OP_TYPEQUERY: - case OP_TYPEMINQUERY: - c = *ecode++ - OP_TYPESTAR; - minimize = (c & 1) != 0; - min = rep_min[c]; /* Pick up values from tables; */ - max = rep_max[c]; /* zero for max => infinity */ - if (max == 0) max = INT_MAX; - - /* Common code for all repeated single character type matches */ - - REPEATTYPE: - ctype = *ecode++; /* Code for the character type */ - - /* First, ensure the minimum number of matches are present. Use inline - code for maximizing the speed, and do the type test once at the start - (i.e. keep it out of the loop). Also we can test that there are at least - the minimum number of bytes before we start, except when doing '.' in - UTF8 mode. Leave the test in in all cases; in the special case we have - to test after each character. */ - - if (min > md->end_subject - eptr) return FALSE; - if (min > 0) switch(ctype) - { - case OP_ANY: -#ifdef SUPPORT_UTF8 - if (md->utf8) - { - for (i = 1; i <= min; i++) - { - if (eptr >= md->end_subject || - (*eptr++ == NEWLINE && (ims & PCRE_DOTALL) == 0)) - return FALSE; - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; - } - break; - } -#endif - /* Non-UTF8 can be faster */ - if ((ims & PCRE_DOTALL) == 0) - { for (i = 1; i <= min; i++) if (*eptr++ == NEWLINE) return FALSE; } - else eptr += min; - break; - - case OP_NOT_DIGIT: - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_digit) != 0) return FALSE; - break; - - case OP_DIGIT: - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_digit) == 0) return FALSE; - break; - - case OP_NOT_WHITESPACE: - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_space) != 0) return FALSE; - break; - - case OP_WHITESPACE: - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_space) == 0) return FALSE; - break; - - case OP_NOT_WORDCHAR: - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_word) != 0) - return FALSE; - break; - - case OP_WORDCHAR: - for (i = 1; i <= min; i++) - if ((md->ctypes[*eptr++] & ctype_word) == 0) - return FALSE; - break; - } - - /* If min = max, continue at the same level without recursing */ - - if (min == max) continue; - - /* If minimizing, we have to test the rest of the pattern before each - subsequent match. */ - - if (minimize) - { - for (i = min;; i++) - { - if (match(eptr, ecode, offset_top, md, ims, eptrb, 0)) return TRUE; - if (i >= max || eptr >= md->end_subject) return FALSE; - - c = *eptr++; - switch(ctype) - { - case OP_ANY: - if ((ims & PCRE_DOTALL) == 0 && c == NEWLINE) return FALSE; -#ifdef SUPPORT_UTF8 - if (md->utf8) - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; -#endif - break; - - case OP_NOT_DIGIT: - if ((md->ctypes[c] & ctype_digit) != 0) return FALSE; - break; - - case OP_DIGIT: - if ((md->ctypes[c] & ctype_digit) == 0) return FALSE; - break; - - case OP_NOT_WHITESPACE: - if ((md->ctypes[c] & ctype_space) != 0) return FALSE; - break; - - case OP_WHITESPACE: - if ((md->ctypes[c] & ctype_space) == 0) return FALSE; - break; - - case OP_NOT_WORDCHAR: - if ((md->ctypes[c] & ctype_word) != 0) return FALSE; - break; - - case OP_WORDCHAR: - if ((md->ctypes[c] & ctype_word) == 0) return FALSE; - break; - } - } - /* Control never gets here */ - } - - /* If maximizing it is worth using inline code for speed, doing the type - test once at the start (i.e. keep it out of the loop). */ - - else - { - const uschar *pp = eptr; - switch(ctype) - { - case OP_ANY: - - /* Special code is required for UTF8, but when the maximum is unlimited - we don't need it. */ - -#ifdef SUPPORT_UTF8 - if (md->utf8 && max < INT_MAX) - { - if ((ims & PCRE_DOTALL) == 0) - { - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || *eptr++ == NEWLINE) break; - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; - } - } - else - { - for (i = min; i < max; i++) - { - eptr++; - while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; - } - } - break; - } -#endif - /* Non-UTF8 can be faster */ - if ((ims & PCRE_DOTALL) == 0) - { - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || *eptr == NEWLINE) break; - eptr++; - } - } - else - { - c = max - min; - if (c > md->end_subject - eptr) c = md->end_subject - eptr; - eptr += c; - } - break; - - case OP_NOT_DIGIT: - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0) - break; - eptr++; - } - break; - - case OP_DIGIT: - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0) - break; - eptr++; - } - break; - - case OP_NOT_WHITESPACE: - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0) - break; - eptr++; - } - break; - - case OP_WHITESPACE: - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0) - break; - eptr++; - } - break; - - case OP_NOT_WORDCHAR: - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0) - break; - eptr++; - } - break; - - case OP_WORDCHAR: - for (i = min; i < max; i++) - { - if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0) - break; - eptr++; - } - break; - } - - while (eptr >= pp) - { - if (match(eptr--, ecode, offset_top, md, ims, eptrb, 0)) - return TRUE; -#ifdef SUPPORT_UTF8 - if (md->utf8) - while (eptr > pp && (*eptr & 0xc0) == 0x80) eptr--; -#endif - } - return FALSE; - } - /* Control never gets here */ - - /* There's been some horrible disaster. */ - - default: - DPRINTF(("Unknown opcode %d\n", *ecode)); - md->errorcode = PCRE_ERROR_UNKNOWN_NODE; - return FALSE; - } - - /* Do not stick any code in here without much thought; it is assumed - that "continue" in the code above comes out to here to repeat the main - loop. */ - - } /* End of main loop */ -/* Control never reaches here */ -} - - - - -/************************************************* -* Execute a Regular Expression * -*************************************************/ - -/* This function applies a compiled re to a subject string and picks out -portions of the string if it matches. Two elements in the vector are set for -each substring: the offsets to the start and end of the substring. - -Arguments: - external_re points to the compiled expression - external_extra points to "hints" from pcre_study() or is NULL - subject points to the subject string - length length of subject string (may contain binary zeros) - start_offset where to start in the subject string - options option bits - offsets points to a vector of ints to be filled in with offsets - offsetcount the number of elements in the vector - -Returns: > 0 => success; value is the number of elements filled in - = 0 => success, but offsets is not big enough - -1 => failed to match - < -1 => some kind of unexpected problem -*/ - -int -pcre_exec(const pcre *external_re, const pcre_extra *external_extra, - const char *subject, int length, int start_offset, int options, int *offsets, - int offsetcount) -{ -int resetcount, ocount; -int first_char = -1; -int req_char = -1; -int req_char2 = -1; -unsigned long int ims = 0; -match_data match_block; -const uschar *start_bits = NULL; -const uschar *start_match = (const uschar *)subject + start_offset; -const uschar *end_subject; -const uschar *req_char_ptr = start_match - 1; -const real_pcre *re = (const real_pcre *)external_re; -const real_pcre_extra *extra = (const real_pcre_extra *)external_extra; -BOOL using_temporary_offsets = FALSE; -BOOL anchored; -BOOL startline; - -if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; - -if (re == NULL || subject == NULL || - (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; -if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; - -anchored = ((re->options | options) & PCRE_ANCHORED) != 0; -startline = (re->options & PCRE_STARTLINE) != 0; - -match_block.start_pattern = re->code; -match_block.start_subject = (const uschar *)subject; -match_block.end_subject = match_block.start_subject + length; -end_subject = match_block.end_subject; - -match_block.endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; -match_block.utf8 = (re->options & PCRE_UTF8) != 0; - -match_block.notbol = (options & PCRE_NOTBOL) != 0; -match_block.noteol = (options & PCRE_NOTEOL) != 0; -match_block.notempty = (options & PCRE_NOTEMPTY) != 0; - -match_block.errorcode = PCRE_ERROR_NOMATCH; /* Default error */ - -match_block.lcc = re->tables + lcc_offset; -match_block.ctypes = re->tables + ctypes_offset; - -/* The ims options can vary during the matching as a result of the presence -of (?ims) items in the pattern. They are kept in a local variable so that -restoring at the exit of a group is easy. */ - -ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL); - -/* If the expression has got more back references than the offsets supplied can -hold, we get a temporary bit of working store to use during the matching. -Otherwise, we can use the vector supplied, rounding down its size to a multiple -of 3. */ - -ocount = offsetcount - (offsetcount % 3); - -if (re->top_backref > 0 && re->top_backref >= ocount/3) - { - ocount = re->top_backref * 3 + 3; - match_block.offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int)); - if (match_block.offset_vector == NULL) return PCRE_ERROR_NOMEMORY; - using_temporary_offsets = TRUE; - DPRINTF(("Got memory to hold back references\n")); - } -else match_block.offset_vector = offsets; - -match_block.offset_end = ocount; -match_block.offset_max = (2*ocount)/3; -match_block.offset_overflow = FALSE; - -/* Compute the minimum number of offsets that we need to reset each time. Doing -this makes a huge difference to execution time when there aren't many brackets -in the pattern. */ - -resetcount = 2 + re->top_bracket * 2; -if (resetcount > offsetcount) resetcount = ocount; - -/* Reset the working variable associated with each extraction. These should -never be used unless previously set, but they get saved and restored, and so we -initialize them to avoid reading uninitialized locations. */ - -if (match_block.offset_vector != NULL) - { - register int *iptr = match_block.offset_vector + ocount; - register int *iend = iptr - resetcount/2 + 1; - while (--iptr >= iend) *iptr = -1; - } - -/* Set up the first character to match, if available. The first_char value is -never set for an anchored regular expression, but the anchoring may be forced -at run time, so we have to test for anchoring. The first char may be unset for -an unanchored pattern, of course. If there's no first char and the pattern was -studied, there may be a bitmap of possible first characters. */ - -if (!anchored) - { - if ((re->options & PCRE_FIRSTSET) != 0) - { - first_char = re->first_char; - if ((ims & PCRE_CASELESS) != 0) first_char = match_block.lcc[first_char]; - } - else - if (!startline && extra != NULL && - (extra->options & PCRE_STUDY_MAPPED) != 0) - start_bits = extra->start_bits; - } - -/* For anchored or unanchored matches, there may be a "last known required -character" set. If the PCRE_CASELESS is set, implying that the match starts -caselessly, or if there are any changes of this flag within the regex, set up -both cases of the character. Otherwise set the two values the same, which will -avoid duplicate testing (which takes significant time). This covers the vast -majority of cases. It will be suboptimal when the case flag changes in a regex -and the required character in fact is caseful. */ - -if ((re->options & PCRE_REQCHSET) != 0) - { - req_char = re->req_char; - req_char2 = ((re->options & (PCRE_CASELESS | PCRE_ICHANGED)) != 0)? - (re->tables + fcc_offset)[req_char] : req_char; - } - -/* Loop for handling unanchored repeated matching attempts; for anchored regexs -the loop runs just once. */ - -do - { - int rc; - register int *iptr = match_block.offset_vector; - register int *iend = iptr + resetcount; - - /* Reset the maximum number of extractions we might see. */ - - while (iptr < iend) *iptr++ = -1; - - /* Advance to a unique first char if possible */ - - if (first_char >= 0) - { - if ((ims & PCRE_CASELESS) != 0) - while (start_match < end_subject && - match_block.lcc[*start_match] != first_char) - start_match++; - else - while (start_match < end_subject && *start_match != first_char) - start_match++; - } - - /* Or to just after \n for a multiline match if possible */ - - else if (startline) - { - if (start_match > match_block.start_subject + start_offset) - { - while (start_match < end_subject && start_match[-1] != NEWLINE) - start_match++; - } - } - - /* Or to a non-unique first char after study */ - - else if (start_bits != NULL) - { - while (start_match < end_subject) - { - register int c = *start_match; - if ((start_bits[c/8] & (1 << (c&7))) == 0) start_match++; else break; - } - } - -#ifdef DEBUG /* Sigh. Some compilers never learn. */ - printf(">>>> Match against: "); - pchars(start_match, end_subject - start_match, TRUE, &match_block); - printf("\n"); -#endif - - /* If req_char is set, we know that that character must appear in the subject - for the match to succeed. If the first character is set, req_char must be - later in the subject; otherwise the test starts at the match point. This - optimization can save a huge amount of backtracking in patterns with nested - unlimited repeats that aren't going to match. We don't know what the state of - case matching may be when this character is hit, so test for it in both its - cases if necessary. However, the different cased versions will not be set up - unless PCRE_CASELESS was given or the casing state changes within the regex. - Writing separate code makes it go faster, as does using an autoincrement and - backing off on a match. */ - - if (req_char >= 0) - { - register const uschar *p = start_match + ((first_char >= 0)? 1 : 0); - - /* We don't need to repeat the search if we haven't yet reached the - place we found it at last time. */ - - if (p > req_char_ptr) - { - /* Do a single test if no case difference is set up */ - - if (req_char == req_char2) - { - while (p < end_subject) - { - if (*p++ == req_char) { p--; break; } - } - } - - /* Otherwise test for either case */ - - else - { - while (p < end_subject) - { - register int pp = *p++; - if (pp == req_char || pp == req_char2) { p--; break; } - } - } - - /* If we can't find the required character, break the matching loop */ - - if (p >= end_subject) break; - - /* If we have found the required character, save the point where we - found it, so that we don't search again next time round the loop if - the start hasn't passed this character yet. */ - - req_char_ptr = p; - } - } - - /* When a match occurs, substrings will be set for all internal extractions; - we just need to set up the whole thing as substring 0 before returning. If - there were too many extractions, set the return code to zero. In the case - where we had to get some local store to hold offsets for backreferences, copy - those back references that we can. In this case there need not be overflow - if certain parts of the pattern were not used. */ - - match_block.start_match = start_match; - if (!match(start_match, re->code, 2, &match_block, ims, NULL, match_isgroup)) - continue; - - /* Copy the offset information from temporary store if necessary */ - - if (using_temporary_offsets) - { - if (offsetcount >= 4) - { - memcpy(offsets + 2, match_block.offset_vector + 2, - (offsetcount - 2) * sizeof(int)); - DPRINTF(("Copied offsets from temporary memory\n")); - } - if (match_block.end_offset_top > offsetcount) - match_block.offset_overflow = TRUE; - - DPRINTF(("Freeing temporary memory\n")); - (pcre_free)(match_block.offset_vector); - } - - rc = match_block.offset_overflow? 0 : match_block.end_offset_top/2; - - if (offsetcount < 2) rc = 0; else - { - offsets[0] = start_match - match_block.start_subject; - offsets[1] = match_block.end_match_ptr - match_block.start_subject; - } - - DPRINTF((">>>> returning %d\n", rc)); - return rc; - } - -/* This "while" is the end of the "do" above */ - -while (!anchored && - match_block.errorcode == PCRE_ERROR_NOMATCH && - start_match++ < end_subject); - -if (using_temporary_offsets) - { - DPRINTF(("Freeing temporary memory\n")); - (pcre_free)(match_block.offset_vector); - } - -DPRINTF((">>>> returning %d\n", match_block.errorcode)); - -return match_block.errorcode; -} - -/* End of pcre.c */ === removed file 'parser/pcre/pcre.h' --- parser/pcre/pcre.h 2006-04-11 21:52:54 +0000 +++ parser/pcre/pcre.h 1970-01-01 00:00:00 +0000 @@ -1,113 +0,0 @@ -/************************************************* -* Perl-Compatible Regular Expressions * -*************************************************/ - -/* Copyright (c) 1997-2001 University of Cambridge */ - -#ifndef _PCRE_H -#define _PCRE_H - -/* The file pcre.h is build by "configure". Do not edit it; instead -make changes to pcre.in. */ - -#define PCRE_MAJOR 3 -#define PCRE_MINOR 9 -#define PCRE_DATE 02-Jan-2002 - -/* Win32 uses DLL by default */ - -#ifdef _WIN32 -# ifdef STATIC -# define PCRE_DL_IMPORT -# else -# define PCRE_DL_IMPORT __declspec(dllimport) -# endif -#else -# define PCRE_DL_IMPORT -#endif - -/* Have to include stdlib.h in order to ensure that size_t is defined; -it is needed here for malloc. */ - -#include - -/* Allow for C++ users */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* Options */ - -#define PCRE_CASELESS 0x0001 -#define PCRE_MULTILINE 0x0002 -#define PCRE_DOTALL 0x0004 -#define PCRE_EXTENDED 0x0008 -#define PCRE_ANCHORED 0x0010 -#define PCRE_DOLLAR_ENDONLY 0x0020 -#define PCRE_EXTRA 0x0040 -#define PCRE_NOTBOL 0x0080 -#define PCRE_NOTEOL 0x0100 -#define PCRE_UNGREEDY 0x0200 -#define PCRE_NOTEMPTY 0x0400 -#define PCRE_UTF8 0x0800 - -/* Exec-time and get-time error codes */ - -#define PCRE_ERROR_NOMATCH (-1) -#define PCRE_ERROR_NULL (-2) -#define PCRE_ERROR_BADOPTION (-3) -#define PCRE_ERROR_BADMAGIC (-4) -#define PCRE_ERROR_UNKNOWN_NODE (-5) -#define PCRE_ERROR_NOMEMORY (-6) -#define PCRE_ERROR_NOSUBSTRING (-7) - -/* Request types for pcre_fullinfo() */ - -#define PCRE_INFO_OPTIONS 0 -#define PCRE_INFO_SIZE 1 -#define PCRE_INFO_CAPTURECOUNT 2 -#define PCRE_INFO_BACKREFMAX 3 -#define PCRE_INFO_FIRSTCHAR 4 -#define PCRE_INFO_FIRSTTABLE 5 -#define PCRE_INFO_LASTLITERAL 6 - -/* Types */ - -struct real_pcre; /* declaration; the definition is private */ -struct real_pcre_extra; /* declaration; the definition is private */ - -typedef struct real_pcre pcre; -typedef struct real_pcre_extra pcre_extra; - -/* Store get and free functions. These can be set to alternative malloc/free -functions if required. Some magic is required for Win32 DLL; it is null on -other OS. */ - -PCRE_DL_IMPORT extern void *(*pcre_malloc)(size_t); -PCRE_DL_IMPORT extern void (*pcre_free)(void *); - -#undef PCRE_DL_IMPORT - -/* Functions */ - -extern pcre *pcre_compile(const char *, int, const char **, int *, - const unsigned char *); -extern int pcre_copy_substring(const char *, int *, int, int, char *, int); -extern int pcre_exec(const pcre *, const pcre_extra *, const char *, - int, int, int, int *, int); -extern void pcre_free_substring(const char *); -extern void pcre_free_substring_list(const char **); -extern int pcre_get_substring(const char *, int *, int, int, const char **); -extern int pcre_get_substring_list(const char *, int *, int, const char ***); -extern int pcre_info(const pcre *, int *, int *); -extern int pcre_fullinfo(const pcre *, const pcre_extra *, int, void *); -extern const unsigned char *pcre_maketables(void); -extern pcre_extra *pcre_study(const pcre *, int, const char **); -extern const char *pcre_version(void); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* End of pcre.h */ === modified file 'parser/tst/Makefile' --- parser/tst/Makefile 2006-12-15 08:10:25 +0000 +++ parser/tst/Makefile 2010-08-03 17:27:13 +0000 @@ -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,23 @@ 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'" + LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/modefail.sd | \ + grep -q "AppArmor parser error for errors/modefail.sd in errors/modefail.sd at line 6: syntax error" + LANG=C $(PARSER) -S -I errors 2>&1 >/dev/null errors/multi_include.sd | \ + grep -q "AppArmor parser error for errors/multi_include.sd in errors/multi_include.sd at line 12: Could not open 'failure'" + @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) === modified file 'parser/tst/README' --- parser/tst/README 2006-06-01 17:02:28 +0000 +++ parser/tst/README 2010-06-05 06:11:20 +0000 @@ -64,5 +64,3 @@ loop. Otherwise, the profile is passed on as-is to the subdomain parser. - -$Id$ === added directory 'parser/tst/errors' === added file 'parser/tst/errors/double.sd' --- parser/tst/errors/double.sd 1970-01-01 00:00:00 +0000 +++ parser/tst/errors/double.sd 2010-06-05 06:11:20 +0000 @@ -0,0 +1,5 @@ +# +/does/not/exist { + #include + #include +} === added directory 'parser/tst/errors/includes' === added file 'parser/tst/errors/includes/base' --- parser/tst/errors/includes/base 1970-01-01 00:00:00 +0000 +++ parser/tst/errors/includes/base 2010-06-05 06:11:20 +0000 @@ -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, === added file 'parser/tst/errors/includes/busted' --- parser/tst/errors/includes/busted 1970-01-01 00:00:00 +0000 +++ parser/tst/errors/includes/busted 2010-06-05 06:11:20 +0000 @@ -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, === added file 'parser/tst/errors/modefail.sd' --- parser/tst/errors/modefail.sd 1970-01-01 00:00:00 +0000 +++ parser/tst/errors/modefail.sd 2010-08-03 17:27:13 +0000 @@ -0,0 +1,7 @@ +# 1 +# 2 +# 3 +/does/not/exist { # 4 + /lib/lib*.so rm, # 5 + /fail abcdefgh, # 6 +} # 7 === added file 'parser/tst/errors/multi_include.sd' --- parser/tst/errors/multi_include.sd 1970-01-01 00:00:00 +0000 +++ parser/tst/errors/multi_include.sd 2010-08-03 17:27:13 +0000 @@ -0,0 +1,13 @@ +# +# +# +/does/not/exist { + #include + #include + #include + #include + + /bin/true rix, + + #include +} === added file 'parser/tst/errors/okay.sd' --- parser/tst/errors/okay.sd 1970-01-01 00:00:00 +0000 +++ parser/tst/errors/okay.sd 2010-06-05 06:11:20 +0000 @@ -0,0 +1,4 @@ +# +/does/not/exist { + #include +} === added file 'parser/tst/errors/single.sd' --- parser/tst/errors/single.sd 1970-01-01 00:00:00 +0000 +++ parser/tst/errors/single.sd 2010-06-05 06:11:20 +0000 @@ -0,0 +1,7 @@ +# +# +#include +# +/does/not/exist { + #include +} === modified file 'profiles/apparmor.d/abstractions/base' --- profiles/apparmor.d/abstractions/base 2010-01-03 21:16:38 +0000 +++ profiles/apparmor.d/abstractions/base 2010-06-05 06:11:20 +0000 @@ -85,6 +85,9 @@ # some applications will display license information /usr/share/common-licenses/** r, + # glibc statvfs + @{PROC}/filesystems r, + # Workaround https://launchpad.net/bugs/359338 until upstream handles stacked # filesystems generally. This does not appreciably decrease security with # Ubuntu profiles because the user is expected to have access to files owned === modified file 'profiles/apparmor.d/abstractions/dbus' --- profiles/apparmor.d/abstractions/dbus 2009-11-04 20:25:42 +0000 +++ profiles/apparmor.d/abstractions/dbus 2010-08-03 17:27:13 +0000 @@ -2,7 +2,7 @@ # $Id$ # ------------------------------------------------------------------ # -# Copyright (C) 2009 Canonical Ltd. +# Copyright (C) 2009-2010 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -10,8 +10,5 @@ # # ------------------------------------------------------------------ - # System socket + # System socket. Be careful when including this abstraction. /var/run/dbus/system_bus_socket w, - - # Machine id - /var/lib/dbus/machine-id r, === added file 'profiles/apparmor.d/abstractions/dbus-session' --- profiles/apparmor.d/abstractions/dbus-session 1970-01-01 00:00:00 +0000 +++ profiles/apparmor.d/abstractions/dbus-session 2010-08-03 17:27:13 +0000 @@ -0,0 +1,14 @@ +# vim:syntax=apparmor +# $Id$ +# ------------------------------------------------------------------ +# +# Copyright (C) 2010 Canonical Ltd. +# +# 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. +# +# ------------------------------------------------------------------ + + /usr/bin/dbus-launch Pix, + /var/lib/dbus/machine-id r, === modified file 'profiles/apparmor.d/abstractions/fonts' --- profiles/apparmor.d/abstractions/fonts 2009-11-04 20:25:42 +0000 +++ profiles/apparmor.d/abstractions/fonts 2010-06-05 06:11:20 +0000 @@ -15,6 +15,7 @@ /usr/lib/xorg/modules/fonts/**.so* mr, + /usr/share/fonts/ r, /usr/share/fonts/** r, /etc/fonts/** r, === modified file 'profiles/apparmor.d/abstractions/freedesktop.org' --- profiles/apparmor.d/abstractions/freedesktop.org 2009-11-04 20:25:42 +0000 +++ profiles/apparmor.d/abstractions/freedesktop.org 2010-06-05 06:11:20 +0000 @@ -27,3 +27,7 @@ @{HOME}/.icons/ r, @{HOME}/.recently-used.xbel* rw, @{HOME}/.config/user-dirs.dirs r, + @{HOME}/.local/share/icons/ r, + @{HOME}/.local/share/icons/** r, + @{HOME}/.local/share/mime/ r, + @{HOME}/.local/share/mime/** r, === modified file 'profiles/apparmor.d/abstractions/gnome' --- profiles/apparmor.d/abstractions/gnome 2009-11-10 20:04:26 +0000 +++ profiles/apparmor.d/abstractions/gnome 2010-08-03 17:27:13 +0000 @@ -3,7 +3,7 @@ # ------------------------------------------------------------------ # # Copyright (C) 2002-2009 Novell/SUSE -# Copyright (C) 2009 Canonical Ltd. +# Copyright (C) 2009-2010 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -31,6 +31,7 @@ /etc/pango/* r, /usr/lib{,32,64}/pango/** mr, /usr/lib{,32,64}/gtk-*/** mr, + /usr/lib{,32,64}/gdk-pixbuf-*/** mr, # per-user gtk configuration @{HOME}/.gnome/Gnome r, === modified file 'profiles/apparmor.d/abstractions/nameservice' --- profiles/apparmor.d/abstractions/nameservice 2009-11-04 20:25:42 +0000 +++ profiles/apparmor.d/abstractions/nameservice 2010-06-05 06:11:20 +0000 @@ -77,3 +77,5 @@ network inet dgram, network inet6 dgram, + # interface details + @{PROC}/*/net/route r, === modified file 'profiles/apparmor.d/abstractions/php5' --- profiles/apparmor.d/abstractions/php5 2010-01-03 21:16:38 +0000 +++ profiles/apparmor.d/abstractions/php5 2010-05-31 18:58:40 +0000 @@ -2,7 +2,7 @@ # ------------------------------------------------------------------ # # Copyright (C) 2002-2006 Novell/SUSE -# Copyright (C) 2009 Canonical, Ltd. +# Copyright (C) 2009-2010 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -11,13 +11,13 @@ # ------------------------------------------------------------------ # shared snippets for config files - /etc/php5/{conf.d,apache2,cli,fastcgi}/ r, - /etc/php5/{conf.d,apache2,cli,fastcgi}/*.ini r, + /etc/php5/{conf.d,apache2,cli,fastcgi,cgi}/ r, + /etc/php5/{conf.d,apache2,cli,fastcgi,cgi}/*.ini r, # Xlibs /usr/X11R6/lib{,32,64}/lib*.so* mr, # php extensions - /usr/lib{64,}/php5/{libexec,extensions}/*.so mr, + /usr/lib{64,}/php5/*/*.so mr, # php5 session mmap socket /var/lib/php5/session_mm_* rwlk, === modified file 'profiles/apparmor.d/abstractions/samba' --- profiles/apparmor.d/abstractions/samba 2009-11-04 20:25:42 +0000 +++ profiles/apparmor.d/abstractions/samba 2010-05-31 18:58:40 +0000 @@ -2,7 +2,7 @@ # $Id$ # ------------------------------------------------------------------ # -# Copyright (C) 2009 Canonical Ltd. +# Copyright (C) 2009-2010 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -12,7 +12,7 @@ /etc/samba/smb.conf r, /usr/share/samba/*.dat r, - /var/lib/samba/**.tdb rw, + /var/lib/samba/**.tdb rwk, /var/log/samba/cores/* w, /var/log/samba/log.* w, /var/run/samba/*.tdb rw, === modified file 'profiles/apparmor.d/abstractions/ubuntu-email' --- profiles/apparmor.d/abstractions/ubuntu-email 2009-11-11 19:42:30 +0000 +++ profiles/apparmor.d/abstractions/ubuntu-email 2010-08-03 17:27:13 +0000 @@ -15,5 +15,5 @@ /usr/bin/tkrat Ux, /usr/lib/thunderbird/thunderbird Ux, - + /usr/lib/thunderbird-3*/thunderbird Ux, === modified file 'profiles/apparmor.d/abstractions/ubuntu-media-players' --- profiles/apparmor.d/abstractions/ubuntu-media-players 2010-03-08 19:50:25 +0000 +++ profiles/apparmor.d/abstractions/ubuntu-media-players 2010-08-03 17:27:13 +0000 @@ -24,6 +24,7 @@ # mplayer /etc/mplayerplug-in.conf r, + /usr/bin/gmplayer Uxr, /usr/bin/gnome-mplayer Uxr, /usr/bin/kmplayer Uxr, /usr/bin/mplayer Uxr, === modified file 'profiles/apparmor.d/abstractions/user-tmp' --- profiles/apparmor.d/abstractions/user-tmp 2009-11-04 20:25:42 +0000 +++ profiles/apparmor.d/abstractions/user-tmp 2010-05-31 18:58:40 +0000 @@ -2,7 +2,7 @@ # ------------------------------------------------------------------ # # Copyright (C) 2002-2009 Novell/SUSE -# Copyright (C) 2009 Canonical Ltd. +# Copyright (C) 2009-2010 Canonical Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -11,11 +11,11 @@ # ------------------------------------------------------------------ # per-user tmp directories - @{HOME}/tmp/** rwkl, - @{HOME}/tmp/ rw, + owner @{HOME}/tmp/** rwkl, + owner @{HOME}/tmp/ rw, # global tmp directories - /var/tmp/** rwkl, - /var/tmp/ rw, - /tmp/** rwkl, - /tmp/ rw, + owner /var/tmp/** rwkl, + owner /var/tmp/ rw, + owner /tmp/** rwkl, + owner /tmp/ rw, === renamed directory 'tests/regression/subdomain' => 'tests/regression/apparmor' === modified file 'tests/regression/apparmor/Makefile' --- tests/regression/subdomain/Makefile 2009-08-21 20:39:45 +0000 +++ tests/regression/apparmor/Makefile 2010-08-03 17:27:13 +0000 @@ -22,6 +22,7 @@ chmod.c \ chown.c \ clone.c \ + coredump.c \ deleted.c \ environ.c \ env_check.c \ @@ -113,6 +114,7 @@ changehat_misc \ chdir \ clone \ + coredump \ deleted \ environ \ exec \ @@ -158,27 +160,39 @@ tests: all @if [ `whoami` = "root" ] ;\ then \ + rc=0; \ for i in $(TESTS) ;\ do \ echo ;\ echo "running $$i" ;\ bash $$i.sh ;\ + if [ $$? -ne 0 ] ; then \ + rc=1;\ + fi;\ done ;\ + exit $$rc;\ else \ echo "must be root to run tests" ;\ + exit 1;\ fi alltests: all @if [ `whoami` = "root" ] ;\ then \ + rc=0; \ for i in $(TESTS) $(RISKY_TESTS) ;\ do \ echo ;\ echo "running $$i" ;\ bash $$i.sh ;\ + if [ $$? -ne 0 ] ; then \ + rc=1;\ + fi;\ done ;\ + exit $$rc;\ else \ echo "must be root to run tests" ;\ + exit 1;\ fi clean: === modified file 'tests/regression/apparmor/README' --- tests/regression/subdomain/README 2006-09-15 22:39:59 +0000 +++ tests/regression/apparmor/README 2010-08-03 17:27:13 +0000 @@ -1,10 +1,10 @@ Running tests ============= -Type "make tests" at the shell prompt, this will make the subprograms -and run the tests. +Type "sudo make tests" at the shell prompt, this will make the +subprograms and run the tests. -You must be root to execute make tests (a requirement of subdomain). +You must be root to execute "make tests" (a requirement of AppArmor). (There is also a 'make alltests', which adds a test for bug that, when triggered, would cause the kernel to crash.) @@ -12,10 +12,13 @@ Test output =========== -No output is displayed for a passing test. The makefile will output +By default, no output is displayed for a passing test. The makefile will +output: running for each test. -Output other than this indicates a problem. +To have verbose output with each subtest reporting successes, set the +environment variable VERBOSE=1: + sudo VERBOSE=1 make tests There are three typical failure scenarios: - Test failed when it was expected to pass @@ -29,7 +32,7 @@ Common user changeable environment variables are stored in the file 'uservars.inc'. Currently the path to the tmp directory, the path -to the subdomain_parser executable, and any additional arguments to give +to the apparmor_parser executable, and any additional arguments to give to the parser are specified in this configuration file. (Note: the tmp directory specified in uservars.inc will have an added @@ -66,7 +69,7 @@ directory will contain the files for the failed subtest. It may be necessary to create certain temp files in this directory in order to -have the test function correctly, see the subdomain profile 'profile' in the +have the test function correctly, see the AppArmor profile 'profile' in the directory in order to determine which files may need to be created to support the executable. @@ -155,7 +158,7 @@ bin=$pwd - + . $bin/prologue.inc - + genprofile $file:$badperm @@ -183,16 +186,11 @@ -Additional documentation -======================== - -See the file 'subdomain_test.txt' - Supporting files ================ strace.sh Not a test harness, used to support strace testing. -mkprofile.sh Not a test harness, used to generate subdomain profiles. +mkprofile.sh Not a test harness, used to generate AppArmor profiles. prologue.inc Must be dotted (included) into the test harness. Provides support routines. epilogue.inc Cleanup support, automatically called upon successful or @@ -222,17 +220,3 @@ This is not an error, rather a sign that bash noticed the kernel had killed a process which was attempting to use a bogus MAGIC number. Alas, there is no way to get bash to not print this diagnostic - -3) Ptrace - Error: open passed. Test 'STRACE OPEN (x confinement)' - was expected to 'fail' - - Regression from 2.4.18 to 2.4.20. (We aren't sure on the first - endpoint, and the problem still happens in 2.4.20-20_imnx_10smp.) - -4) Open - Error: open passed. Test 'OPEN W (create)' was expected to 'fail' - - LSM issue. Flags passed to inode_permission are 0 if O_CREAT is used to - open file. Need to submit a patch to inode_create hook to receive the - O_RDWR flags. See https://bugs.wirex.com/show_bug.cgi?id=2885 === modified file 'tests/regression/apparmor/changehat_misc.sh' --- tests/regression/subdomain/changehat_misc.sh 2006-05-19 17:32:14 +0000 +++ tests/regression/apparmor/changehat_misc.sh 2010-08-03 17:27:13 +0000 @@ -64,7 +64,7 @@ echo "*** A 'Killed' message from bash is expected for the following test" runchecktest "CHANGEHAT (subprofile->subprofile w/ bad magic)" signal9 $subtest $subtest2 badmagic $file -# 1. ATTEMPT TO CHANGEGAT TO AN INVALUD PROFILE, SHOULD PUT US INTO A NULL +# 1. ATTEMPT TO CHANGEHAT TO AN INVALID PROFILE, SHOULD PUT US INTO A NULL # PROFILE # 2. ATTEMPT TO CHANGEHAT OUT WITH BAD TOKEN settest changehat_fail === modified file 'tests/regression/apparmor/coredump.c' --- tests/regression/subdomain/coredump.c 2006-05-19 17:32:14 +0000 +++ tests/regression/apparmor/coredump.c 2010-08-03 17:27:13 +0000 @@ -1,7 +1,9 @@ +#include int *ptr; /* * Copyright (C) 2002-2005 Novell/SUSE + * Copyright (C) 2010 Canonical, Ltd * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -9,7 +11,7 @@ * License. */ -main() +int main(int argc, char *argv[]) { printf("This will cause a sigsegv\n"); === modified file 'tests/regression/apparmor/coredump.sh' --- tests/regression/subdomain/coredump.sh 2006-05-19 17:32:14 +0000 +++ tests/regression/apparmor/coredump.sh 2010-08-03 17:27:13 +0000 @@ -1,7 +1,6 @@ #! /bin/bash -# $Id$ - # Copyright (C) 2002-2005 Novell/SUSE +# Copyright (C) 2010 Canonical, Ltd # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -11,26 +10,52 @@ #=NAME coredump #=DESCRIPTION coredump test +cleancorefile() +{ + rm -f core core.* +} + checkcorefile() { -_corefilelist=`echo core.*` -if [ "$_corefilelist" = "core.*" ] -then - _corefile=no -else - _corefile=yes -fi - -if [ "$1" = "yes" -a "$_corefile" = "no" ] -then - echo "Error: corefile expected but not present - $2" -elif [ "$1" = "no" -a "$_corefile" = "yes" ] -then - echo "Error: corefile present when not expected -- $2" -fi - -unset _corefile _corefilelist -rm -f core.* + # global _testdesc _pfmode _known outfile + if [ ${1:0:1} == "x" ] ; then + requirement=${1#x} + _known=" (known problem)" + else + requirement=$1 + _known="" + fi + + _corefilelist=`echo core.*` + if [ ! -f core ] && [ "$_corefilelist" = "core.*" ] + then + _corefile=no + else + _corefile=yes + fi + + if [ "$requirement" = "yes" -a "$_corefile" = "no" ] ; then + if [ -n $_known ] ; then + echo -n "XFAIL: " + fi + echo "Error: corefile expected but not present - $2" + if [ -z $_known ] ; then + cat $profile + testfailed + fi + elif [ "$requirement" = "no" -a "$_corefile" = "yes" ] ; then + if [ -n "$_known" ] ; then + echo -n "XFAIL: " + fi + echo "Error: corefile present when not expected -- $2" + if [ -z "$_known" ] ; then + cat $profile + testfailed + fi + fi + + unset _corefile _corefilelist + cleancorefile } pwd=`dirname $0` @@ -45,15 +70,18 @@ # enable coredumps ulimit -c 1000000 +cleancorefile +checkcorefile no "COREDUMP (starting with clean slate)" # PASS TEST, no confinement +cleancorefile echo "*** A 'Segmentation Fault' message from bash is expected for the following test" runchecktest "COREDUMP (no confinement)" signal11 checkcorefile yes "COREDUMP (no confinement)" # PASS TEST, with r confinement -genprofile $test:$coreperm -cat $profile +cleancorefile +genprofile image=$test:$coreperm echo echo "*** A 'Segmentation Fault' message from bash is expected for the following test" @@ -61,10 +89,10 @@ checkcorefile yes "COREDUMP ($coreperm confinement)" # FAIL TEST, with x confinement -genprofile $test:$nocoreperm -cat $profile +cleancorefile +genprofile image=$test:$nocoreperm echo echo "*** A 'Segmentation Fault' message from bash is expected for the following test" runchecktest "COREDUMP ($nocoreperm confinement)" signal11 -checkcorefile no "COREDUMP ($nocoreperm confinement)" +checkcorefile xno "COREDUMP ($nocoreperm confinement)" === modified file 'tests/regression/apparmor/mkprofile.pl' --- tests/regression/subdomain/mkprofile.pl 2009-11-11 18:44:26 +0000 +++ tests/regression/apparmor/mkprofile.pl 2010-08-03 17:27:13 +0000 @@ -5,7 +5,7 @@ # # Gawd, I hate writing perl. It shows, too. # -my $__VERSION__='$Id$'; +my $__VERSION__=$0; use strict; use Getopt::Long; === modified file 'tests/regression/apparmor/prologue.inc' --- tests/regression/subdomain/prologue.inc 2010-02-07 07:04:57 +0000 +++ tests/regression/apparmor/prologue.inc 2010-08-03 17:27:13 +0000 @@ -1,9 +1,14 @@ # vim:syntax=sh # -# prologue.inc -# # Test infrastructure support. # +# Copyright 2010 Canonical, Ltd. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, version 2 of the +# License. +# # This file should be included by each test case # It does a lot of hidden 'magic', Downside is that # this magic makes debugging fauling tests more difficult. @@ -93,8 +98,10 @@ while [ -h ${link} ] do - if [ -x /usr/bin/readlink ] ; then - target=$(/usr/bin/readlink ${link}) + if [ -x /usr/bin/readlink ] ; then + target=$(/usr/bin/readlink -f ${link}) + elif [ -x /bin/readlink ] ; then + target=$(/bin/readlink -f ${link}) else # I'm sure there's a more perlish way to do this target=$( perl -e "printf (\"%s\n\", readlink(\"${link}\"));") @@ -251,6 +258,7 @@ then echo "Error: ${testname} passed. Test '${_testdesc}' was expected to '${_pfmode}'" testfailed + return elif [ "$_pfmode" == "pass" -a -n "${_known}" ] then echo "Alert: ${testname} passed. Test '${_testdesc}' was marked as expected pass but known problem (xpass)" @@ -260,6 +268,7 @@ then echo "Error: ${testname} failed. Test '${_testdesc}' was expected to '${_pfmode}'. Reason for failure '${ret}'" testfailed + return elif [ "$_pfmode" == "fail" -a -n "${_known}" ] then echo "Alert: ${testname} failed. Test '${_testdesc}' was marked as expected fail but known problem (xfail)." @@ -272,16 +281,23 @@ then echo "Error: ${testname} failed. Test '${_testdesc}' was expected to terminate with signal ${expectedsig}${_known}. Instead it terminated with signal ${killedsig}" testfailed + return fi ;; *) echo "Error: ${testname} failed. Test '${_testdesc}' was expected to '${_pfmode}'${_known}. Reason for failure 'killed by signal ${killedsig}'" testfailed + return ;; esac ;; *) testerror + return ;; esac + + if [ -n "$VERBOSE" ]; then + echo "ok: ${_testdesc}" + fi } runchecktest() @@ -399,22 +415,12 @@ # it is most often used after --, in fact it is basically # mandatory after -- case "$1" in - profile=*) imagename=`echo $1 | sed 's/^profile=[rix]*//'` - perm=`echo $1 | sed -n 's/^profile=\([rix]*\).*$/\1/p'` - if [ -n "$perm" ] - then - imageperm=$perm - fi - num_emitted=0 - shift - ;; - - image=*) imagename=`echo $1 | sed 's/^image=[rix]*//'` + image=*) imagename=`echo $1 | sed 's/^image=\([^:]*\).*$/\1/'` if [ ! -x "$imagename" ] then fatalerror "invalid imagename specified in input '$1'" fi - perm=`echo $1 | sed -n 's/^image=\([rix]*\).*$/\1/p'` + perm=`echo $1 | sed -n 's/^image=[^:]*:\(.*\)$/\1/p'` if [ -n "$perm" ] then imageperm=$perm === modified file 'tests/regression/apparmor/pwrite.sh' --- tests/regression/subdomain/pwrite.sh 2007-12-23 00:58:47 +0000 +++ tests/regression/apparmor/pwrite.sh 2010-08-03 17:27:13 +0000 @@ -27,7 +27,7 @@ genprofile $file:$okperm -runtestbg "PWRITE with w" pass $file +runtestbg "PREAD/PWRITE with rw" pass $file sleep 2 === modified file 'tests/regression/apparmor/swap.sh' --- tests/regression/subdomain/swap.sh 2006-05-19 17:32:14 +0000 +++ tests/regression/apparmor/swap.sh 2010-08-03 17:27:13 +0000 @@ -32,7 +32,7 @@ swap_file=$tmpdir/swapfile dd if=/dev/zero of=${swap_file} bs=1024 count=512 2> /dev/null -/sbin/mkswap ${swap_file} > /dev/null +/sbin/mkswap -f ${swap_file} > /dev/null # TEST 1. Make sure can enable and disable swap unconfined === modified file 'tests/regression/apparmor/syscall.sh' --- tests/regression/subdomain/syscall.sh 2007-12-23 01:02:50 +0000 +++ tests/regression/apparmor/syscall.sh 2010-08-03 17:27:13 +0000 @@ -1,7 +1,7 @@ #! /bin/bash -# $Id$ - +# # Copyright (C) 2002-2005 Novell/SUSE +# Copyright (C) 2010 Canonical, Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -114,9 +114,9 @@ runchecktest "MKNOD sock (permissions)" fail s $mknod_file ## -## D. SETHOSTNAME +## C. SYSCTL ## -sh syscall_sysctl.sh +bash syscall_sysctl.sh ## ## D. SETHOSTNAME === modified file 'tests/regression/apparmor/syscall_mknod.c' --- tests/regression/subdomain/syscall_mknod.c 2006-05-19 17:32:14 +0000 +++ tests/regression/apparmor/syscall_mknod.c 2010-08-03 17:27:13 +0000 @@ -19,7 +19,7 @@ int main(int argc, char *argv[]) { -int fd, mode; + int mode; if (argc != 3){ fprintf(stderr, "usage: %s b|c|f|s|r file\n", @@ -49,8 +49,6 @@ return 1; } - close(fd); - printf("PASS\n"); return 0; === modified file 'tests/regression/apparmor/unix_fd_server.c' --- tests/regression/subdomain/unix_fd_server.c 2006-05-19 17:32:14 +0000 +++ tests/regression/apparmor/unix_fd_server.c 2010-08-03 17:27:13 +0000 @@ -2,6 +2,7 @@ /* * Copyright (C) 2002-2005 Novell/SUSE + * Copyright (C) 2010 Canonical, Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -134,6 +135,7 @@ } /* Check for info re: reading the file */ + memset(inbound_buffer, 0, sizeof(inbound_buffer)); if (recv(in_sock, inbound_buffer, 16,0) == -1 ) { fprintf(stderr, "FAIL - recv %s\n", strerror(errno)); === modified file 'tests/stress/parser/stress.rb' --- tests/stress/parser/stress.rb 2008-11-26 22:16:48 +0000 +++ tests/stress/parser/stress.rb 2010-05-31 18:58:40 +0000 @@ -14,10 +14,27 @@ return sprintf("%0#{len}x", rand(2 ** (4 * len))) end +def get_random_regex() + case rand(10) + when 0..3 + return "{#{get_random_name(rand(8) + 2)},#{get_random_name(rand(8) + 2)},#{get_random_name(rand(8) + 2)}}" + when 4..5 + return "[#{get_random_name(rand(5) + 1)}]" + when 6..7 + return "*" + when 8..9 + return "**" + end +end + def get_random_path() out = "" - 0.upto(rand(20)) do - out = "#{out}/#{get_random_name(4)}" + 0.upto(rand(20) + 2) do + if rand(4) == 0 + out = "#{out}/#{get_random_regex}" + else + out = "#{out}/#{get_random_name(rand(10) + 4)}" + end end return out end @@ -83,7 +100,10 @@ "mknod", "lease", "audit_write", - "audit_control" + "audit_control", + "setfcap", + "mac_override", + "mac_admin" ] def initialize() @@ -95,6 +115,93 @@ end end +class NetRule < Rule + # XXX Fill me in +end + +class RlimitRule < Rule + RLIMIT_LIST = [ +#"cpu", # cpu rlimit not supported + "fsize", + "data", + "stack", + "core", + "rss", + "nofile", + "ofile", + "as", + "nproc", + "memlock", + "locks", + "sigpending", + "msgqueue", + "nice", + "rtprio" + ] + + def initialize() + @rlimit = RLIMIT_LIST[rand(RLIMIT_LIST.length)] + if rand(20) == 0 + @limit = "infinity" + elsif @rlimit == "nice" + @limit = rand(40) - 20 + else + @limit = rand(2 ** 31) + end + end + + def to_s + return " set rlimit #{@rlimit} <= #{@limit}," + end +end + +class Flags + FLAG_LIST = [ + "complain", + "audit", + "chroot_relative", + "namespace_relative", + "mediate_deleted", + "delegate_deleted", + "attach_disconnected", + "no_attach_disconnected", + "chroot_attach", + "chroot_no_attach" + ] + + FLAG_CONFLICTS = [ + ["chroot_relative", "namespace_relative"], + ["mediate_deleted", "delegate_deleted"], + ["attach_disconnected", "no_attach_disconnected"], + ["chroot_attach", "chroot_no_attach"] + ] + + def initialize() + @flags = [] + if rand(2) == 1 + return + end + + 0.upto(4 - Math.log(rand(32) + 1).to_int) do |x| + @flags << FLAG_LIST[rand(FLAG_LIST.length)] + end + + FLAG_CONFLICTS.each do |c| + if @flags.include?(c[0]) and @flags.include?(c[1]) + @flags.delete(c[rand(2)]) + end + end + end + + def to_s + if @flags.empty? + return "" + end + out = @flags.join(",") + return "flags=(#{out})" + end +end + def prefix_to_s(name) out = [] out << "#" @@ -112,16 +219,19 @@ @rvalue = get_random_name() @name = "/does/not/exist/#{@rvalue}" @rules = [] + @flags = Flags.new() end def generate_rules - @rules << FileRule.new(@name, "rm") + @rules << FileRule.new(@name, "rm").to_s 0.upto(rand($max_rules - $min_rules) + $min_rules) do |x| case rand(100) - when 0..19 - @rules << CapRule.new - when 19..100 - @rules << FileRule.new + when 0..14 + @rules << CapRule.new.to_s + when 15..24 + @rules << RlimitRule.new.to_s + when 25..100 + @rules << FileRule.new.to_s end end end @@ -132,10 +242,10 @@ out << "# profile for #{@name}" out << "# generated by #{__FILE__} #{$my_version}" out << "#" - out << "#{@name} {" + out << "#{@name} #{@flags} {" out << " #include " out << "" - @rules.each { |r| out << r.to_s } + @rules.sort.each { |r| out << " #{r}" } out << "}" out << "" end === modified file 'utils/SubDomain.pm' --- utils/SubDomain.pm 2010-03-10 23:30:06 +0000 +++ utils/SubDomain.pm 2010-08-03 17:27:13 +0000 @@ -2,6 +2,7 @@ # # ---------------------------------------------------------------------- # Copyright (c) 2006 Novell, Inc. All Rights Reserved. +# Copyright (c) 2010 Canonical, Ltd. # # This program is free software; you can redistribute it and/or # modify it under the terms of version 2 of the GNU General Public @@ -2413,10 +2414,13 @@ our $seenmark; my $RE_LOG_v2_0_syslog = qr/SubDomain/; my $RE_LOG_v2_1_syslog = qr/kernel:\s+(\[[\d\.\s]+\]\s+)?(audit\([\d\.\:]+\):\s+)?type=150[1-6]/; +my $RE_LOG_v2_6_syslog = qr/kernel:\s+(\[[\d\.\s]+\]\s+)?type=\d+\s+audit\([\d\.\:]+\):\s+apparmor=/; my $RE_LOG_v2_0_audit = qr/type=(APPARMOR|UNKNOWN\[1500\]) msg=audit\([\d\.\:]+\):/; my $RE_LOG_v2_1_audit = qr/type=(UNKNOWN\[150[1-6]\]|APPARMOR_(AUDIT|ALLOWED|DENIED|HINT|STATUS|ERROR))/; +my $RE_LOG_v2_6_audit = + qr/type=AVC\s+audit\([\d\.\:]+\):\s+apparmor=/; sub prefetch_next_log_entry { # if we already have an existing cache entry, something's broken @@ -2434,6 +2438,8 @@ $RE_LOG_v2_0_audit | $RE_LOG_v2_1_audit | $RE_LOG_v2_1_syslog | + $RE_LOG_v2_6_syslog | + $RE_LOG_v2_6_audit | $logmark }x); } @@ -6612,10 +6618,14 @@ LibAppArmor::free_record($event); #map new c and d to w as logprof doesn't support them yet - $rmask =~ s/c/w/g; - $rmask =~ s/d/w/g; - $dmask =~ s/c/w/g; - $dmask =~ s/d/w/g; + if ($rmask) { + $rmask =~ s/c/w/g; + $rmask =~ s/d/w/g; + } + if ($dmask) { + $dmask =~ s/c/w/g; + $dmask =~ s/d/w/g; + } if ($rmask && !validate_log_mode(hide_log_mode($rmask))) { fatal_error(sprintf(gettext('Log contains unknown mode %s.'), === modified file 'utils/apparmor_notify' --- utils/apparmor_notify 2010-03-10 16:11:26 +0000 +++ utils/apparmor_notify 2010-05-31 18:58:40 +0000 @@ -30,8 +30,7 @@ require Time::Local; require File::Basename; -use vars qw($opt_p $opt_s $opt_l $opt_h $opt_v $opt_d $opt_w); -use Getopt::Std; +use Getopt::Long; my %prefs; my $conf = "/etc/apparmor/notify.conf"; @@ -67,7 +66,6 @@ $ENV{SHELL} = "/bin/sh"; defined($ENV{IFS}) and $ENV{IFS} = ' \t\n'; -print $0 . "\n"; my $prog = File::Basename::basename($0); if ($prog !~ /^[a-zA-Z0-9_\-]+$/) { @@ -75,32 +73,66 @@ exitscript(1); } -my $logfile = "/var/log/kern.log"; --e "/var/run/auditd.pid" and $logfile = "/var/log/audit/audit.log"; - $> == $< or die "Cannot be suid\n"; $) == $( or die "Cannot be sgid\n"; my $login; +our $orig_euid = $>; -getopts('dhlpvs:w:'); +my $opt_d = ''; +my $opt_h = ''; +my $opt_l = ''; +my $opt_p = ''; +my $opt_v = ''; +my $opt_f = ''; +my $opt_s = 0; +my $opt_u = ''; +my $opt_w = 0; +GetOptions( + 'debug|d' => \$opt_d, + 'help|h' => \$opt_h, + 'since-last|l' => \$opt_l, + 'poll|p' => \$opt_p, + 'verbose|v' => \$opt_v, + 'file|f=s' => \$opt_f, + 'since-days|s=n' => \$opt_s, + 'user|u=s' => \$opt_u, + 'wait|w=n' => \$opt_w, +); if ($opt_h) { usage; exitscript(0); } +# monitor file specified with -f, else use audit.log if auditd is running, +# otherwise kern.log +our $logfile = "/var/log/kern.log"; +if ($opt_f) { + -f $opt_f or die "'$opt_f' does not exist. Aborting\n"; + $logfile = $opt_f; +} else { + -e "/var/run/auditd.pid" and $logfile = "/var/log/audit/audit.log"; +} + +-r $logfile or die "Cannot read '$logfile'\n"; +our $logfile_inode = get_logfile_inode($logfile); +our $logfile_size = get_logfile_size($logfile); open (LOGFILE, "<$logfile") or die "Could not open '$logfile'\n"; # Drop priviliges, if running as root if ($< == 0) { $login = "root"; if (defined($ENV{SUDO_UID}) and defined($ENV{SUDO_GID})) { POSIX::setgid($ENV{SUDO_GID}) or _error("Could not change gid"); - POSIX::setuid($ENV{SUDO_UID}) or _error("Could not change uid"); + $> = $ENV{SUDO_UID} or _error("Could not change euid"); defined($ENV{SUDO_USER}) and $login = $ENV{SUDO_USER}; } else { + my $drop_to = $nobody_user; + if ($opt_u) { + $drop_to = $opt_u; + } # nobody/nogroup - POSIX::setgid(scalar(getpwnam($nobody_group))) or _error("Could not change gid to '$nobody_group'"); - POSIX::setuid(scalar(getpwnam($nobody_user))) or _error("Could not change uid to '$nobody_user'"); + POSIX::setgid(scalar(getgrnam($nobody_group))) or _error("Could not change gid to '$nobody_group'"); + $> = scalar(getpwnam($drop_to)) or _error("Could not change euid to '$drop_to'"); } } else { $login = getlogin(); @@ -111,7 +143,7 @@ readconf($conf); if (defined($prefs{use_group})) { my ($name, $passwd, $gid, $members) = getgrnam($prefs{use_group}); - if (not defined($members) or not defined($login) or not grep { $_ eq $login } split(/ /, $members)) { + if (not defined($members) or not defined($login) or (not grep { $_ eq $login } split(/ /, $members) and $login ne "root")) { _error("'$login' must be in '$prefs{use_group}' group. Aborting"); } } @@ -220,7 +252,6 @@ defined($name) and $formatted .= "Name: $name\n"; defined($denied) and $formatted .= "Denied: $denied\n"; defined($family) and defined ($sock_type) and $formatted .= "Family: $family\nSocket type: $sock_type\n"; - #defined($date) and $since > 0 and $formatted .= "Date: ". scalar(localtime($date)) ."\n"; $formatted .= "Logfile: $logfile\n"; return $formatted; @@ -259,6 +290,26 @@ close(PS); } +sub send_message { + my $msg = $_[0]; + + my $pid = fork(); + if ($pid == 0) { # child + # notify-send needs $< to be the unprivileged user + $< = $>; + + # 'system' uses execvp() so no shell metacharacters here. + # $notify_exe is an absolute path so execvp won't search PATH. + system "$notify_exe", "-i", "gtk-dialog-warning", "-u", "critical", "--", "AppArmor Message", "$msg"; + my $exit_code = $? >> 8; + exit($exit_code); + } + + # parent + waitpid($pid, 0); + return $?; +} + sub do_notify { my %seen; my $seconds = 5; @@ -273,7 +324,7 @@ umask 0; open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!"; - open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!"; + #open STDERR, '>/dev/null' or die "Can't write to /dev/null: $!"; my $pid = fork(); exit if $pid; die "Couldn't fork: $!" unless defined($pid); @@ -293,12 +344,24 @@ my $count = 0; my $footer = "For more information, please see:\n$url"; my $first_run = 1; - my $since = $now - (int($opt_s) * 60 * 60 * 24); + my $since = $now; + if ($opt_s and int($opt_s) > 0) { + $since = $since - (int($opt_s) * 60 * 60 * 24); + } for (my $i=0; $time_to_die == 0; $i++) { + if ($logfile_inode != get_logfile_inode($logfile)) { + _warn("$logfile changed inodes, reopening"); + reopen_logfile(); + } elsif (get_logfile_size($logfile) < $logfile_size) { + _warn("$logfile is smaller, reopening"); + reopen_logfile(); + } while(my $msg = ) { my @attrib; - if ($first_run == 1 and $opt_s) { - @attrib = parse_message($msg, $since); + if ($first_run == 1) { + if ($since != $now) { + @attrib = parse_message($msg, $since); + } } else { @attrib = parse_message($msg); } @@ -340,12 +403,9 @@ $m .= $footer; - # 'system' uses execvp() so no shell metacharacters here. - # $notify_exe is an absolute path so execvp won't search PATH. - system "$notify_exe", "-i", "gtk-dialog-warning", "-u", "critical", "--", "AppArmor Message", "$m"; - my $exit_code = $? >> 8; - if ($exit_code != 0) { - _warn("'$notify_exe' exited with '$exit_code'"); + my $rc = send_message($m); + if ($rc != 0) { + _warn("'$notify_exe' exited with error '$rc'"); $time_to_die = 1; last; } @@ -356,7 +416,7 @@ if ($first_run) { if ($count > 0) { - my $m = "$logfile contains $count existing denied message"; + my $m = "$logfile contains $count denied message"; $count > 1 and $m .= "s"; if ($opt_s) { $m .= " in the last "; @@ -368,7 +428,7 @@ } $m .= ". "; $m .= $footer; - system "$notify_exe", "-i", "gtk-dialog-warning", "-u", "critical", "--", "AppArmor Message", "$m"; + send_message($m); } $first_run = 0; } @@ -390,6 +450,9 @@ } sub show_since { + my %msg_hash; + my %last_date; + my @msg_list; my $count = 0; while(my $msg = ) { my @attrib = parse_message($msg, $_[0]); @@ -397,10 +460,31 @@ my $m = format_message(@attrib); $m ne "" or next; - - $opt_v and print "$m\n"; + my $date = $attrib[6]; + if ($opt_v) { + if (exists($msg_hash{$m})) { + $msg_hash{$m}++; + defined($date) and $last_date{$m} = scalar(localtime($date)); + } else { + $msg_hash{$m} = 1; + push(@msg_list, $m); + } + } $count++; } + if ($opt_v) { + foreach my $m (@msg_list) { + print "$m"; + if ($msg_hash{$m} gt 1) { + print "($msg_hash{$m} found"; + if (exists($last_date{$m})) { + print ", most recent from '$last_date{$m}'"; + } + print ")\n"; + } + print "\n"; + } + } return $count; } @@ -452,19 +536,63 @@ sub usage { my $s = <<'EOF'; USAGE: apparmor_notify [OPTIONS] + Display AppArmor notifications or messages for DENIED entries. OPTIONS: - -p poll AppArmor logs and display notifications - -l display stats since last login - -s NUM show stats for last NUM days (can be used alone or with -p) - -v show messages with stats - -h display this help - -w NUM wait NUM seconds before displaying notifications (with -p) + -p, --poll poll AppArmor logs and display notifications + -f FILE, --file=FILE search FILE for AppArmor messages + -l, --since-last display stats since last login + -s NUM, --since-days=NUM show stats for last NUM days (can be used alone + or with -p) + -v, --verbose show messages with stats + -h, --help display this help + -u USER, --user=USER user to drop privileges to when not using sudo + -w NUM, --wait=NUM wait NUM seconds before displaying + notifications (with -p) EOF print $s; } +sub reopen_logfile { + # reopen the logfile, temporarily switching back to starting euid for + # file permissions. + close(LOGFILE); + + my $old_euid = $>; + my $change_euid = 0; + if ($> != $<) { + _debug("raising privileges to '$orig_euid' in reopen_logfile()"); + $change_euid = 1; + $> = $orig_euid; + $> == $orig_euid or die "Could not raise privileges\n"; + } + + $logfile_inode = get_logfile_inode($logfile); + $logfile_size = get_logfile_size($logfile); + open (LOGFILE, "<$logfile") or die "Could not open '$logfile'\n"; + + if ($change_euid) { + _debug("dropping privileges to '$old_euid' in reopen_logfile()"); + $> = $old_euid; + $> == $old_euid or die "Could not drop privileges\n"; + } +} + +sub get_logfile_size { + my $fn = $_[0]; + my $size; + defined(($size = (stat($fn))[7])) or (sleep(10) and defined(($size = (stat($fn))[7])) or die "'$fn' disappeared. Aborting\n"); + return $size; +} + +sub get_logfile_inode { + my $fn = $_[0]; + my $inode; + defined(($inode = (stat($fn))[1])) or (sleep(10) and defined(($inode = (stat($fn))[1])) or die "'$fn' disappeared. Aborting\n"); + return $inode; +} + # # end Subroutines # === modified file 'utils/apparmor_notify.pod' --- utils/apparmor_notify.pod 2010-02-12 16:25:02 +0000 +++ utils/apparmor_notify.pod 2010-05-31 18:58:40 +0000 @@ -40,24 +40,37 @@ =over 4 -=item -p +=item -p, --poll poll AppArmor logs and display desktop notifications. Can be used with '-s' option to display a summary on startup. -=item -l +=item -f FILE, --file=FILE + +search FILE for AppArmor messages + +=item -l, --since-last show summary since last login. -=item -s NUM +=item -s NUM, --since-days=NUM show summary for last NUM of days. -=item -v +=item -u USER, --user=USER + +user to drop privileges to when running privileged. This has no effect when +running under sudo. + +=item -w NUM, --wait=NUM + +wait NUM seconds before displaying notifications (for use with -p) + +=item -v, --verbose show messages with summaries. -=item -h +=item -h, --help displays a short usage statement.