2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-09-05 00:35:13 +00:00

Compare commits

...

45 Commits

Author SHA1 Message Date
Steve Beattie
1439d006cd Subject: add apparmor.vim install target to utils/ install
This patch adds a make install target for the generated apparmor.vim
file, installing by default into /usr/share/apparmor based on IRC
discussions; alternate suggestions welcome. (Installing directly
into the vim syntax tree is difficult as the system path by default
contains the vim version number.)
2012-03-22 13:27:29 -07:00
Steve Beattie
b4feb99841 Subject: rewrite apparmor.vim generation and integrate into build
This patch replaces the apparmor.vim generating script with a python
version that eliminates the need for using the replace tool from the
mysql-server package. It makes use of the automatically generated
lists of capabilities and network protocols provided by the build
infrastructure. I did not capture all the notes and TODOs that
Christian had in the shell script; I can do so if desired.

It also hooks the generation of the apparmor.vim file into the utils/
build and clean stages.
2012-03-22 13:26:20 -07:00
Steve Beattie
63c43ae9f5 Subject: add missing capabilities to severity.db
This patch adds several missing capabilities to the utils/
severity.db file as detected by the newly added make check target,
along with corresponding severity levels that I believe :re appropriate
(discussion welcome):

  CAP_MAC_ADMIN 10
  CAP_MAC_OVERRIDE 10
  CAP_SETFCAP 9
  CAP_SYSLOG 8
  CAP_WAKE_ALARM 8

The latter two are undocumented in the capabilities(7) man page
provided in Ubuntu 12.04; the syslog one is the separation out of
accessing the dmesg buffer from CAP_SYSADMIN, and the CAP_WAKE_ALARM
allows setting alarms that would wake a system from a suspended state,
if my reading is correct.

This also fixes a trailing whitespace on CAP_CHOWN, moves
CAP_DAC_READ_SEARCH to the end of the section of capabilities it's
in due to its lower priority level (7).
2012-03-22 13:24:12 -07:00
Steve Beattie
a31e1349ce Subject: utils/: add check to ensure severity.db contains all
capabilities

This patch adds a new make target, check_severity_db, to the
utils/Makefile. It greps the severity.db for the presence of each
capability, as computed by the newly abstracted out variable in
common/Make.rules, and issues a build time error if it finds any
missing.

It also silences the check targets, so that only the output from them
will be emitted.
2012-03-22 13:23:19 -07:00
John Johansen
f4240fcc74 Rename and invert logic of is_null to is_accept to better reflect its use
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 13:21:55 -07:00
Steve Beattie
8eaeb44f56 Subject: abstract out cap and net proto generation to common/Make.rules
This patch abstracts out the generation of the lists of capabilities
and network protocol names to the common Make.rules file that is
included in most locations in the build tree, to allow it to be
re-used in the utils/ tree and possibly elsewhere.

It provides the lists in both make variables and as make targets.

It also sorts the resulting lists, which causes it to output differently
than the before case. I did confirm that the results for the generated
files used in the parser build were the same after taking the sorting
into account.
2012-03-22 13:19:27 -07:00
Steve Beattie
bfc1032fc1 Subject: toplevel makefile: correct location of libapparmor
This patch fixes an issue with the toplevel make clean target that did
not take into account where the libapparmor tree had been moved to.
2012-03-22 13:17:48 -07:00
Jamie Strandboge
65f90c0942 fix distro-specific apparmor.vim man page
Acked-By: Jamie Strandboge <jamie@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
Acked-by: Kees Cook <kees@ubuntu.com>
2012-03-22 15:15:20 -05:00
John Johansen
4fcd1f33dc Fix aa-exec file mode to be 751 so that it can be exec'd 2012-03-22 12:52:58 -07:00
John Johansen
86527a2f4c Fix the return size of aa_getprocattr
aa_getprocattr is returning the size of the buffer not the size of the
data read that it is supposed to return.  Also update the man page to
reflect the return value as documented in the functions, and update
the test cases to check the return value.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:58:18 -07:00
John Johansen
648166ecca Fix error case of aa_getprocattr to set buffers to NULL
While aa_getprocattr does return the documented error code on failure
the **buf and **mode parameters can point into the buffer that was
allocated and then discarded on failure.

Set them to null on failure so that even if the error code is ignored
they do not point to heap data.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:57:18 -07:00
John Johansen
2e3b5ff134 Fix mnt_flags passed for remount
Remount should not be screening off the set of flags it is.  They are
the set of flags that the kernel is masking out for make_type and
should not be used on remount. Instead just screen off the other cmds
that can have their own rules generated.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:55:58 -07:00
John Johansen
3c9cdfb841 rework the is_null test to not include deny
The deny information is not used as valid accept state information,
so remove it from the is_null test.  This does not change the dfa
generated but does result in the dumped information changing,
as states that don't have any accept information are no longer
reported as accepting. This is what changes the number of states
reported in the minimize tests.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:55:00 -07:00
John Johansen
e7f6e0f9f1 Fix dfa minimization around the nonmatching state
The same mappings routine had two bugs in it, that in practice haven't
manifested because of partition ordering during minimization.  The
result is that some states may fail comparison and split, resulting
in them not being eliminated when they could be.

The first is that direct comparison to the nonmatching state should
not be done as it is a candiate for elimination, instead its partion
should be compared against.  This simplifies the first test


The other error is the comparison
  if (rep->otherwise != nonmatching)

again this is wrong because nomatching should not be directly
compared against.  And again can result in the current rep->otherwise
not being eliminated/replaced by the partion.  Again resulting in
extra trap states.

These tests where original done the way they were because
 ->otherwise could be null, which was used to represent nonmatching.
The code was cleaned up a while ago to remove this, ->otherwise is
always a valid pointer now.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:50:35 -07:00
John Johansen
7fcbd543d7 Factor all the permissions dump code into a single perms method
Also make sure the perms method properly switches to hex and back to dec
as some of the previous perm dump code did not.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-22 07:49:43 -07:00
John Johansen
2f603cc73e Add the aa-exec command line utility
The aa-exec command can be used to launch an application under a specified
confinement, which may be different for what regular profile attachment
would apply.

Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-03-20 11:45:13 -07:00
Steve Beattie
69dc13efdf This patch adds testcases that confirm that using a bare
file,

rule will allow access to both the '/' directory and other directories.
2012-03-15 16:46:50 -07:00
John Johansen
456220db56 Bump revision and tag for 2.8-beta3 2012-03-15 12:57:13 -07:00
John Johansen
c50858a877 Update permission mapping for changes made to the upstream kernel patch.
The changes are around how user data is handled.

1. permissions are mapped before data is matched
2. If data is to be mapped a AA_CONT_MATCH flag is set in the permissions
   which allows data matching to continue.
3. If data auditing is to occur the AA_AUDIT_MNT_DATA flag is set

This allows better control over matching and auditing of data which can
be binary and should not be matched or audited

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-15 12:54:34 -07:00
John Johansen
a11efe838a Fix the bare file rule so that it grants access to to root
file, should grant access to all files paths on the system but it does
not currently allow access to /

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-15 12:16:56 -07:00
John Johansen
d6dc04d737 Fix pivot_root to support named transitions correctly
Rename the pivotroot rule to pivot_root to match the command and the fn
and fix it to support named transition correctly leveraging the parsing
action used for exec transitions.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-15 12:14:15 -07:00
John Johansen
feeea88a58 Fix the case where no flags match
Currently the backend doesn't like it (blows up) when the a vector entry is
empty.  For the case where no flags match build_mnt_flags generates an
alternation of an impossible entry and nothing

  (impossible|)

This provides the effect of a null entry without having an empty vector
entry.  Unfortunately the impossible entry is not correct.

Note: how this is done needs to be changed and fixed in the next release
this is just a minimal patch to get it working for 2.8


Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-15 12:10:35 -07:00
John Johansen
36d44a3b25 Fix the mount flags set generated by the parser
When generating the flag set the parser was not generating the complete
set when flags where not consecutive.  This is because the len value
was not being reset for each flag considered, so once it was set for
a flag, then the next flag would have to be set to reset it else the
output string was still incremented by the old len value.

  Eg.
  echo "/t { mount options=rbind, }" | apparmor_parser -QT -D rule-exprs

  results in
  rule: \x07[^\000]*\x00[^\000]*\x00[^\000]*\x00\x0d  ->

  however \x0d only covers the bind and not the recursive flag

This is fixed by adding a continue to the flags generation loop for the
else case.

  resulting the dump from above generating

  rule: \x07[^\000]*\x00[^\000]*\x00[^\000]*\x00\x0d\x0f  ->

  \x0d\x0f covers both of the required flags

Also fix the flags output to allow for the allow any flags case.  This
was being screened out.  By masking the flags even when no flags where
specified.

  this results in a difference of

  echo "/t { mount, }" | apparmor_parser -QT -D rule-exprs

    rule: \x07[^\000]*\x00[^\000]*\x00[^\000]*\x00(\x01|)(\x02|)(\x03|)(\x04|)(\x05|)\x00[^\000]*

  becoming
    \x07[^\000]*\x00[^\000]*\x00[^\000]*\x00[^\000]*\x00[^\000]*

  which is simplified and covers all permissions vs. the first rule output

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-15 09:03:48 -07:00
John Johansen
fc5f4dc86f Revert commit: -r 1955 Default profiles to be chroot relative
This commit causes policy problems because we do not have chroot rules
and policy extension to support it.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-15 08:59:56 -07:00
John Johansen
59c0bb0f46 Fix minimize.sh test to screen out more parser error messages by grepping
closer to the expected -O dfa-states output
2012-03-09 06:48:03 -08:00
John Johansen
fae11e12cf Mark the minimize test as executable 2012-03-09 05:54:54 -08:00
John Johansen
e0a74881bf Bump version for 2.8-beta2 2012-03-09 04:44:37 -08:00
John Johansen
ce38857061 tag apparmor_2.8-beta2 2012-03-09 04:27:47 -08:00
John Johansen
c8e134930f Fix the "Kernel features are written to cache:" test
the cache test is failing because it assumes that kernel features are
stored in a file instead of a directory

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:25:03 -08:00
John Johansen
3876299fa0 Fix caching when used with a newer kernel with the feature directory
On newer kernels the features directory causes the creation of a
cache/.feature file that contains newline characters.  This causes the
feature comparison to fail, because get_flags_string() uses fgets
which stop reading in the feature file after the first newline.

This caches the features comparision to compare a single line of the
file against the full kernel feature directory resulting in caching
failure.

Worse this also means the cache won't get updated as the parser doesn't
change what set gets caches after the .feature file gets created.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:24:20 -08:00
John Johansen
b0b2bde160 Fix permissions attached to the bare file keyword
file,

was not given the correct permissions.  It was only being given the owner
set of permissions.  This would result in rejects when trying look at
files owned by other users

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:23:25 -08:00
John Johansen
3a1b7bb54c Fix infinite loop bug in normalization.
There are some rare occassions, when lots of alternations are used that
tree simplification can result in an expression of
  (E | (E | E)) or (E . (E . E))   where E is the epsnode

both of these expressions will lead to an inifinite loop in normalize_tree
as the epsnode test
       if ((&epsnode == t->child[dir]) &&
       	        (&epsnode != t->child[!dir]) &&
		      	         dynamic_cast<TwoChildNode *>(t)) {

and the tree node rotation test
    	} else if ((dynamic_cast<AltNode *>(t) &&
	           dynamic_cast<AltNode *>(t->child[dir])) ||
		   			   (dynamic_cast<CatNode *>(t) &&
					   			    dynamic_cast<CatNode *>(t->child[dir]))) {

end up undoing each others work, ie.

                eps flip                 rotate
  (E | (E | E)) --------> ((E | E) | E) -------> (E | (E | E))

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:22:42 -08:00
John Johansen
04ef92ca94 Fix a couple build warnings in mount.c
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:21:54 -08:00
John Johansen
d7a6860a23 Fix Make file for mount.c so that warnings are emitted during a build
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:21:06 -08:00
John Johansen
5e361a4a05 Fix dfa minimization to deal with exec conflicts
Minimization was failing because it was too agressive.  It was minimizing
as if there was only 1 accept condition.  This allowed it to remove more
states but at the cost of loosing unique permission sets, they where
being combined into single commulative perms.  This means that audit,
deny, xtrans, ... info on one path would be applied to all other paths
that it was combined with during minimization.

This means that we need to retain the unique accept states, not allowing
them to be combined into a single state.  To do this we put each unique
permission set into its own partition at the start of minimization.

The states within a partition have the  same permissions and can be combined
within the other states in the partition as the loss of unique path
information is will not result in a conflict.

This is similar to what perm hashing used to do but deny information is
still being correctly applied and carried.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:20:19 -08:00
John Johansen
cf5f7ef9c2 Fix the x intersection consistency test
The in x intersection consistency test for minimization was failing because
it was screening off the AA_MAY_EXEC permission before passing the exec
information to the consistency test fn.  This resulted in the consistency
test fn not testing the consistency because it treated the permission set
as not having x permissions.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:19:24 -08:00
John Johansen
811d8aefa3 Fix transition character reporting of dfa dumps
Make them report a hex value strings instead of the default C++
\vvvvv

Make them consistent,
- Dump to report the default transition and what isn't transitioned
  on it.


Signed-off-by: John Johansen <john.johansen@canonical.com>
2012-03-09 04:18:35 -08:00
John Johansen
37f446dd79 Fix/cleanup the permission reporting for the dfa dumps
The permission reporting was not reporting the full set of permission
flags and was inconsistent between the dump routines.

Report permissions as the quad (allow/deny/audit/quiet) in hex.

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:17:47 -08:00
John Johansen
1a01b5c296 Fix/cleanup the dfa dump routines output to provide state label
Fix the transitions states output so that they output the state label
instead of the state address.  That is
  {1} -> 0x10831a0:  /
now becomes
  {1} -> {2}:  /

Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-09 04:14:34 -08:00
Jamie Strandboge
b47197b881 allow read access to ~/.drirc
Bug-Ubuntu: https://launchpad.net/bugs/941506

Acked-By: Jamie Strandboge <jamie@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-02 15:09:21 -06:00
Jamie Strandboge
3e5ae57164 Description: allow read access to /usr/share/texmf/fonts
Bug-Ubuntu: https://launchpad.net/bugs/941503

Acked-By: Jamie Strandboge <jamie@canonical.com>
Acked-By: Steve Beattie <sbeattie@ubuntu.com>
2012-03-02 15:08:03 -06:00
Jamie Strandboge
a0048ec064 ubuntu-browsers.d/java: update to fix LP: #945019 2012-03-02 13:03:04 -06:00
Jamie Strandboge
c35e10f875 fix path to java in ubuntu-browsers.d/java
Bug-Ubuntu: https://launchpad.net/bugs/943161

Acked-By: Jamie Strandboge <jamie@canonical.com>
2012-03-02 11:18:11 -06:00
Jamie Strandboge
46d9aae952 include /etc/drirc in the X abstraction
Bug-Ubuntu: https://launchpad.net/bugs/918879

Acked-By: Jamie Strandboge <jamie@canonical.com>
Signed-off-by: Kees Cook <kees@ubuntu.com>
2012-02-28 12:50:20 -06:00
John Johansen
bd67bb909a tag apparmor 2.8 beta1 release as 2.7.99 2012-02-24 04:38:24 -08:00
38 changed files with 979 additions and 548 deletions

View File

@@ -7,7 +7,7 @@ include common/Make.rules
DIRS=parser \
profiles \
utils \
changehat/libapparmor \
libraries/libapparmor \
changehat/mod_apparmor \
changehat/pam_apparmor \
tests

View File

@@ -150,6 +150,40 @@ _clean:
-rm -f ${NAME}-${VERSION}-*.tar.gz
-rm -f ${MANPAGES} *.[0-9].gz ${HTMLMANPAGES} pod2htm*.tmp
# =====================
# generate list of capabilities based on
# /usr/include/sys/capabilities.h for use in multiple locations in
# the source tree
# =====================
# emits defined capabilities in a simple list, e.g. "CAP_NAME CAP_NAME2"
CAPABILITIES=$(shell echo "\#include <sys/capability.h>" | cpp -dM | LC_ALL=C sed -n -e '/CAP_EMPTY_SET/d' -e 's/^\#define[ \t]\+CAP_\([A-Z0-9_]\+\)[ \t]\+\([0-9xa-f]\+\)\(.*\)$$/CAP_\1/p' | sort)
.PHONY: list_capabilities
list_capabilities: /usr/include/linux/capability.h
@echo "$(CAPABILITIES)"
# =====================
# generate list of network protocols based on
# sys/socket.h for use in multiple locations in
# the source tree
# =====================
# These are the families that it doesn't make sense for apparmor
# to mediate. We use PF_ here since that is what is required in
# bits/socket.h, but we will rewrite these as AF_.
FILTER_FAMILIES=PF_UNSPEC PF_UNIX PF_LOCAL PF_NETLINK
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')
# emits the AF names in a "AF_NAME NUMBER," pattern
AF_NAMES=$(shell echo "\#include <sys/socket.h>" | cpp -dM | LC_ALL=C sed -n -e '/$(__FILTER)/d' -e 's/^\#define[ \t]\+PF_\([A-Z0-9_]\+\)[ \t]\+\([0-9]\+\).*$$/AF_\1 \2,/p' | sort -n -k2)
.PHONY: list_af_names
list_af_names:
@echo "$(AF_NAMES)"
# =====================
# manpages
# =====================

View File

@@ -1 +1 @@
2.7.99
2.7.101

View File

@@ -69,7 +69,8 @@ does not handle buffer allocation.
=head1 RETURN VALUE
On success zero is returned. On error, -1 is returned, and
On success size of data placed in the buffer is returned, this includes the
mode if present and any terminating characters. On error, -1 is returned, and
errno(3) is set appropriately.
=head1 ERRORS

View File

@@ -278,11 +278,12 @@ int aa_getprocattr(pid_t tid, const char *attr, char **buf, char **mode)
if (rc == -1) {
free(buffer);
size = -1;
*buf = NULL;
*mode = NULL;
} else
*buf = buffer;
return size;
return rc;
}
static int setprocattr(pid_t tid, const char *attr, const char *buf, int len)
@@ -617,6 +618,7 @@ int aa_getpeercon(int fd, char **con)
if (rc == -1) {
free(buffer);
*con = NULL;
size = -1;
} else
*con = buffer;

View File

@@ -200,26 +200,25 @@ parser_alias.o: parser_alias.c parser.h
parser_common.o: parser_common.c parser.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
mount.o: mount.c mount.h parser.h immunix.h
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
parser_version.h: Makefile
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
@mv -f .ver $@
# These are the families that it doesn't make sense for apparmor to mediate.
# We use PF_ here since that is what is required in bits/socket.h, but we will
# rewrite these as AF_.
FILTER_FAMILIES=PF_MAX PF_UNSPEC PF_UNIX PF_LOCAL PF_NETLINK
__FILTER=$(shell echo $(strip $(FILTER_FAMILIES)) | sed -e 's/ /\\\|/g')
# af_names and capabilities generation has moved to common/Make.rules,
# as well as the filtering that occurs for network protocols that
# apparmor should not mediate.
.PHONY: af_names.h
af_names.h:
echo "#include <sys/socket.h>" | cpp -dM | 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" > $@
echo "#include <sys/socket.h>" | cpp -dM | LC_ALL=C sed -n -e "s/^\#define[ \\t]\\+PF_MAX[ \\t]\\+\\([0-9]\\+\\)\\+.*/#define AA_AF_MAX \\1\n/p" >> $@
echo "$(AF_NAMES)" | LC_ALL=C sed -n -e 's/[ \t]\?AF_MAX[ \t]\+[0-9]\+,//g' -e 's/[ \t]\+\?AF_\([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\n/pg' > $@
echo "$(AF_NAMES)" | LC_ALL=C sed -n -e 's/.*,[ \t]\+AF_MAX[ \t]\+\([0-9]\+\),\?.*/#define AA_AF_MAX \1\n/p' >> $@
# 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" $< > $@
echo "$(CAPABILITIES)" | LC_ALL=C sed -n -e "s/[ \\t]\\?CAP_\\([A-Z0-9_]\\+\\)/\{\"\\L\\1\", \\UCAP_\\1\},\\n/pg" > $@
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
$(CC) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS)

View File

@@ -16,7 +16,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.
# ----------------------------------------------------------------------
@@ -28,13 +28,11 @@ apparmor.vim - vim syntax highlighting file for AppArmor profiles
=head1 SYNOPSIS
The SUSE vim package is configured to automatically use syntax
highlighting for AppArmor policies stored in /etc/apparmor.d/ and the
extra policies stored in /etc/apparmor/profiles/extras/. If you wish to
use the syntax highlighting in a specific vim session, you may run:
Your system may be configured to automatically use syntax highlighting
for installed AppArmor policies. If not, you can enable syntax highlighting in
a specific vim session by performing:
:syntax on
:setf apparmor
:set syntax=apparmor
=head1 DESCRIPTION

View File

@@ -96,6 +96,9 @@
#define ALL_AA_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE)
#define ALL_USER_EXEC (AA_USER_EXEC | AA_USER_EXEC_TYPE)
#define ALL_OTHER_EXEC (AA_OTHER_EXEC | AA_OTHER_EXEC_TYPE)
#define AA_LINK_BITS ((AA_MAY_LINK << AA_USER_SHIFT) | \
(AA_MAY_LINK << AA_OTHER_SHIFT))

View File

@@ -223,7 +223,7 @@ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
tree->dump(cerr);
if (deny)
cerr << " deny";
cerr << " (" << hex << allow <<"/" << audit << dec << ")";
cerr << " (0x" << hex << allow <<"/" << audit << dec << ")";
accept->dump(cerr);
cerr << "\n\n";
}

View File

@@ -187,14 +187,22 @@ void normalize_tree(Node *t, int dir)
return;
for (;;) {
if ((&epsnode == t->child[dir]) &&
(&epsnode != t->child[!dir]) &&
dynamic_cast<TwoChildNode *>(t)) {
if (dynamic_cast<TwoChildNode *>(t) &&
(&epsnode == t->child[dir]) &&
(&epsnode != t->child[!dir])) {
// (E | a) -> (a | E)
// Ea -> aE
Node *c = t->child[dir];
t->child[dir] = t->child[!dir];
t->child[!dir] = c;
// Test for E | (E | E) and E . (E . E) which will
// result in an infinite loop
Node *c = t->child[!dir];
if (dynamic_cast<TwoChildNode *>(c) &&
&epsnode == c->child[dir] &&
&epsnode == c->child[!dir]) {
c->release();
c = &epsnode;
}
t->child[dir] = c;
t->child[!dir] = &epsnode;
// Don't break here as 'a' may be a tree that
// can be pulled up.
} else if ((dynamic_cast<AltNode *>(t) &&

View File

@@ -340,14 +340,8 @@ void DFA::remove_unreachable(dfaflags_t flags)
cerr << "unreachable: " << **i;
if (*i == start)
cerr << " <==";
if (!(*i)->perms.is_null()) {
cerr << " (0x" << hex
<< (*i)->perms.allow << " "
<< (*i)->perms.deny << " "
<< (*i)->perms.audit << " "
<< (*i)->perms.quiet << dec
<< ')';
}
if ((*i)->perms.is_accept())
(*i)->perms.dump(cerr);
cerr << "\n";
}
State *current = *i;
@@ -366,26 +360,17 @@ void DFA::remove_unreachable(dfaflags_t flags)
/* test if two states have the same transitions under partition_map */
bool DFA::same_mappings(State *s1, State *s2)
{
if (s1->otherwise != nonmatching) {
if (s2->otherwise == nonmatching)
return false;
Partition *p1 = s1->otherwise->partition;
Partition *p2 = s2->otherwise->partition;
if (p1 != p2)
return false;
} else if (s2->otherwise != nonmatching) {
if (s1->otherwise->partition != s2->otherwise->partition)
return false;
}
if (s1->trans.size() != s2->trans.size())
return false;
for (StateTrans::iterator j1 = s1->trans.begin(); j1 != s1->trans.end(); j1++) {
StateTrans::iterator j2 = s2->trans.find(j1->first);
if (j2 == s2->trans.end())
return false;
Partition *p1 = j1->second->partition;
Partition *p2 = j2->second->partition;
if (p1 != p2)
if (j1->second->partition != j2->second->partition)
return false;
}
@@ -431,7 +416,7 @@ int DFA::apply_and_clear_deny(void)
/* minimize the number of dfa states */
void DFA::minimize(dfaflags_t flags)
{
map<size_t, Partition *> perm_map;
map<pair<uint64_t, size_t>, Partition *> perm_map;
list<Partition *> partitions;
/* Set up the initial partitions
@@ -448,20 +433,18 @@ void DFA::minimize(dfaflags_t flags)
int final_accept = 0;
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
size_t hash = 0;
if (!(*i)->perms.is_null())
/* combine all states carrying accept info together
into an single initial parition */
hash = 1;
uint64_t permtype = ((uint64_t) (PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny)) << 32) | (uint64_t) (*i)->perms.allow;
if (flags & DFA_CONTROL_MINIMIZE_HASH_TRANS)
hash |= hash_trans(*i) << 1;
map<size_t, Partition *>::iterator p = perm_map.find(hash);
hash |= hash_trans(*i);
pair<uint64_t, size_t> group = make_pair(permtype, hash);
map<pair<uint64_t, size_t>, Partition *>::iterator p = perm_map.find(group);
if (p == perm_map.end()) {
Partition *part = new Partition();
part->push_back(*i);
perm_map.insert(make_pair(hash, part));
perm_map.insert(make_pair(group, part));
partitions.push_back(part);
(*i)->partition = part;
if (hash & 1)
if (permtype)
accept_count++;
} else {
(*i)->partition = p->second;
@@ -555,10 +538,8 @@ void DFA::minimize(dfaflags_t flags)
cerr << *rep << " : ";
/* update representative state's transitions */
if (rep->otherwise != nonmatching) {
Partition *partition = rep->otherwise->partition;
rep->otherwise = *partition->begin();
}
rep->otherwise = *rep->otherwise->partition->begin();
for (StateTrans::iterator c = rep->trans.begin(); c != rep->trans.end(); c++) {
Partition *partition = c->second->partition;
c->second = *partition->begin();
@@ -575,7 +556,7 @@ void DFA::minimize(dfaflags_t flags)
(*i)->label = -1;
rep->perms.add((*i)->perms);
}
if (!rep->perms.is_null())
if (rep->perms.is_accept())
final_accept++;
//if ((*p)->size() > 1)
//cerr << "\n";
@@ -630,28 +611,52 @@ out:
void DFA::dump(ostream & os)
{
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if (*i == start || !(*i)->perms.is_null()) {
if (*i == start || (*i)->perms.is_accept()) {
os << **i;
if (*i == start)
os << " <==";
if ((*i)->perms.allow) {
os << " (0x" << hex << (*i)->perms.allow << " "
<< (*i)->perms.deny << " "
<< (*i)->perms.audit << " "
<< (*i)->perms.audit << dec << ')';
}
os << " <== (allow/deny/audit/quiet)";
if ((*i)->perms.is_accept())
(*i)->perms.dump(os);
os << "\n";
}
}
os << "\n";
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
if ((*i)->otherwise != nonmatching)
os << **i << " -> " << (*i)->otherwise << "\n";
Chars excluded;
for (StateTrans::iterator j = (*i)->trans.begin();
j != (*i)->trans.end(); j++) {
os << **i << " -> " << j->second << ": "
<< j->first << "\n";
if (j->second == nonmatching) {
excluded.insert(j->first);
} else {
os << **i;
if ((*i)->perms.is_accept())
os << " ", (*i)->perms.dump(os);
os << " -> " << *(j)->second << ": 0x"
<< hex << (int) j->first;
if (isprint(j->first))
os << " " << j->first;
os << dec << "\n";
}
}
if ((*i)->otherwise != nonmatching) {
os << **i;
if ((*i)->perms.is_accept())
os << " ", (*i)->perms.dump(os);
os << " -> " << *(*i)->otherwise << ": [";
if (!excluded.empty()) {
os << "^";
for (Chars::iterator k = excluded.begin();
k != excluded.end(); k++) {
if (isprint(*k))
os << *k;
else
os << "\\0x" << hex << (int) *k << dec;
}
}
os << "]\n";
}
}
os << "\n";
@@ -672,10 +677,10 @@ void DFA::dump_dot_graph(ostream & os)
if (*i == start) {
os << "\t\tstyle=bold" << "\n";
}
uint32_t perms = (*i)->perms.allow;
if (perms) {
os << "\t\tlabel=\"" << **i << "\\n("
<< perms << ")\"" << "\n";
if ((*i)->perms.is_accept()) {
os << "\t\tlabel=\"" << **i << "\\n";
(*i)->perms.dump(os);
os << "\"\n";
}
os << "\t]" << "\n";
}
@@ -688,18 +693,26 @@ void DFA::dump_dot_graph(ostream & os)
else {
os << "\t\"" << **i << "\" -> \"" << *j->second
<< "\" [" << "\n";
os << "\t\tlabel=\"" << j->first << "\"\n";
os << "\t]" << "\n";
os << "\t\tlabel=\"";
if (isprint(j->first))
os << j->first;
else
os << "\\0x" << hex << (int) j->first << dec;
os << "\"\n\t]" << "\n";
}
}
if ((*i)->otherwise != nonmatching) {
os << "\t\"" << **i << "\" -> \"" << *(*i)->otherwise
os << "\t\"" << **i << "\" -> \"" << *(*i)->otherwise
<< "\" [" << "\n";
if (!excluded.empty()) {
os << "\t\tlabel=\"[^";
for (Chars::iterator i = excluded.begin();
i != excluded.end(); i++) {
os << *i;
if (isprint(*i))
os << *i;
else
os << "\\0x" << hex << (int) *i << dec;
}
os << "]\"" << "\n";
}

View File

@@ -43,15 +43,22 @@ class perms_t {
public:
perms_t(void) throw(int): allow(0), deny(0), audit(0), quiet(0), exact(0) { };
bool is_null(void) { return !(allow | deny | audit | quiet); }
bool is_accept(void) { return (allow | audit | quiet); }
void dump(ostream &os)
{
os << " (0x " << hex
<< allow << "/" << deny << "/" << audit << "/" << quiet
<< ')' << dec;
}
void clear(void) { allow = deny = audit = quiet = 0; }
void add(perms_t &rhs)
{
deny |= rhs.deny;
if (!is_merged_x_consistent(allow & AA_USER_EXEC_TYPE,
rhs.allow & AA_USER_EXEC_TYPE)) {
if (!is_merged_x_consistent(allow & ALL_USER_EXEC,
rhs.allow & ALL_USER_EXEC)) {
if ((exact & AA_USER_EXEC_TYPE) &&
!(rhs.exact & AA_USER_EXEC_TYPE)) {
/* do nothing */
@@ -64,8 +71,8 @@ public:
} else
allow |= rhs.allow & AA_USER_EXEC_TYPE;
if (!is_merged_x_consistent(allow & AA_OTHER_EXEC_TYPE,
rhs.allow & AA_OTHER_EXEC_TYPE)) {
if (!is_merged_x_consistent(allow & ALL_OTHER_EXEC,
rhs.allow & ALL_OTHER_EXEC)) {
if ((exact & AA_OTHER_EXEC_TYPE) &&
!(rhs.exact & AA_OTHER_EXEC_TYPE)) {
/* do nothing */
@@ -79,10 +86,10 @@ public:
allow |= rhs.allow & AA_OTHER_EXEC_TYPE;
allow = (allow | (rhs.allow & ~ALL_AA_EXEC_TYPE)) & ~deny;
allow = (allow | (rhs.allow & ~ALL_AA_EXEC_TYPE));
audit |= rhs.audit;
quiet = (quiet | rhs.quiet) & deny;
quiet = (quiet | rhs.quiet);
/*
if (exec & AA_USER_EXEC_TYPE &&
(exec & AA_USER_EXEC_TYPE) != (allow & AA_USER_EXEC_TYPE))
@@ -99,7 +106,7 @@ public:
allow &= ~deny;
quiet &= deny;
deny = 0;
return is_null();
return !is_accept();
}
return 0;
}

View File

@@ -190,8 +190,6 @@
* mount options=ro /dev/foo, #allow mounting /dev/foo as read only
* mount options=(ro,foo) /dev/foo,
* mount options=ro options=foo /dev/foo,
* mount -> /mnt/**, # allow any mount on dirs under /mnt/
* mount options=ro -> /mnt/**, # allow any read only mount under /mnt/
* mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/
*
*----------------------------------------------------------------------
@@ -277,7 +275,7 @@ static struct mnt_keyword_table mnt_opts_table[] = {
{"user", 0, MS_NOUSER},
{"nouser", MS_NOUSER, 0},
{ }
{NULL, 0, 0}
};
static struct mnt_keyword_table mnt_conds_table[] = {
@@ -286,7 +284,7 @@ static struct mnt_keyword_table mnt_conds_table[] = {
{"fstype", MNT_SRC_OPT | MNT_DST_OPT, MNT_COND_FSTYPE},
{"vfstype", MNT_SRC_OPT | MNT_DST_OPT, MNT_COND_FSTYPE},
{ }
{NULL, 0, 0}
};
static int find_mnt_keyword(struct mnt_keyword_table *table, const char *name)
@@ -391,7 +389,7 @@ static struct value_list *extract_options(struct cond_entry **conds)
}
struct mnt_entry *new_mnt_entry(struct cond_entry *src_conds, char *device,
struct cond_entry *dst_conds, char *mnt_point,
struct cond_entry *dst_conds __unused, char *mnt_point,
int allow)
{
/* FIXME: dst_conds are ignored atm */
@@ -399,8 +397,6 @@ struct mnt_entry *new_mnt_entry(struct cond_entry *src_conds, char *device,
struct mnt_entry *ent;
ent = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
if (ent) {
unsigned int rclear, aclear;
ent->mnt_point = mnt_point;
ent->device = device;
ent->dev_type = extract_fstype(&src_conds);
@@ -420,8 +416,8 @@ struct mnt_entry *new_mnt_entry(struct cond_entry *src_conds, char *device,
ent->inv_flags = 0;
} else if (!(ent->flags | ent->inv_flags)) {
/* no flag options, and not remount, allow everything */
ent->flags = 0xffffffff;
ent->inv_flags = 0xffffffff;
ent->flags = MS_ALL_FLAGS;
ent->inv_flags = MS_ALL_FLAGS;
}
ent->allow = allow;

View File

@@ -85,7 +85,6 @@
MS_BORN | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| \
MS_KERNMOUNT | MS_STRICTATIME)
#define MS_REMOUNT_FLAGS (MS_REMOUNT | MNT_FLAGS)
#define MS_BIND_FLAGS (MS_BIND | MS_REC)
#define MS_MAKE_FLAGS ((MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED | \
MS_REC) | (MS_ALL_FLAGS & ~(MNT_FLAGS)))
@@ -93,6 +92,7 @@
#define MS_CMDS (MS_MOVE | MS_REMOUNT | MS_BIND | MS_PRIVATE | MS_SLAVE | \
MS_SHARED | MS_UNBINDABLE)
#define MS_REMOUNT_FLAGS (MS_ALL_FLAGS & ~(MS_CMDS & ~MS_REMOUNT))
#define MNT_SRC_OPT 1
#define MNT_DST_OPT 2
@@ -103,8 +103,10 @@
#define AA_MAY_PIVOTROOT 1
#define AA_MAY_MOUNT 2
#define AA_MAY_UMOUNT 4
#define AA_DUMMY_REMOUNT 32 /* dummy perm for remount rule - is remapped
* to a mount option*/
#define AA_MATCH_CONT 0x40
#define AA_AUDIT_MNT_DATA AA_MATCH_CONT
#define AA_DUMMY_REMOUNT 0x40000000 /* dummy perm for remount rule - is
* remapped to a mount option*/
struct mnt_entry {

View File

@@ -768,7 +768,7 @@ static char *handle_features_dir(const char *filename, char **buffer, int size,
}
pos = snprintf_buffer(*buffer, pos, size, " }\n");
pos = snprintf_buffer(*buffer, pos, size, "}\n");
}
if (dirent_path)
free(dirent_path);
@@ -844,6 +844,7 @@ out:
static void get_flags_string(char **flags, char *flags_file) {
char *pos;
FILE *f = NULL;
size_t size;
/* abort if missing or already set */
if (!flags || *flags)
@@ -857,8 +858,10 @@ static void get_flags_string(char **flags, char *flags_file) {
if (!*flags)
goto fail;
if (!fgets(*flags, FLAGS_STRING_SIZE, f))
size = fread(*flags, 1, FLAGS_STRING_SIZE - 1, f);
if (!size || ferror(f))
goto fail;
(*flags)[size] = 0;
fclose(f);
pos = strstr(*flags, "change_hat=");

View File

@@ -83,7 +83,7 @@ static struct keyword_table keyword_table[] = {
{"remount", TOK_REMOUNT},
{"umount", TOK_UMOUNT},
{"unmount", TOK_UMOUNT},
{"pivotroot", TOK_PIVOTROOT},
{"pivot_root", TOK_PIVOTROOT},
/* terminate */
{NULL, 0}
};

View File

@@ -692,7 +692,7 @@ static int build_mnt_flags(char *buffer, int size, unsigned int flags,
char *p = buffer;
int i, len = 0;
if (flags == 0xffffffff) {
if (flags == MS_ALL_FLAGS) {
/* all flags are optional */
len = snprintf(p, size, "[^\\000]*");
if (len < 0 || len >= size)
@@ -704,7 +704,8 @@ static int build_mnt_flags(char *buffer, int size, unsigned int flags,
len = snprintf(p, size, "(\\x%02x|)", i + 1);
else if (flags & (1 << i))
len = snprintf(p, size, "\\x%02x", i + 1);
/* else no entry = not set */
else /* no entry = not set */
continue;
if (len < 0 || len >= size)
return FALSE;
@@ -712,6 +713,7 @@ static int build_mnt_flags(char *buffer, int size, unsigned int flags,
size -= len;
}
/* this needs to go once the backend is updated. */
if (buffer == p) {
/* match nothing - use impossible 254 as regex parser doesn't
* like the empty string
@@ -719,7 +721,7 @@ static int build_mnt_flags(char *buffer, int size, unsigned int flags,
if (size < 9)
return FALSE;
strcpy(p, "(\\0xfe|)");
strcpy(p, "(\\xfe|)");
}
return TRUE;
@@ -774,6 +776,7 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
char optsbuf[PATH_MAX + 3];
char *p, *vec[5];
int count = 0;
unsigned int flags, inv_flags;
/* a single mount rule may result in multiple matching rules being
* created in the backend to cover all the possible choices
@@ -781,6 +784,7 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_REMOUNT)
&& !entry->device && !entry->dev_type) {
int allow;
/* remount can't be conditional on device and type */
p = mntbuf;
/* rule class single byte header */
@@ -801,18 +805,43 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
vec[1] = devbuf;
/* skip type */
vec[2] = devbuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_REMOUNT_FLAGS,
entry->inv_flags & MS_REMOUNT_FLAGS))
flags = entry->flags;
inv_flags = entry->inv_flags;
if (flags != MS_ALL_FLAGS)
flags &= MS_REMOUNT_FLAGS;
if (inv_flags != MS_ALL_FLAGS)
flags &= MS_REMOUNT_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
goto fail;
vec[3] = flagsbuf;
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
goto fail;
vec[4] = optsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 5, vec, dfaflags))
if (entry->opts)
allow = AA_MATCH_CONT;
else
allow = entry->allow;
/* rule for match without required data || data MATCH_CONT */
if (!aare_add_rule_vec(dfarules, entry->deny, allow,
entry->audit | AA_AUDIT_MNT_DATA, 4,
vec, dfaflags))
goto fail;
count++;
if (entry->opts) {
/* rule with data match required */
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
goto fail;
vec[4] = optsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny,
entry->allow,
entry->audit | AA_AUDIT_MNT_DATA,
5, vec, dfaflags))
goto fail;
count++;
}
}
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_BIND)
&& !entry->dev_type && !entry->opts) {
@@ -829,9 +858,14 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
goto fail;
vec[2] = typebuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_BIND_FLAGS,
entry->inv_flags & MS_BIND_FLAGS))
flags = entry->flags;
inv_flags = entry->inv_flags;
if (flags != MS_ALL_FLAGS)
flags &= MS_BIND_FLAGS;
if (inv_flags != MS_ALL_FLAGS)
flags &= MS_BIND_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
goto fail;
vec[3] = flagsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
@@ -856,9 +890,14 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
goto fail;
vec[1] = devbuf;
vec[2] = devbuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_MAKE_FLAGS,
entry->inv_flags & MS_MAKE_FLAGS))
flags = entry->flags;
inv_flags = entry->inv_flags;
if (flags != MS_ALL_FLAGS)
flags &= MS_MAKE_FLAGS;
if (inv_flags != MS_ALL_FLAGS)
flags &= MS_MAKE_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
goto fail;
vec[3] = flagsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
@@ -884,9 +923,14 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
goto fail;
vec[2] = typebuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & MS_MOVE_FLAGS,
entry->inv_flags & MS_MOVE_FLAGS))
flags = entry->flags;
inv_flags = entry->inv_flags;
if (flags != MS_ALL_FLAGS)
flags &= MS_MOVE_FLAGS;
if (inv_flags != MS_ALL_FLAGS)
flags &= MS_MOVE_FLAGS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
goto fail;
vec[3] = flagsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
@@ -896,6 +940,7 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
}
if ((entry->allow & AA_MAY_MOUNT) &&
(entry->flags | entry->inv_flags) & ~MS_CMDS) {
int allow;
/* generic mount if flags are set that are not covered by
* above commands
*/
@@ -911,18 +956,41 @@ static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
if (!build_list_val_expr(typebuf, PATH_MAX+2, entry->dev_type))
goto fail;
vec[2] = typebuf;
if (!build_mnt_flags(flagsbuf, PATH_MAX,
entry->flags & ~MS_CMDS,
entry->inv_flags & ~MS_CMDS))
flags = entry->flags;
inv_flags = entry->inv_flags;
if (flags != MS_ALL_FLAGS)
flags &= ~MS_CMDS;
if (inv_flags != MS_ALL_FLAGS)
flags &= ~MS_CMDS;
if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags))
goto fail;
vec[3] = flagsbuf;
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
goto fail;
vec[4] = optsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
entry->audit, 5, vec, dfaflags))
if (entry->opts)
allow = AA_MATCH_CONT;
else
allow = entry->allow;
/* rule for match without required data || data MATCH_CONT */
if (!aare_add_rule_vec(dfarules, entry->deny, allow,
entry->audit | AA_AUDIT_MNT_DATA, 4,
vec, dfaflags))
goto fail;
count++;
if (entry->opts) {
/* rule with data match required */
if (!build_mnt_opts(optsbuf, PATH_MAX, entry->opts))
goto fail;
vec[4] = optsbuf;
if (!aare_add_rule_vec(dfarules, entry->deny,
entry->allow,
entry->audit | AA_AUDIT_MNT_DATA,
5, vec, dfaflags))
goto fail;
count++;
}
}
if (entry->allow & AA_MAY_UMOUNT) {
p = mntbuf;

View File

@@ -435,10 +435,6 @@ flagvals: flagvals flagval
(PATH_CHROOT_REL | PATH_NS_REL))
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
if (!($1.path & PATH_NS_REL))
/* default to chroot relative profiles */
$1.path |= PATH_CHROOT_REL;
if (($1.path & (PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED)) ==
(PATH_MEDIATE_DELETED | PATH_DELEGATE_DELETED))
yyerror(_("Profile flag mediate_deleted conflicts with delegate_deleted"));
@@ -963,12 +959,14 @@ frule: file_mode opt_subset_flag id_or_var opt_named_transition TOK_END_OF_RULE
file_rule: TOK_FILE TOK_END_OF_RULE
{
char *path = strdup("/**");
char *path = strdup("/{**,}");
int perms = ((AA_BASE_PERMS & ~AA_EXEC_TYPE) |
(AA_EXEC_INHERIT | AA_MAY_EXEC));
/* duplicate to other permission set */
perms |= perms << AA_OTHER_SHIFT;
if (!path)
yyerror(_("Memory allocation error."));
$$ = do_file_rule(NULL, path, ((AA_BASE_PERMS & ~AA_EXEC_TYPE) |
(AA_EXEC_INHERIT | AA_MAY_EXEC)),
NULL, NULL);
$$ = do_file_rule(NULL, path, perms, NULL, NULL);
}
| opt_file file_rule_tail { $$ = $2; }
@@ -1114,14 +1112,23 @@ mnt_rule: TOK_UMOUNT opt_conds opt_id TOK_END_OF_RULE
$$ = do_mnt_rule($2, NULL, NULL, $3, AA_MAY_UMOUNT);
}
mnt_rule: TOK_PIVOTROOT opt_conds opt_id TOK_END_OF_RULE
mnt_rule: TOK_PIVOTROOT opt_conds opt_id opt_named_transition TOK_END_OF_RULE
{
$$ = do_pivot_rule($2, $3, NULL);
}
char *name = NULL;
if ($4.present && $4.namespace) {
name = malloc(strlen($4.namespace) +
strlen($4.name) + 3);
if (!name) {
PERROR("Memory allocation error\n");
exit(1);
}
sprintf(name, ":%s:%s", $4.namespace, $4.name);
free($4.namespace);
free($4.name);
} else if ($4.present)
name = $4.name;
mnt_rule: TOK_PIVOTROOT opt_conds opt_id TOK_ARROW TOK_ID TOK_END_OF_RULE
{
$$ = do_pivot_rule($2, $3, $5);
$$ = do_pivot_rule($2, $3, name);
}
hat_start: TOK_CARET {}
@@ -1313,18 +1320,20 @@ struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root,
char *transition)
{
struct mnt_entry *ent = NULL;
char *device = NULL;
if (old) {
if (strcmp(old->name, "oldroot") != 0)
yyerror(_("invalid pivotroot conditional '%s'"), old->name);
if (old->vals) {
device = old->vals->value;
old->vals->value = NULL;
}
free_cond_entry(old);
}
ent = new_mnt_entry(NULL, old->vals->value, NULL, root,
ent = new_mnt_entry(NULL, device, NULL, root,
AA_MAY_PIVOTROOT);
ent->trans = transition;
old->vals->value = NULL;
free_cond_entry(old);
return ent;
}

View File

@@ -12,8 +12,8 @@ endif
all: tests
.PHONY: tests error_output gen_xtrans parser_sanity caching
tests: error_output gen_xtrans parser_sanity caching
.PHONY: tests error_output gen_xtrans parser_sanity caching minimize
tests: error_output gen_xtrans parser_sanity caching minimize
GEN_TRANS_DIRS=simple_tests/generated_x/ simple_tests/generated_perms_leading/ simple_tests/generated_perms_safe/
@@ -41,6 +41,9 @@ parser_sanity: $(PARSER)
caching: $(PARSER)
LANG=C ./caching.sh
minimize: $(PARSER)
LANG=C ./minimize.sh
$(PARSER):
make -C $(PARSER_DIR) $(PARSER_BIN)

View File

@@ -49,11 +49,34 @@ echo -n "Profiles are cached when requested: "
[ ! -f $basedir/cache/$profile ] && echo "FAIL ($basedir/cache/$profile does not exist)" && exit 1
echo "ok"
read_features_dir()
{
directory="$1"
if [ ! -d "$directory" ] ; then
return
fi
for f in `ls -AU "$directory"` ; do
if [ -f "$directory/$f" ] ; then
read -r -d "" KF < "$directory/$f" || true
echo -e "$f {$KF\n}"
elif [ -d "$directory/$f" ] ; then
echo -n "$f {"
KF=`read_features_dir "$directory/$f" "$KF"` || true
echo "$KF"
echo -e "}"
fi
done
}
echo -n "Kernel features are written to cache: "
[ ! -f $basedir/cache/.features ] && echo "FAIL ($basedir/cache/.features missing)" && exit 1
read CF < $basedir/cache/.features || true
read KF < /sys/kernel/security/apparmor/features || true
[ "$CF" != "$KF" ] && echo "FAIL (feature text mismatch: cache '$CF' vs kernel '$KF')" && exit 1
read -r -d "" CF < $basedir/cache/.features || true
if [ -d /sys/kernel/security/apparmor/features ] ; then
KF=`read_features_dir /sys/kernel/security/apparmor/features`
else
read -r -d "" KF < /sys/kernel/security/apparmor/features || true
fi
[ "$CF" != "$KF" ] && echo -e "FAIL (feature text mismatch:\n cache '$CF'\nvs\n kernel '$KF')" && exit 1
echo "ok"
echo -n "Cache is loaded when it exists and features match: "

199
parser/tst/minimize.sh Executable file
View File

@@ -0,0 +1,199 @@
#!/bin/bash
# Format of -D dfa-states
# dfa-states output is split into 2 parts:
# the accept state infomation
# {state} (allow deny audit XXX) ignore XXX for now
# followed by the transition table information
# {Y} -> {Z}: 0xXX Char #0xXX is the hex dump of Char
# where the start state is always shown as
# {1} <==
#
# Eg. echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ./apparmor_parser -QT -O minimize -D dfa-states --quiet
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/2800a/0/2800a)
# {4} (0x 10004/2800a/0/2800a)
# {7} (0x 40010/2800a/0/2800a)
# {8} (0x 80020/2800a/0/2800a)
# {9} (0x 100040/2800a/0/2800a)
# {c} (0x 40030/0/0/0)
#
# {1} -> {2}: 0x2f /
# {2} -> {4}: 0x61 a
# {2} -> {3}: 0x62 b
# {2} -> {3}: 0x63 c
# {2} -> {7}: 0x64 d
# {2} -> {8}: 0x65 e
# {2} -> {9}: 0x66 f
# {2} -> {3}: [^\0x0/]
# {3} -> {3}: [^\0x0]
# {4} -> {3}: [^\0x0]
# {7} -> {a}: 0x0
# {7} -> {3}: []
# {8} -> {3}: [^\0x0]
# {9} -> {3}: [^\0x0]
# {a} -> {b}: 0x2f /
# {b} -> {c}: [^/]
# {c} -> {c}: []
#
# These tests currently only look at the accept state permissions
#
# To view any of these DFAs as graphs replace --D dfa-states with -D dfa-graph
# strip of the test stuff around the parser command and use the the dot
# command to convert
# Eg.
# echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ./apparmor_parser -QT -O minimize -D dfa-graph --quiet 2>min.graph
# dot -T png -o min.png min.graph
# and then view min.png in your favorite viewer
#
#------------------------------------------------------------------------
# test to see if minimization is eliminating non-redundant accept state
# Test xtrans and regular perms separately. The are the same basic failure
# but can xtrans has an extra code path.
#
# The permission test is setup to have all the none xtrans permissions show
# up once on unique paths and have a global write permission that adds to
# it.
# This should result in a un-minimized dump looking like. Notice it has 6
# states with accept information, 1 for each rule except for the 'w'
# permission which is combined into a single state for /b and /**
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2800a/0/0/0)
# {4} (0x 3800e/0/0/0)
# {5} (0x 6801a/0/0/0)
# {6} (0x a802a/0/0/0)
# {7} (0x 12804a/0/0/0)
# {a} (0x 40030/0/0/0)
# A dump of minimization that is not respecting the uniqueness of the
# permissions on the states looks like below. Notice it has only 3 states
# with accept information
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 1b806e/0/0/0)
# {5} (0x 6801a/0/0/0)
# {a} (0x 40030/0/0/0)
echo -n "Minimize profiles basic perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 6 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above except with audit perms added
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2800a/0/2800a/0)
# {4} (0x 3800e/0/2800a/0)
# {7} (0x 6801a/0/2800a/0)
# {8} (0x a802a/0/2800a/0)
# {9} (0x 12804a/0/2800a/0)
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles audit perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 6 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above except with deny 'w' perm added to /**, this does not
# elimnates the states with 'w' and 'a' because the quiet information is
# being carried
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/2800a/0/2800a)
# {4} (0x 10004/2800a/0/2800a)
# {7} (0x 40010/2800a/0/2800a)
# {8} (0x 80020/2800a/0/2800a)
# {9} (0x 100040/2800a/0/2800a)
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles deny perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, deny /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 6 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above except with audit deny 'w' perm added to /**, with the
# parameter this elimnates the states with 'w' and 'a' because
# the quiet information is NOT being carried
#
# {1} <== (allow/deny/audit/quiet)
# {4} (0x 10004/0/0/0)
# {7} (0x 40010/0/0/0)
# {8} (0x 80020/0/0/0)
# {9} (0x 100040/0/0/0)
# {c} (0x 40030/0/0/0)
echo -n "Minimize profiles audit deny perms "
if [ `echo "/t { /a r, /b w, /c a, /d l, /e k, /f m, audit deny /** w, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 5 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# The x transition test profile is setup so that there are 3 conflicting x
# permissions, two are on paths that won't collide during dfa creation. The
# 3rd is a generic permission that should be overriden during dfa creation.
#
# This should result in a dfa that specifies transitions on 'a' and 'b' to
# unique states that store the alternate accept information. However
# minimization can remove the unique accept permission states if x permissions
# are treated as a single accept state.
#
# The minimized dump should retain the 'a' and 'b' transitions accept states.
# notice the below dump has 3 states with accept information {3}, {4}, {5}
#
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2914a45/0/0/0)
# {4} (0x 4115045/0/0/0)
# {5} (0x 2514945/0/0/0)
#
# A dump of minimization that is not respecting the uniqueness of the
# permissions on the states transitioned to by 'a' and 'b' looks like
# below. Notice that only state {3} has accept information
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 2514945/0/0/0)
#
echo -n "Minimize profiles xtrans "
if [ `echo "/t { /b px, /* Pixr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 3 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# same test as above + audit
echo -n "Minimize profiles audit xtrans "
if [ `echo "/t { /b px, audit /* Pixr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 3 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# now try denying x and make sure perms are cleared
# notice that only deny and quiet information is being carried
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/fe17f85/0/14005)
echo -n "Minimize profiles deny xtrans "
if [ `echo "/t { /b px, deny /* xr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 1 ] ; then
echo "failed"
exit 1;
fi
echo "ok"
# now try audit + denying x and make sure perms are cleared
# notice that the deny info is being carried, by an artifical trap state
# {1} <== (allow/deny/audit/quiet)
# {3} (0x 0/fe17f85/0/0)
echo -n "Minimize profiles audit deny xtrans "
if [ `echo "/t { /b px, audit deny /* xr, /a Cx -> foo, }" | ../apparmor_parser -QT -O minimize -D dfa-states 2>&1 | grep -v '<==' | grep '^{.*} (.*)$' | wc -l` -ne 0 ] ; then
echo "failed"
exit 1;
fi
echo "ok"

View File

@@ -1,11 +1,13 @@
#
#=DESCRIPTION test for conflict resolution in minimization phase of dfa gen
#=EXRESULT FAIL
#=EXRESULT PASS
#=TODO
#
/usr/bin/foo {
# need to build minimal test for this yet
/b px,
/* Pixr,
/a Cx -> foo,
}

View File

@@ -0,0 +1,11 @@
#
#=DESCRIPTION test for conflict resolution in minimization phase of dfa gen
#=EXRESULT FAIL
#=TODO
#
/usr/bin/foo {
/b* px,
/* Pixr,
/a* Cx -> foo,
}

View File

@@ -36,6 +36,8 @@
/usr/lib{,32,64}/dri/** mr,
/usr/lib/@{multiarch}/dri/** mr,
/dev/dri/** rw,
/etc/drirc r,
owner @{HOME}/.drirc r,
# mouse themes
/etc/X11/cursors/ r,

View File

@@ -30,7 +30,7 @@
/usr/share/a2ps/fonts/** r,
/usr/share/xfce/fonts/** r,
/usr/share/ghostscript/fonts/** r,
/usr/share/texmf/*/fonts/** r,
/usr/share/texmf/{,*/}fonts/** r,
/var/lib/ghostscript/** r,
@{HOME}/.fonts.conf r,

View File

@@ -4,8 +4,9 @@
owner @{HOME}/.java/deployment/deployment.properties k,
/etc/java-*/ r,
/etc/java-*/** r,
/usr/lib/jvm/java-6-openjdk/jre/lib/*/IcedTeaPlugin.so mr,
/usr/lib/jvm/java-6-openjdk*/jre/lib/*/IcedTeaPlugin.so mr,
/usr/lib/jvm/java-6-openjdk/jre/bin/java cx -> browser_openjdk,
/usr/lib/jvm/java-6-openjdk-{amd64,armel,armhf,i386,powerpc}/jre/bin/java cx -> browser_openjdk,
/usr/lib/jvm/java-*-sun-1.*/jre/bin/java{,_vm} cx -> browser_java,
/usr/lib/jvm/java-*-sun-1.*/jre/lib/*/libnp*.so cx -> browser_java,
/usr/lib/j2*-ibm/jre/bin/java cx -> browser_java,
@@ -43,8 +44,8 @@
/var/lib/dbus/machine-id r,
/usr/bin/env ix,
/usr/lib/jvm/java-6-openjdk/jre/bin/java ix,
/usr/lib/jvm/java-6-openjdk/jre/lib/i386/client/classes.jsa m,
/usr/lib/jvm/java-6-openjdk*/jre/bin/java ix,
/usr/lib/jvm/java-6-openjdk*/jre/lib/i386/client/classes.jsa m,
# Why would java need this?
deny /usr/bin/gconftool-2 x,

View File

@@ -31,7 +31,8 @@ int main(int argc, char *argv[])
}
if (strcmp(argv[1], "self") == 0){
if (aa_getcon(&profile, &mode) == -1) {
rc = aa_getcon(&profile, &mode);
if (rc == -1) {
int serrno = errno;
fprintf(stderr,
"FAIL: introspect_confinement %s failed - %s\n",
@@ -47,12 +48,15 @@ int main(int argc, char *argv[])
"FAIL: query_confinement - invalid pid: %s\n",
argv[1]);
exit(serrno);
} else if (aa_gettaskcon(pid, &profile, &mode) == -1) {
int serrno = errno;
fprintf(stderr,
"FAIL: query_confinement %s failed - %s\n",
argv[1], strerror(errno));
exit(serrno);
} else {
rc = aa_gettaskcon(pid, &profile, &mode);
if (rc == -1) {
int serrno = errno;
fprintf(stderr,
"FAIL: query_confinement %s failed - %s\n",
argv[1], strerror(errno));
exit(serrno);
}
}
}
if (strcmp(profile, argv[2]) != 0) {
@@ -61,6 +65,21 @@ int main(int argc, char *argv[])
profile);
exit(1);
}
if (mode) {
if (rc != strlen(profile) + strlen(mode) + 4) {
/* rc includes mode. + 2 null term + 1 ( + 1 space */
fprintf(stderr,
"FAIL: expected return len %d != actual %d\n",
strlen(profile) + strlen(mode) + 4, rc);
exit(1);
}
} else if (rc != strlen(profile) + 1) {
/* rc includes null termination */
fprintf(stderr,
"FAIL: expected return len %d != actual %d\n",
strlen(profile) + 1, rc);
exit(1);
}
if (argv[3] && (!mode || strcmp(mode, argv[3]) != 0)) {
fprintf(stderr,
"FAIL: expected mode \"%s\" != \"%s\"\n", argv[3],

View File

@@ -164,9 +164,10 @@ sub gen_file($) {
my $rule = shift;
my @rules = split (/:/, $rule);
# default: file rules
if (@rules != 2) {
(!$nowarn) && print STDERR "Warning: invalid file access '$rule', ignored\n";
} else {
if (@rules == 1) {
# support raw rules
push (@{$output_rules{$hat}}, " $rules[0],\n");
} elsif (@rules == 2) {
if ($escape) {
$rules[0]=~ s/(["[\]{}\\\:\#])/\\$1/g;
$rules[0]=~ s/(\#)/\\043/g;
@@ -176,6 +177,8 @@ sub gen_file($) {
} else {
push (@{$output_rules{$hat}}, " $rules[0] $rules[1],\n");
}
} else {
(!$nowarn) && print STDERR "Warning: invalid file access '$rule', ignored\n";
}
}

View File

@@ -54,3 +54,15 @@ runchecktest "OPEN R+dac_override" fail $file
rm -f ${file}
genprofile $file:$badperm2
runchecktest "OPEN W (create)" fail $file
# This is a test where using just a raw 'file,' rule allowing all file
# access
genprofile file
runchecktest "OPEN 'file' RW" pass $file
# this test is to make sure the raw 'file' rule allows access to things
# that are not covered by the owner rule
chown nobody $file
chmod 666 $file
genprofile file
runchecktest "OPEN 'file' RW" pass $file

View File

@@ -26,14 +26,20 @@ badperm=ix
mkdir $dir
# CHDIR TEST
# READDIR TEST
genprofile $dir/:$okperm
runchecktest "READDIR" pass $dir
# CHDIR TEST (no perm)
# READDIR TEST (no perm)
genprofile $dir/:$badperm
runchecktest "READDIR (no perm)" fail $dir
# this test is to make sure the raw 'file' rule allows access
# to directories
genprofile file
runchecktest "READDIR 'file' dir" pass $dir
# this test is to make sure the raw 'file' rule allows access
# to '/'
genprofile file
runchecktest "READDIR 'file' '/'" pass '/'

View File

@@ -28,7 +28,7 @@ endif
MODDIR = Immunix
PERLTOOLS = aa-genprof aa-logprof aa-autodep aa-audit aa-complain aa-enforce \
aa-unconfined aa-notify aa-disable
aa-unconfined aa-notify aa-disable aa-exec
TOOLS = ${PERLTOOLS} aa-decode aa-status
MODULES = ${MODDIR}/AppArmor.pm ${MODDIR}/Repository.pm \
${MODDIR}/Config.pm ${MODDIR}/Severity.pm
@@ -37,6 +37,7 @@ MANPAGES = ${TOOLS:=.8} logprof.conf.5
all: ${MANPAGES} ${HTMLMANPAGES}
$(MAKE) -C po all
$(MAKE) -C vim all
# need some better way of determining this
DESTDIR=/
@@ -59,6 +60,7 @@ install: ${MANPAGES} ${HTMLMANPAGES}
install -m 644 ${MODULES} ${PERLDIR}
$(MAKE) -C po install DESTDIR=${DESTDIR} NAME=${NAME}
$(MAKE) install_manpages DESTDIR=${DESTDIR}
$(MAKE) -C vim install DESTDIR=${DESTDIR}
ln -sf aa-status.8 ${DESTDIR}/${MANDIR}/man8/apparmor_status.8
.PHONY: clean
@@ -67,8 +69,24 @@ clean: _clean
rm -f core core.* *.o *.s *.a *~
rm -f Make.rules
$(MAKE) -C po clean
$(MAKE) -C vim clean
check:
# ${CAPABILITIES} is defined in common/Make.rules
.PHONY: check_severity_db
.SILENT: check_severity_db
check_severity_db: /usr/include/sys/capability.h severity.db
# The sed statement is based on the one in the parser's makefile
RC=0 ; for cap in ${CAPABILITIES} ; do \
if ! grep -q -w $${cap} severity.db ; then \
echo "Warning! capability $${cap} not found in severity.db" ; \
RC=1 ; \
fi ;\
done ; \
test "$$RC" -eq 0
.PHONY: check
.SILENT: check
check: check_severity_db
for i in ${MODULES} ${PERLTOOLS} ; do \
perl -c $$i || exit 1; \
done

124
utils/aa-exec Executable file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/perl
# ------------------------------------------------------------------
#
# Copyright (C) 2011 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.
#
# ------------------------------------------------------------------
use strict;
use warnings;
use Errno;
require LibAppArmor;
require POSIX;
require Time::Local;
require File::Basename;
my $opt_d = '';
my $opt_h = '';
my $opt_p = '';
my $opt_n = '';
my $opt_i = '';
my $opt_v = '';
my $opt_f = '';
sub _warn {
my $msg = $_[0];
print STDERR "aa-exec: WARN: $msg\n";
}
sub _error {
my $msg = $_[0];
print STDERR "aa-exec: ERROR: $msg\n";
exit 1
}
sub _debug {
$opt_d or return;
my $msg = $_[0];
print STDERR "aa-exec: DEBUG: $msg\n";
}
sub _verbose {
$opt_v or return;
my $msg = $_[0];
print STDERR "$msg\n";
}
sub usage() {
my $s = <<'EOF';
USAGE: aa-exec [OPTIONS] <prog> <args>
Confine <prog> with the specified PROFILE.
OPTIONS:
-p PROFILE, --profile=PROFILE PROFILE to confine <prog> with
-n NAMESPACE, --namespace=NAMESPACE NAMESPACE to confine <prog> in
-f FILE, --file FILE profile file to load
-i, --immediate change profile immediately instead of at exec
-v, --verbose show messages with stats
-h, --help display this help
EOF
print $s;
}
use Getopt::Long;
GetOptions(
'debug|d' => \$opt_d,
'help|h' => \$opt_h,
'profile|p=s' => \$opt_p,
'namespace|n=s' => \$opt_n,
'file|f=s' => \$opt_f,
'immediate|i' => \$opt_i,
'verbose|v' => \$opt_v,
);
if ($opt_h) {
usage();
exit(0);
}
if ($opt_n || $opt_p) {
my $test;
my $prof;
if ($opt_n) {
$prof = ":$opt_n:";
}
$prof .= $opt_p;
if ($opt_f) {
system("apparmor_parser", "-r", "$opt_f") == 0
or _error("\'aborting could not load $opt_f\'");
}
if ($opt_i) {
_verbose("aa_change_profile(\"$prof\")");
$test = LibAppArmor::aa_change_profile($prof);
_debug("$test = aa_change_profile(\"$prof\"); $!");
} else {
_verbose("aa_change_onexec(\"$prof\")");
$test = LibAppArmor::aa_change_onexec($prof);
_debug("$test = aa_change_onexec(\"$prof\"); $!");
}
if ($test != 0) {
if ($!{ENOENT} || $!{EACCESS}) {
my $pre = ($opt_p) ? "profile" : "namespace";
_error("$pre \'$prof\' does not exist\n");
} elsif ($!{EINVAL}) {
_error("AppArmor interface not available\n");
} else {
_error("$!\n");
}
}
}
_verbose("exec @ARGV");
exec @ARGV;

95
utils/aa-exec.pod Normal file
View File

@@ -0,0 +1,95 @@
# This publication is intellectual property of Canonical Ltd. Its contents
# can be duplicated, either in part or in whole, provided that a copyright
# label is visibly located on each copy.
#
# All information found in this book has been compiled with utmost
# attention to detail. However, this does not guarantee complete accuracy.
# Neither Canonical Ltd, the authors, nor the translators shall be held
# liable for possible errors or the consequences thereof.
#
# Many of the software and hardware descriptions cited in this book
# are registered trademarks. All trade names are subject to copyright
# restrictions and may be registered trade marks. Canonical Ltd
# essentially adheres to the manufacturer's spelling.
#
# Names of products and trademarks appearing in this book (with or without
# specific notation) are likewise subject to trademark and trade protection
# laws and may thus fall under copyright restrictions.
#
=pod
=head1 NAME
aa-exec - confine a program with the specified AppArmor profile
=head1 SYNOPSIS
B<aa-exec> [options] [--] [I<E<lt>commandE<gt>> ...]
=head1 DESCRIPTION
B<aa-exec> is used to launch a program confined by the specified profile
and or namespace. If both a profile and namespace are specified command
will be confined by profile in the new policy namespace. If only a namespace
is specified, the profile name of the current confinement will be used. If
neither a profile or namespace is specified command will be run using
standard profile attachment (ie. as if run without the aa-exec command).
If the arguments are to be pasted to the I<E<lt>commandE<gt>> being invoked
by aa-exec then -- should be used to separate aa-exec arguments from the
command.
aa-exec -p profile1 -- ls -l
=head1 OPTIONS
B<aa-exec> accepts the following arguments:
=over 4
=item -p PROFILE, --profile=PROFILE
confine I<E<lt>commandE<gt>> with PROFILE. If the PROFILE is not specified
use the current profile name (likely unconfined).
=item -n NAMESPACE, --namespace=NAMESPACE
use profiles in NAMESPACE. This will result in confinement transitioning
to using the new profile namespace.
=item -f FILE, --file=FILE
a file or directory containing profiles to load before confining the program.
=item -i, --immediate
transition to PROFILE before doing executing I<E<lt>commandE<gt>>. This
subjects the running of I<E<lt>commandE<gt>> to the exec transition rules
of the current profile.
=item -v, --verbose
show commands being performed
=item -d, --debug
show commands and error codes
=item --
Signal the end of options and disables further option processing. Any
arguments after the -- are treated as arguments of the command. This is
useful when passing arguments to the I<E<lt>commandE<gt>> being invoked by
aa-exec.
=head1 BUGS
If you find any bugs, please report them at
L<http://https://bugs.launchpad.net/apparmor/+filebug>.
=head1 SEE ALSO
aa-stack(8), aa-namespace(8), apparmor(7), apparmor.d(5), aa_change_profile(3),
aa_change_onexec(3) and L<http://wiki.apparmor.net>.
=cut

View File

@@ -1,234 +0,0 @@
" $Id: apparmor.vim,v 1.11 2011/01/31 22:48:07 cb Exp $
"
" ----------------------------------------------------------------------
" Copyright (c) 2005 Novell, Inc. All Rights Reserved.
" Copyright (c) 2006-2011 Christian Boltz. 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 as published by the Free Software Foundation.
"
" This program is distributed in the hope that it will be useful,
" but WITHOUT ANY WARRANTY; without even the implied warranty of
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
" GNU General Public License for more details.
"
" You should have received a copy of the GNU General Public License
" along with this program; if not, contact Novell, Inc.
"
" To contact Novell about this file by physical or electronic mail,
" you may find current contact information at www.novell.com.
"
" To contact Christian Boltz about this file by physical or electronic
" mail, you may find current contact information at www.cboltz.de/en/kontakt.
"
" If you want to report a bug via bugzilla.novell.com, please assign it
" to suse-beta[AT]cboltz.de (replace [AT] with @).
" ----------------------------------------------------------------------
"
" stick this file into ~/.vim/syntax/ and add these commands into your .vimrc
" to have vim automagically use this syntax file for these directories:
"
" autocmd BufNewFile,BufRead /etc/apparmor.d/* set syntax=apparmor
" autocmd BufNewFile,BufRead /etc/apparmor/profiles/* set syntax=apparmor
" profiles are case sensitive
syntax case match
" color setup...
" adjust colors according to the background
" switching colors depending on the background color doesn't work
" unfortunately, so we use colors that work with light and dark background.
" Patches welcome ;-)
"if &background == "light"
" light background
hi sdProfileName ctermfg=lightblue
hi sdHatName ctermfg=darkblue
hi sdExtHat ctermfg=darkblue
" hi sdComment2 ctermfg=darkblue
hi sdGlob ctermfg=darkmagenta
hi sdAlias ctermfg=darkmagenta
hi sdEntryWriteExec ctermfg=black ctermbg=yellow
hi sdEntryUX ctermfg=darkred cterm=underline
hi sdEntryUXe ctermfg=darkred
hi sdEntryIX ctermfg=darkcyan
hi sdEntryM ctermfg=darkcyan
hi sdEntryPX ctermfg=darkgreen cterm=underline
hi sdEntryPXe ctermfg=darkgreen
hi sdEntryW ctermfg=darkyellow
hi sdCap ctermfg=lightblue
hi sdSetCap ctermfg=black ctermbg=yellow
hi sdNetwork ctermfg=lightblue
hi sdNetworkDanger ctermfg=darkred
hi sdCapKey cterm=underline ctermfg=lightblue
hi sdCapDanger ctermfg=darkred
hi sdRLimit ctermfg=lightblue
hi def link sdEntryR Normal
hi def link sdEntryK Normal
hi def link sdFlags Normal
hi sdEntryChangeProfile ctermfg=darkgreen cterm=underline
"else
" dark background
" hi sdProfileName ctermfg=white
" hi sdHatName ctermfg=white
" hi sdGlob ctermfg=magenta
" hi sdEntryWriteExec ctermfg=black ctermbg=yellow
" hi sdEntryUX ctermfg=red cterm=underline
" hi sdEntryUXe ctermfg=red
" hi sdEntryIX ctermfg=cyan
" hi sdEntryM ctermfg=cyan
" hi sdEntryPX ctermfg=green cterm=underline
" hi sdEntryPXe ctermfg=green
" hi sdEntryW ctermfg=yellow
" hi sdCap ctermfg=lightblue
" hi sdCapKey cterm=underline ctermfg=lightblue
" hi def link sdEntryR Normal
" hi def link sdFlags Normal
" hi sdCapDanger ctermfg=red
"endif
hi def link sdInclude Include
high def link sdComment Comment
"high def link sdComment2 Comment
high def link sdFlagKey TODO
high def link sdError ErrorMsg
" always sync from the start. should be relatively quick since we don't have
" that many rules and profiles shouldn't be _extremely_ large...
syn sync fromstart
syn keyword sdFlagKey complain debug
" highlight invalid syntax
syn match sdError /{/ contained
syn match sdError /}/
syn match sdError /^.*$/ contains=sdComment "highlight all non-valid lines as error
" TODO: do not mark lines containing only whitespace as error
" TODO: the sdGlob pattern is not anchored with ^ and $, so it matches all lines matching ^@{...}.*
" This allows incorrect lines also and should be checked better.
" This also (accidently ;-) includes variable definitions (@{FOO}=/bar)
" TODO: make a separate pattern for variable definitions, then mark sdGlob as contained
syn match sdGlob /\v\?|\*|\{.*,.*\}|[[^\]]\+\]|\@\{[a-zA-Z][a-zA-Z0-9_]*\}/
syn match sdAlias /\v^alias\s+(\/|\@\{\S*\})\S*\s+-\>\s+(\/|\@\{\S*\})\S*\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob
" syn match sdComment /#.*/
syn cluster sdEntry contains=sdEntryWriteExec,sdEntryR,sdEntryW,sdEntryIX,sdEntryPX,sdEntryPXe,sdEntryUX,sdEntryUXe,sdEntryM,sdCap,sdSetCap,sdExtHat,sdRLimit,sdNetwork,sdNetworkDanger,sdEntryChangeProfile
" TODO: support audit and deny keywords for all rules (not only for files)
" TODO: higlight audit and deny keywords everywhere
" Capability line
" normal capabilities - really keep this list? syn match sdCap should be enough... (difference: sdCapKey words would loose underlining)
syn keyword sdCapKey chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease
" dangerous capabilities - highlighted separately
syn keyword sdCapDanger sys_admin audit_control audit_write set_fcap mac_override mac_admin
" full line. Keywords are from sdCapKey + sdCapDanger
syn match sdCap /\v^\s*(audit\s+)?(deny\s+)?capability\s+(chown|dac_override|dac_read_search|fowner|fsetid|kill|setgid|setuid|setpcap|linux_immutable|net_bind_service|net_broadcast|net_admin|net_raw|ipc_lock|ipc_owner|sys_module|sys_rawio|sys_chroot|sys_ptrace|sys_pacct|sys_boot|sys_nice|sys_resource|sys_time|sys_tty_config|mknod|lease|sys_admin|audit_control|audit_write|set_fcap|mac_override|mac_admin)\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdCapKey,sdCapDanger,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" set capability was removed - TODO: remove everywhere in apparmor.vim
" syn match sdSetCap /\v^\s*set\s+capability\s+(chown|dac_override|dac_read_search|fowner|fsetid|kill|setgid|setuid|setpcap|linux_immutable|net_bind_service|net_broadcast|net_admin|net_raw|ipc_lock|ipc_owner|sys_module|sys_rawio|sys_chroot|sys_ptrace|sys_pacct|sys_boot|sys_nice|sys_resource|sys_time|sys_tty_config|mknod|lease|sys_admin|audit_control|audit_write|set_fcap|mac_override|mac_admin)\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdCapKey,sdCapDanger,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" Network line
" Syntax: network domain (inet, ...) type (stream, ...) protocol (tcp, ...)
" TODO: 'owner' isn't supported, but will be (JJ, 2011-01-11)
syn match sdNetwork /\v^\s*(audit\s+)?(deny\s+)?network(\s+(inet|ax25|ipx|appletalk|netrom|bridge|atmpvc|x25|inet6|rose|netbeui|security|key|packet|ash|econet|atmsvc|sna|irda|pppox|wanpipe|bluetooth))?(\s+(stream|dgram|seqpacket|rdm|packet))?(\s+tcp|\s+udp|\s+icmp)?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" network rules containing 'raw'
syn match sdNetworkDanger /\v^\s*(audit\s+)?(deny\s+)?network(\s+(inet|ax25|ipx|appletalk|netrom|bridge|atmpvc|x25|inet6|rose|netbeui|security|key|packet|ash|econet|atmsvc|sna|irda|pppox|wanpipe|bluetooth))?(\s+(raw))(\s+tcp|\s+udp|\s+icmp)?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" 'all networking' includes raw -> mark as dangerous
syn match sdNetworkDanger /\v^\s*(audit\s+)?(deny\s+)?network\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" Change Profile
" TODO: audit and deny support will be added (JJ, 2011-01-11)
syn match sdEntryChangeProfile /\v^\s*change_profile\s+-\>\s+\S+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" rlimit
" TODO: audit and deny support will be added (JJ, 2011-01-11)
"
"syn match sdRLimit /\v^\s*rlimit\s+()\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment
syn match sdRLimit /\v^\s*set\s+rlimit\s+(nofile|nproc|rtprio)\s+[0-9]+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment
syn match sdRLimit /\v^\s*set\s+rlimit\s+(locks|sigpending)\s+\<\=\s+[0-9]+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment
syn match sdRLimit /\v^\s*set\s+rlimit\s+(fsize|data|stack|core|rss|as|memlock|msgqueue)\s+\<\=\s+[0-9]+([KMG])?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment
syn match sdRLimit /\v^\s*set\s+rlimit\s+nice\s+\<\=\s+(-1?[0-9]|-20|1?[0-9])\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment
" link rules
syn match sdEntryW /\v^\s+(audit\s+)?(deny\s+)?(owner\s+)?link\s+(subset\s+)?(\/|\@\{\S*\})\S*\s+-\>\s+(\/|\@\{\S*\})\S*\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob
" file permissions
"
" TODO: Support filenames enclosed in quotes ("/home/foo/My Documents/") - ideally by only allowing quotes pair-wise
"
" write + exec/mmap - danger!
" known bug: accepts 'aw' to keep things simple
syn match sdEntryWriteExec /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(l|r|w|a|m|k|[iuUpPcC]x)+(\s+-\>\s+\S+)?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" ux(mr) - unconstrained entry, flag the line red
syn match sdEntryUX /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(r|m|k|ux)+(\s+-\>\s+\S+)?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" Ux(mr) - like ux + clean environment
syn match sdEntryUXe /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(r|m|k|Ux)+(\s+-\>\s+\S+)?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" px/cx/pix/cix(mrk) - standard exec entry, flag the line blue
syn match sdEntryPX /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(r|m|k|px|cx|pix|cix)+(\s+-\>\s+\S+)?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" Px/Cx/Pix/Cix(mrk) - like px/cx + clean environment
syn match sdEntryPXe /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(r|m|k|Px|Cx|Pix|Cix)+(\s+-\>\s+\S+)?\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" ix(mr) - standard exec entry, flag the line green
syn match sdEntryIX /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(r|m|k|ix)+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" mr - mmap with PROT_EXEC
syn match sdEntryM /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(r|m|k)+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" if we've got u or i without x, it's an error
" rule is superfluous because of the '/.*/ is an error' rule ;-)
"syn match sdError /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(l|r|w|k|u|p|i)+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" write + append is an error also
"syn match sdError /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(\S*r\S*a\S*|\S*a\S*w\S*)\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
syn match sdError /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+\S*(w\S*a|a\S*w)\S*\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" write entry, flag the line yellow
syn match sdEntryW /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(l|r|w|k)+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" append entry, flag the line yellow
syn match sdEntryW /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+(l|r|a|k)+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" read entry + locking, currently no highlighting
syn match sdEntryK /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+[rlk]+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
" read entry, no highlighting
syn match sdEntryR /\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+[rl]+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdGlob,sdComment nextgroup=@sdEntry,sdComment,sdError,sdInclude
syn match sdExtHat /\v^\s+(\^|profile\s+)\S+\s*,(\s*$|(\s*#.*$)\@=)/ contains=sdComment " hat without {...}
syn match sdProfileName /\v^((profile\s+)?\/\S+|profile\s+([a-zA-Z0-9]\S*\s)?\S+)\s+((flags\s*\=\s*)?\(\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative)(\s*,\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative))*\s*\)\s+)=\{/ contains=sdProfileStart,sdHatName,sdFlags,sdComment,sdGlob
syn match sdProfileStart /{/ contained
syn match sdProfileEnd /^}\s*(#.*)?$/ contained " TODO: syn region does not (yet?) allow usage of comment in end=
" TODO: Removing the $ mark from end= will allow non-comments also :-(
syn match sdHatName /\v^\s+(\^|profile\s+)\S+\s+((flags\s*\=\s*)?\(\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative)(\s*,\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative))*\s*\)\s+)=\{/ contains=sdProfileStart,sdFlags,sdComment
syn match sdHatStart /{/ contained
syn match sdHatEnd /}/ contained " TODO: allow comments + [same as for syn match sdProfileEnd]
syn match sdFlags /\v((flags\s*\=\s*)?\(\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative)(\s*,\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative))*\s*\)\s+)/ contained contains=sdFlagKey
syn match sdComment /\s*#.*$/
" NOTE: contains=sdComment changes #include highlighting to comment color.
" NOTE: Comment highlighting still works without contains=sdComment.
syn match sdInclude /\s*#include\s<\S*>/ " TODO: doesn't check until $
syn match sdInclude /\s*include\s<\S*>/ " TODO: doesn't check until $
" basic profile block...
" \s+ does not work in end=, therefore using \s\s*
syn region Normal start=/\v^(profile\s+)?\S+\s+((flags\s*\=\s*)?\(\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative)(\s*,\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative))*\s*\)\s+)=\{/ matchgroup=sdProfileEnd end=/^}\s*$/ contains=sdProfileName,Hat,@sdEntry,sdComment,sdError,sdInclude
syn region Hat start=/\v^\s+(\^|profile\s+)\S+\s+((flags\s*\=\s*)?\(\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative)(\s*,\s*(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative))*\s*\)\s+)=\{/ matchgroup=sdHatEnd end=/^\s\s*}\s*$/ contains=sdHatName,@sdEntry,sdComment,sdError,sdInclude

View File

@@ -14,9 +14,12 @@
CAP_SYS_MODULE 10
CAP_SYS_PTRACE 10
CAP_SYS_RAWIO 10
CAP_MAC_ADMIN 10
CAP_MAC_OVERRIDE 10
# Allow other processes to 0wn the machine:
CAP_SETPCAP 9
CAP_CHOWN 9
CAP_SETFCAP 9
CAP_CHOWN 9
CAP_FSETID 9
CAP_MKNOD 9
CAP_LINUX_IMMUTABLE 9
@@ -38,9 +41,11 @@
CAP_LEASE 8
CAP_IPC_LOCK 8
CAP_SYS_TTY_CONFIG 8
CAP_DAC_READ_SEARCH 7
CAP_AUDIT_CONTROL 8
CAP_AUDIT_WRITE 8
CAP_SYSLOG 8
CAP_WAKE_ALARM 8
CAP_DAC_READ_SEARCH 7
# unused
CAP_NET_BROADCAST 0

View File

@@ -1,5 +1,25 @@
apparmor.vim: apparmor.vim.in Makefile create-apparmor.vim.sh
sh create-apparmor.vim.sh
COMMONDIR=../../common/
all:
include common/Make.rules
COMMONDIR_EXISTS=$(strip $(shell [ -d ${COMMONDIR} ] && echo true))
ifeq ($(COMMONDIR_EXISTS), true)
common/Make.rules: $(COMMONDIR)/Make.rules
ln -sf $(COMMONDIR) .
endif
VIM_INSTALL_PATH=${DESTDIR}/usr/share/apparmor
all: apparmor.vim
apparmor.vim: apparmor.vim.in Makefile create-apparmor.vim.py
python create-apparmor.vim.py > $@
install: apparmor.vim
install -d $(VIM_INSTALL_PATH)
install -m 644 $< $(VIM_INSTALL_PATH)
clean:
rm -f apparmor.vim

View File

@@ -0,0 +1,108 @@
#!/usr/bin/python
#
# Copyright (C) 2012 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.
#
# Written by Steve Beattie <steve@nxnw.org>, based on work by
# Christian Boltz <apparmor@cboltz.de>
import os
import re
import subprocess
import sys
# dangerous capabilities
danger_caps=["audit_control",
"audit_write",
"mac_override",
"mac_admin",
"set_fcap",
"sys_admin",
"sys_module",
"sys_rawio"]
aa_network_types=r'\s+tcp|\s+udp|\s+icmp'
aa_flags=r'(complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative)'
def cmd(command, input = None, stderr = subprocess.STDOUT, stdout = subprocess.PIPE, stdin = None, timeout = None):
'''Try to execute given command (array) and return its stdout, or
return a textual error if it failed.'''
try:
sp = subprocess.Popen(command, stdin=stdin, stdout=stdout, stderr=stderr, close_fds=True)
except OSError, e:
return [127, str(e)]
out, outerr = sp.communicate(input)
# Handle redirection of stdout
if out == None:
out = ''
# Handle redirection of stderr
if outerr == None:
outerr = ''
return [sp.returncode,out+outerr]
# get capabilities list
(rc, output) = cmd(['make', '-s', '--no-print-directory', 'list_capabilities'])
if rc != 0:
print >>sys.stderr, ("make list_capabilities failed: " + output)
exit(rc)
capabilities = re.sub('CAP_', '', output.strip()).lower().split(" ")
benign_caps =[]
for cap in capabilities:
if cap not in danger_caps:
benign_caps.append(cap)
# get network protos list
(rc, output) = cmd(['make', '-s', '--no-print-directory', 'list_af_names'])
if rc != 0:
print >>sys.stderr, ("make list_af_names failed: " + output)
exit(rc)
af_names = []
af_pairs = re.sub('AF_', '', output.strip()).lower().split(",")
for af_pair in af_pairs:
af_name = af_pair.lstrip().split(" ")[0]
# skip max af name definition
if len(af_name) > 0 and af_name != "max":
af_names.append(af_name)
# TODO: does a "debug" flag exist? Listed in apparmor.vim.in sdFlagKey,
# but not in aa_flags...
# -> currently (2011-01-11) not, but might come back
aa_regex_map = {
'FILE': r'\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+',
'DENYFILE': r'\v^\s*(audit\s+)?deny\s+(owner\s+)?(\/|\@\{\S*\})\S*\s+',
'auditdenyowner': r'(audit\s+)?(deny\s+)?(owner\s+)?',
'auditdeny': r'(audit\s+)?(deny\s+)?',
'FILENAME': r'(\/|\@\{\S*\})\S*',
'EOL': r'\s*,(\s*$|(\s*#.*$)\@=)',
'TRANSITION': r'(\s+-\>\s+\S+)?',
'sdKapKey': " ".join(benign_caps),
'sdKapKeyDanger': " ".join(danger_caps),
'sdKapKeyRegex': "|".join(capabilities),
'sdNetworkType': aa_network_types,
'sdNetworkProto': "|".join(af_names),
'flags': r'((flags\s*\=\s*)?\(\s*' + aa_flags + r'(\s*,\s*' + aa_flags + r')*\s*\)\s+)',
}
def my_repl(matchobj):
#print matchobj.group(1)
if matchobj.group(1) in aa_regex_map:
return aa_regex_map[matchobj.group(1)]
return matchobj.group(0)
regex = "@@(" + "|".join(aa_regex_map) + ")@@"
with file("apparmor.vim.in") as template:
for line in template:
line = re.sub(regex, my_repl, line.rstrip())
print line

View File

@@ -1,129 +0,0 @@
#!/bin/bash
# not-too-dangerous capabilities
sdKapKey="chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_chroot sys_ptrace sys_pacct sys_boot sys_nice sys_resource sys_time sys_tty_config syslog mknod lease"
# dangerous capabilities
sdKapKeyDanger="audit_control audit_write mac_override mac_admin set_fcap sys_admin sys_module sys_rawio"
sdNetworkProto="inet|ax25|ipx|appletalk|netrom|bridge|atmpvc|x25|inet6|rose|netbeui|security|key|packet|ash|econet|atmsvc|sna|irda|pppox|wanpipe|bluetooth"
sdNetworkType='\s+tcp|\s+udp|\s+icmp'
sdFlags="complain|audit|attach_disconnect|no_attach_disconnected|chroot_attach|chroot_no_attach|chroot_relative|namespace_relative"
# TODO: does a "debug" flag exist? Listed in apparmor.vim.in sdFlagKey, but not in sdFlags...
# -> currently (2011-01-11) not, but might come back
sdKapKeyRegex="$(echo "$sdKapKey $sdKapKeyDanger" | sed 's/ /|/g')"
sdFlagsRegex="($sdFlags)"
# '@@FILE@@' '\v^\s*((owner\s+)|(audit\s+)|(deny\s+))*(\/|\@\{\S*\})\S*\s+' \
replace \
'@@FILE@@' '\v^\s*(audit\s+)?(deny\s+)?(owner\s+)?(\/|\@\{\S*\})\S*\s+' \
'@@DENYFILE@@' '\v^\s*(audit\s+)?deny\s+(owner\s+)?(\/|\@\{\S*\})\S*\s+' \
'@@auditdenyowner@@' '(audit\s+)?(deny\s+)?(owner\s+)?' \
'@@auditdeny@@' '(audit\s+)?(deny\s+)?' \
'@@FILENAME@@' '(\/|\@\{\S*\})\S*' \
'@@EOL@@' '\s*,(\s*$|(\s*#.*$)\@=)' \
'@@TRANSITION@@' '(\s+-\>\s+\S+)?' \
'@@sdKapKey@@' "$sdKapKey" \
'@@sdKapKeyDanger@@' "$sdKapKeyDanger" \
'@@sdKapKeyRegex@@' "$sdKapKeyRegex" \
'@@sdNetworkProto@@' "$sdNetworkProto" \
'@@sdNetworkType@@' "$sdNetworkType" \
'@@flags@@' "((flags\s*\=\s*)?\(\s*$sdFlagsRegex(\s*,\s*$sdFlagsRegex)*\s*\)\s+)" \
\
< apparmor.vim.in \
> apparmor.vim
# @@FILE@@: Start of a file rule (whitespace_+_, owner etc. flag_?_, filename pattern, whitespace_+_)
# @@FILENAME@@: Just a filename (taken from @@FILE@@)
# @@EOL@@: End of a line (whitespace_?_, comma, whitespace_?_ comment.*)
# I had to learn that vim has a restriction on the number of (...) I may use in
# a RegEx (up to 9 are allowed), and therefore had to change the RegEx that
# matches tcp/udp/icmp from "(\s+(tcp|udp|icmp))?" to
# "(\s+tcp|\s+udp|\s+icmp)?". *argh*
# (sdNetworkProto could be changed the same way if needed)
# TODO: permissions first
# valid rules:
# owner rw /foo,
# owner /foo rw,
# INVALID rules
# rw owner /foo,
# rw /foo owner,
# /foo owner rw,
# /foo rw owner,
# the *** proposed *** syntax for owner= and user= is
#
# owner=<name> <whitespace> <rule>
# owner='('<names>')' <whitespace> <rule>
#
# where the list followed the syntax for the flags value, however the list
# syntax part needs to be made consistent, ie. we either need to fix the
# flags list separator or make the list separator here the same as flags
# and also fix it for variables, etc. switching flags to use just whitespace
# is by far the easiest.
#
# So going with the whitespace separator we would have
# owner=jj /foo r,
# owner=(jj) /foo r,
# owner=(jj smb) /foo r,
# > capability dac_override {
# > /file/bar rw,
# > }
# > capability chown {
# > /file/bar (user1, user2),
# > }
# > (Are those things specific to dac_override and chown?)
# >
# Hehe, now your veering even more into unimplemented stuff :) Those where
# merely proposed syntax and I don't believe we are using them now.
# The idea behind those was a way to enhance the capabilities and remain
# backwards compatible.
#
# And use the syntax for each would have to be capability (or type specific)
#
# eg. for chown we could have a path and user
#
# chown /foo to (user1 user2),
#
# but for setuid it wouldn't have a path.
# setuid to (user1 user2)
#
#
# > uses ipc,
# > ipc rw /profile,
# > ipc signal w (child) /profile,
# > deny ipc signal w (kill) /profile,
# >
# > Which keywords can apply to ipc? I'd guess audit and deny. What about
# > owner?
# >
# owner and user could be selectively applied but not to allow of ipc
#
# owner doesn't really make sense for signal, but user might this is just
# another place we need to look at before we commit to the syntax.
#
# ipc may hit spring 2011
# > That all said: are there some example profiles I could use to test
# > apparmor.vim?
# >
# Hrmmm, yes. The goal is to keep adding to the parser test suite, and
# get it to contain at least on example of every valid syntax and also
# example profiles of invalid syntax. I won't say that the coverage
# is complete yet but it does have hundreds of simple examples.
#
# it can be found in parser/tst/simple_tests/
#