mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-31 14:25:52 +00:00
Compare commits
186 Commits
v2.7.1
...
v2.8-beta5
Author | SHA1 | Date | |
---|---|---|---|
|
68297d9398 | ||
|
6f27ba3abb | ||
|
7afa066be3 | ||
|
562eb63964 | ||
|
852907e1cc | ||
|
50aa2335eb | ||
|
3ff29d2e4b | ||
|
24e46508d5 | ||
|
f7ce93b27c | ||
|
f67168cf2d | ||
|
c80254eb3f | ||
|
01fe7f42a0 | ||
|
f37f59f47b | ||
|
521b237e8b | ||
|
daa5b9f496 | ||
|
18ddf78dbe | ||
|
3356dc4edd | ||
|
c1722cdfdb | ||
|
5c09f44f8b | ||
|
40588d182a | ||
|
83ead1217f | ||
|
4a89f974f6 | ||
|
93308e4a29 | ||
|
593cb59d38 | ||
|
1439d006cd | ||
|
b4feb99841 | ||
|
63c43ae9f5 | ||
|
a31e1349ce | ||
|
f4240fcc74 | ||
|
8eaeb44f56 | ||
|
bfc1032fc1 | ||
|
65f90c0942 | ||
|
4fcd1f33dc | ||
|
86527a2f4c | ||
|
648166ecca | ||
|
2e3b5ff134 | ||
|
3c9cdfb841 | ||
|
e7f6e0f9f1 | ||
|
7fcbd543d7 | ||
|
2f603cc73e | ||
|
69dc13efdf | ||
|
456220db56 | ||
|
c50858a877 | ||
|
a11efe838a | ||
|
d6dc04d737 | ||
|
feeea88a58 | ||
|
36d44a3b25 | ||
|
fc5f4dc86f | ||
|
59c0bb0f46 | ||
|
fae11e12cf | ||
|
e0a74881bf | ||
|
ce38857061 | ||
|
c8e134930f | ||
|
3876299fa0 | ||
|
b0b2bde160 | ||
|
3a1b7bb54c | ||
|
04ef92ca94 | ||
|
d7a6860a23 | ||
|
5e361a4a05 | ||
|
cf5f7ef9c2 | ||
|
811d8aefa3 | ||
|
37f446dd79 | ||
|
1a01b5c296 | ||
|
b47197b881 | ||
|
3e5ae57164 | ||
|
a0048ec064 | ||
|
c35e10f875 | ||
|
46d9aae952 | ||
|
bd67bb909a | ||
|
c454964e5b | ||
|
e61b7b9241 | ||
|
954dc6f694 | ||
|
c9e31b7fbd | ||
|
df46234c55 | ||
|
662ad60cd7 | ||
|
a5640ec89c | ||
|
93cd01d7e6 | ||
|
ca9b813aea | ||
|
cbe3f33daf | ||
|
b8f36df713 | ||
|
e087db57b2 | ||
|
dd7427d1eb | ||
|
4a4ec1c54a | ||
|
18c87e98bf | ||
|
8a3edd677c | ||
|
def8c20168 | ||
|
613997fd7e | ||
|
eabeb4f7b3 | ||
|
ac6c7dd37f | ||
|
e7c550243c | ||
|
6f95ff5637 | ||
|
82a20d9bb8 | ||
|
fb55e9cddc | ||
|
4d406621ee | ||
|
e074def743 | ||
|
13e959f8c0 | ||
|
5151168071 | ||
|
77f37e84eb | ||
|
8e3b75c40a | ||
|
4037c3ae65 | ||
|
c817e01774 | ||
|
8d1e8c9c2e | ||
|
b0ad1303ac | ||
|
e4e173233c | ||
|
dd1756ba9e | ||
|
cec0d50cfd | ||
|
1ef2d7e757 | ||
|
03976a038e | ||
|
83a3de91f8 | ||
|
e7cc3e2094 | ||
|
4fa434a3d0 | ||
|
44ca1c0f11 | ||
|
a0fbc1f26c | ||
|
62a7934ea6 | ||
|
5fdf33c689 | ||
|
7031a91aec | ||
|
39a3f5b08d | ||
|
7eee94290b | ||
|
45e7265c82 | ||
|
3c2684b674 | ||
|
b1a8e7cf44 | ||
|
7887d5906d | ||
|
b0ef4d2b6a | ||
|
c626e62da6 | ||
|
b93c49974c | ||
|
d045d609be | ||
|
333cd41d33 | ||
|
2526933747 | ||
|
a33c5822a9 | ||
|
d6c4f56da8 | ||
|
25f800ac7d | ||
|
ad0f942bb5 | ||
|
1ff5a08f60 | ||
|
8a223ce3d5 | ||
|
2f85e0b7d2 | ||
|
6d55882b4a | ||
|
c5ccbb50d2 | ||
|
572bab7e84 | ||
|
f561b8cdfe | ||
|
3ff8b4d19a | ||
|
47280bb483 | ||
|
2b4e235ffd | ||
|
27dacf2eca | ||
|
15e4f8a05f | ||
|
0cb4e48344 | ||
|
f29c0cc377 | ||
|
960d19b6cb | ||
|
4b34c77a39 | ||
|
61b614543c | ||
|
30c8dfe12c | ||
|
3d42221ba8 | ||
|
bd56500d03 | ||
|
401363854a | ||
|
a6d274dcb0 | ||
|
9d20afa95c | ||
|
32362d2f79 | ||
|
3a201bf72b | ||
|
d15fcb69ab | ||
|
c1850f9855 | ||
|
b9bbcdc45c | ||
|
ff98d79963 | ||
|
6ef6f605b0 | ||
|
7f9c79e345 | ||
|
37e2975d4d | ||
|
49142c74a5 | ||
|
98f196506a | ||
|
c52f417406 | ||
|
ec68828a30 | ||
|
c259deb5b2 | ||
|
f0f520eeff | ||
|
a9697cec28 | ||
|
9e6a13fa78 | ||
|
95015dae9f | ||
|
cba77b26a5 | ||
|
0069bf82a6 | ||
|
9e9a7ff572 | ||
|
18821b079b | ||
|
2674a8b708 | ||
|
8bc30c8851 | ||
|
bd10235397 | ||
|
35b7ee91eb | ||
|
d452f53576 | ||
|
9d374d4726 | ||
|
4beee46c52 | ||
|
319cd6c038 | ||
|
bd66fba55f |
2
Makefile
2
Makefile
@@ -7,7 +7,7 @@ include common/Make.rules
|
||||
DIRS=parser \
|
||||
profiles \
|
||||
utils \
|
||||
changehat/libapparmor \
|
||||
libraries/libapparmor \
|
||||
changehat/mod_apparmor \
|
||||
changehat/pam_apparmor \
|
||||
tests
|
||||
|
@@ -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/linux/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 <linux/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
|
||||
# =====================
|
||||
@@ -172,29 +206,8 @@ install_manpages: $(MANPAGES)
|
||||
|
||||
MAN_RELEASE="AppArmor ${VERSION}"
|
||||
|
||||
%.1: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=1 > $@
|
||||
|
||||
%.2: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=2 > $@
|
||||
|
||||
%.3: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=3 > $@
|
||||
|
||||
%.4: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=4 > $@
|
||||
|
||||
%.5: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=5 > $@
|
||||
|
||||
%.6: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=6 > $@
|
||||
|
||||
%.7: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=7 > $@
|
||||
|
||||
%.8: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --section=8 > $@
|
||||
%.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8: %.pod
|
||||
$(POD2MAN) $< --release=$(MAN_RELEASE) --center=AppArmor --stderr --section=$(subst .,,$(suffix $@)) > $@
|
||||
|
||||
%.1.html: %.pod
|
||||
$(POD2HTML) --header --css apparmor.css --infile=$< --outfile=$@
|
||||
|
@@ -1 +1 @@
|
||||
2.7.0
|
||||
2.7.102
|
||||
|
@@ -16,4 +16,5 @@ BUILT_SOURCES = $(man_MANS)
|
||||
--section=2 \
|
||||
--release="AppArmor $(VERSION)" \
|
||||
--center="AppArmor" \
|
||||
--stderr \
|
||||
$< > $@
|
||||
|
@@ -85,14 +85,9 @@ Insufficient kernel memory was available.
|
||||
|
||||
The calling application is not confined by apparmor.
|
||||
|
||||
=item B<ECHILD>
|
||||
|
||||
The application's profile has no hats defined for it.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
The specified I<profile> does not exist in this profile or the
|
||||
process tried to change another process's domain.
|
||||
The task does not have sufficient permissions to change its domain.
|
||||
|
||||
=back
|
||||
|
||||
@@ -175,6 +170,7 @@ The output when run:
|
||||
|
||||
If /tmp/change_p is to be confined as well, then the following profile can be
|
||||
used (in addition to the one for 'i_cant_be_trusted_anymore', above):
|
||||
|
||||
# Confine change_p to be able to read /etc/passwd and aa_change_profile()
|
||||
# to the 'i_cant_be_trusted_anymore' profile.
|
||||
/tmp/change_p {
|
||||
|
@@ -38,8 +38,9 @@ Link with B<-lapparmor> when compiling.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The aa_is_enabled function returns true (1) if apparmor is enabled. If it
|
||||
isn't it sets the errno to reflect the reason it is not enabled and returns 0.
|
||||
The aa_is_enabled function returns true (1) if apparmor is enabled.
|
||||
If it isn't it sets the errno(3) to reflect the reason it is not
|
||||
enabled and returns 0.
|
||||
|
||||
The aa_find_mountpoint function finds where the apparmor filesystem is mounted
|
||||
on the system, and returns a string containing the mount path. It is the
|
||||
@@ -57,10 +58,10 @@ appropriately.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
=over 4
|
||||
|
||||
B<aa_is_enabled>
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<ENOSYS>
|
||||
|
||||
AppArmor extensions to the system are not available.
|
||||
@@ -84,18 +85,21 @@ Did not have sufficient permissions to determine if AppArmor is enabled.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
+Did not have sufficient permissions to determine if AppArmor is enabled.
|
||||
Did not have sufficient permissions to determine if AppArmor is enabled.
|
||||
|
||||
=back
|
||||
|
||||
B<aa_find_mountpoint>
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<ENOMEM>
|
||||
|
||||
Insufficient memory was available.
|
||||
|
||||
=item B<EACCES>
|
||||
|
||||
Access to the the required paths was denied.
|
||||
Access to the required paths was denied.
|
||||
|
||||
=item B<ENOENT>
|
||||
|
||||
|
@@ -50,7 +50,7 @@ Link with B<-lapparmor> when compiling.
|
||||
The aa_getcon function gets the current AppArmor confinement context for the
|
||||
current task. The confinement context is usually just the name of the AppArmor
|
||||
profile restricting the task, but it may include the profile namespace or in
|
||||
some cases a set of profile names (known as a stack of profiles). The returned string *con should be freed using <free()>.
|
||||
some cases a set of profile names (known as a stack of profiles). The returned string *con should be freed using free().
|
||||
|
||||
The aa_gettaskcon function is like the aa_getcon function except it will work
|
||||
for any arbitrary task in the system.
|
||||
@@ -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
|
||||
|
@@ -141,6 +141,10 @@ typedef struct
|
||||
char *net_family;
|
||||
char *net_protocol;
|
||||
char *net_sock_type;
|
||||
char *net_local_addr;
|
||||
unsigned long net_local_port;
|
||||
char *net_foreign_addr;
|
||||
unsigned long net_foreign_port;
|
||||
} aa_log_record;
|
||||
|
||||
/**
|
||||
|
@@ -83,6 +83,7 @@ aa_record_event_type lookup_aa_event(unsigned int type)
|
||||
%token <t_str> TOK_QUOTED_STRING TOK_ID TOK_MODE TOK_DMESG_STAMP
|
||||
%token <t_str> TOK_AUDIT_DIGITS TOK_DATE_MONTH TOK_DATE_TIME
|
||||
%token <t_str> TOK_HEXSTRING TOK_TYPE_OTHER TOK_MSG_REST
|
||||
%token <t_str> TOK_IP_ADDR
|
||||
|
||||
%token TOK_EQUALS
|
||||
%token TOK_COLON
|
||||
@@ -133,6 +134,10 @@ aa_record_event_type lookup_aa_event(unsigned int type)
|
||||
%token TOK_KEY_CAPNAME
|
||||
%token TOK_KEY_OFFSET
|
||||
%token TOK_KEY_TARGET
|
||||
%token TOK_KEY_LADDR
|
||||
%token TOK_KEY_FADDR
|
||||
%token TOK_KEY_LPORT
|
||||
%token TOK_KEY_FPORT
|
||||
|
||||
%token TOK_SYSLOG_KERNEL
|
||||
|
||||
@@ -268,6 +273,14 @@ key: TOK_KEY_OPERATION TOK_EQUALS TOK_QUOTED_STRING
|
||||
{ /* target was always name2 in the past */
|
||||
ret_record->name2 = $3;
|
||||
}
|
||||
| TOK_KEY_LADDR TOK_EQUALS TOK_IP_ADDR
|
||||
{ ret_record->net_local_addr = $3;}
|
||||
| TOK_KEY_FADDR TOK_EQUALS TOK_IP_ADDR
|
||||
{ ret_record->net_foreign_addr = $3;}
|
||||
| TOK_KEY_LPORT TOK_EQUALS TOK_DIGITS
|
||||
{ ret_record->net_local_port = $3;}
|
||||
| TOK_KEY_FPORT TOK_EQUALS TOK_DIGITS
|
||||
{ ret_record->net_foreign_port = $3;}
|
||||
| TOK_MSG_REST
|
||||
{
|
||||
ret_record->event = AA_RECORD_INVALID;
|
||||
|
@@ -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;
|
||||
|
@@ -133,8 +133,15 @@ key_capability "capability"
|
||||
key_capname "capname"
|
||||
key_offset "offset"
|
||||
key_target "target"
|
||||
key_laddr "laddr"
|
||||
key_faddr "faddr"
|
||||
key_lport "lport"
|
||||
key_fport "fport"
|
||||
audit "audit"
|
||||
|
||||
/* network addrs */
|
||||
ip_addr [a-f[:digit:].:]{3,}
|
||||
|
||||
/* syslog tokens */
|
||||
syslog_kernel kernel{colon}
|
||||
syslog_month Jan(uary)?|Feb(ruary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?
|
||||
@@ -149,6 +156,7 @@ dmesg_timestamp \[[[:digit:] ]{5,}\.[[:digit:]]{6,}\]
|
||||
%x dmesg_timestamp
|
||||
%x safe_string
|
||||
%x audit_types
|
||||
%x ip_addr
|
||||
%x other_audit
|
||||
%x unknown_message
|
||||
|
||||
@@ -201,6 +209,12 @@ yy_flex_debug = 0;
|
||||
. { /* eek, error! try another state */ BEGIN(INITIAL); yyless(0); }
|
||||
}
|
||||
|
||||
<ip_addr>{
|
||||
{ip_addr} { yylval->t_str = strdup(yytext); yy_pop_state(yyscanner); return(TOK_IP_ADDR); }
|
||||
{equals} { return(TOK_EQUALS); }
|
||||
. { /* eek, error! try another state */ BEGIN(INITIAL); yyless(0); }
|
||||
}
|
||||
|
||||
<audit_types>{
|
||||
{equals} { return(TOK_EQUALS); }
|
||||
{digits} { yylval->t_long = atol(yytext); BEGIN(INITIAL); return(TOK_DIGITS); }
|
||||
@@ -270,6 +284,10 @@ yy_flex_debug = 0;
|
||||
{key_capname} { return(TOK_KEY_CAPNAME); }
|
||||
{key_offset} { return(TOK_KEY_OFFSET); }
|
||||
{key_target} { return(TOK_KEY_TARGET); }
|
||||
{key_laddr} { yy_push_state(ip_addr, yyscanner); return(TOK_KEY_LADDR); }
|
||||
{key_faddr} { yy_push_state(ip_addr, yyscanner); return(TOK_KEY_FADDR); }
|
||||
{key_lport} { return(TOK_KEY_LPORT); }
|
||||
{key_fport} { return(TOK_KEY_FPORT); }
|
||||
|
||||
{syslog_kernel} { BEGIN(dmesg_timestamp); return(TOK_SYSLOG_KERNEL); }
|
||||
{syslog_month} { yylval->t_str = strdup(yytext); return(TOK_DATE_MONTH); }
|
||||
|
@@ -51,6 +51,18 @@ int main(int argc, char **argv)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define print_string(description, var) \
|
||||
if ((var) != NULL) { \
|
||||
printf("%s: %s\n", (description), (var)); \
|
||||
}
|
||||
|
||||
/* unset is the value that the library sets to the var to indicate
|
||||
that it is unset */
|
||||
#define print_long(description, var, unset) \
|
||||
if ((var) != (unsigned long) (unset)) { \
|
||||
printf("%s: %ld\n", (description), (var)); \
|
||||
}
|
||||
|
||||
int print_results(aa_log_record *record)
|
||||
{
|
||||
printf("Event type: ");
|
||||
@@ -185,6 +197,11 @@ int print_results(aa_log_record *record)
|
||||
{
|
||||
printf("Protocol: %s\n", record->net_protocol);
|
||||
}
|
||||
print_string("Local addr", record->net_local_addr);
|
||||
print_string("Foreign addr", record->net_foreign_addr);
|
||||
print_long("Local port", record->net_local_port, 0);
|
||||
print_long("Foreign port", record->net_foreign_port, 0);
|
||||
|
||||
printf("Epoch: %lu\n", record->epoch);
|
||||
printf("Audit subid: %u\n", record->audit_sub_id);
|
||||
return(0);
|
||||
|
@@ -0,0 +1 @@
|
||||
Apr 5 19:30:56 precise-amd64 kernel: [153073.826757] type=1400 audit(1308766940.698:3704): apparmor="DENIED" operation="sendmsg" parent=24737 profile="/usr/bin/evince-thumbnailer" pid=24743 comm="evince-thumbnai" laddr=192.168.66.150 lport=765 faddr=192.168.66.200 fport=2049 family="inet" sock_type="stream" protocol=6
|
@@ -0,0 +1,18 @@
|
||||
START
|
||||
File: test_multi/testcase_network_01.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1308766940.698:3704
|
||||
Operation: sendmsg
|
||||
Profile: /usr/bin/evince-thumbnailer
|
||||
Command: evince-thumbnai
|
||||
Parent: 24737
|
||||
PID: 24743
|
||||
Network family: inet
|
||||
Socket type: stream
|
||||
Protocol: tcp
|
||||
Local addr: 192.168.66.150
|
||||
Foreign addr: 192.168.66.200
|
||||
Local port: 765
|
||||
Foreign port: 2049
|
||||
Epoch: 1308766940
|
||||
Audit subid: 3704
|
@@ -0,0 +1 @@
|
||||
Apr 5 19:31:04 precise-amd64 kernel: [153073.826757] type=1400 audit(1308766940.698:3704): apparmor="DENIED" operation="sendmsg" parent=24737 profile="/usr/bin/evince-thumbnailer" pid=24743 comm="evince-thumbnai" lport=765 fport=2049 family="inet" sock_type="stream" protocol=6
|
@@ -0,0 +1,16 @@
|
||||
START
|
||||
File: test_multi/testcase_network_02.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1308766940.698:3704
|
||||
Operation: sendmsg
|
||||
Profile: /usr/bin/evince-thumbnailer
|
||||
Command: evince-thumbnai
|
||||
Parent: 24737
|
||||
PID: 24743
|
||||
Network family: inet
|
||||
Socket type: stream
|
||||
Protocol: tcp
|
||||
Local port: 765
|
||||
Foreign port: 2049
|
||||
Epoch: 1308766940
|
||||
Audit subid: 3704
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1333648169.009:11707146): apparmor="ALLOWED" operation="accept" parent=25932 profile="/usr/lib/dovecot/imap-login" pid=5049 comm="imap-login" lport=143 family="inet6" sock_type="stream" protocol=6
|
@@ -0,0 +1,15 @@
|
||||
START
|
||||
File: test_multi/testcase_network_03.in
|
||||
Event type: AA_RECORD_ALLOWED
|
||||
Audit ID: 1333648169.009:11707146
|
||||
Operation: accept
|
||||
Profile: /usr/lib/dovecot/imap-login
|
||||
Command: imap-login
|
||||
Parent: 25932
|
||||
PID: 5049
|
||||
Network family: inet6
|
||||
Socket type: stream
|
||||
Protocol: tcp
|
||||
Local port: 143
|
||||
Epoch: 1333648169
|
||||
Audit subid: 11707146
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1333697181.284:273901): apparmor="DENIED" operation="recvmsg" parent=1596 profile="/home/ubuntu/tmp/nc" pid=1056 comm="nc" laddr=::1 lport=2048 faddr=::1 fport=33986 family="inet6" sock_type="stream" protocol=6
|
@@ -0,0 +1,18 @@
|
||||
START
|
||||
File: test_multi/testcase_network_04.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1333697181.284:273901
|
||||
Operation: recvmsg
|
||||
Profile: /home/ubuntu/tmp/nc
|
||||
Command: nc
|
||||
Parent: 1596
|
||||
PID: 1056
|
||||
Network family: inet6
|
||||
Socket type: stream
|
||||
Protocol: tcp
|
||||
Local addr: ::1
|
||||
Foreign addr: ::1
|
||||
Local port: 2048
|
||||
Foreign port: 33986
|
||||
Epoch: 1333697181
|
||||
Audit subid: 273901
|
@@ -0,0 +1 @@
|
||||
type=AVC msg=audit(1333698107.128:273917): apparmor="DENIED" operation="recvmsg" parent=1596 profile="/home/ubuntu/tmp/nc" pid=1875 comm="nc" laddr=::ffff:127.0.0.1 lport=2048 faddr=::ffff:127.0.0.1 fport=59180 family="inet6" sock_type="stream" protocol=6
|
@@ -0,0 +1,18 @@
|
||||
START
|
||||
File: test_multi/testcase_network_05.in
|
||||
Event type: AA_RECORD_DENIED
|
||||
Audit ID: 1333698107.128:273917
|
||||
Operation: recvmsg
|
||||
Profile: /home/ubuntu/tmp/nc
|
||||
Command: nc
|
||||
Parent: 1596
|
||||
PID: 1875
|
||||
Network family: inet6
|
||||
Socket type: stream
|
||||
Protocol: tcp
|
||||
Local addr: ::ffff:127.0.0.1
|
||||
Foreign addr: ::ffff:127.0.0.1
|
||||
Local port: 2048
|
||||
Foreign port: 59180
|
||||
Epoch: 1333698107
|
||||
Audit subid: 273917
|
@@ -76,8 +76,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\"
|
||||
SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
|
||||
parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
|
||||
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
|
||||
parser_alias.c
|
||||
HDRS = parser.h parser_include.h immunix.h
|
||||
parser_alias.c mount.c
|
||||
HDRS = parser.h parser_include.h immunix.h mount.h
|
||||
TOOLS = apparmor_parser
|
||||
|
||||
OBJECTS = $(SRCS:.c=.o)
|
||||
@@ -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)
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# 2008, 2009
|
||||
# NOVELL (All rights reserved)
|
||||
#
|
||||
# Copyright (c) 2010
|
||||
# Copyright (c) 2010 - 2012
|
||||
# Canonical Ltd. (All rights reserved)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
@@ -54,7 +54,7 @@ B<COMMENT> = '#' I<TEXT>
|
||||
|
||||
B<TEXT> = any characters
|
||||
|
||||
B<PROFILE> = [ I<COMMENT> ... ] [ I<VARIABLE ASSIGNMENT> ... ] ( '"' I<PROGRAM> '"' | I<PROGRAM> ) [ 'flags=(complain)' ]'{' [ ( I<RESOURCE RULE> | I<COMMENT> | I<INCLUDE> | I<SUBPROFILE> | 'capability ' I<CAPABILITY> | I<NETWORK RULE> | 'change_profile -> ' I<PROGRAMCHILD> ) ... ] '}'
|
||||
B<PROFILE> = [ I<COMMENT> ... ] [ I<VARIABLE ASSIGNMENT> ... ] ( '"' I<PROGRAM> '"' | I<PROGRAM> ) [ 'flags=(complain)' ]'{' [ ( I<RESOURCE RULE> | I<COMMENT> | I<INCLUDE> | I<SUBPROFILE> | 'capability ' I<CAPABILITY> | I<NETWORK RULE> | I<MOUNT RULE> | I<FILE RULE> | 'change_profile -> ' I<PROGRAMCHILD> ) ... ] '}'
|
||||
|
||||
B<SUBPROFILE> = [ I<COMMENT> ... ] ( I<PROGRAMHAT> | 'profile ' I<PROGRAMCHILD> ) '{' [ ( I<FILE RULE> | I<COMMENT> | I<INCLUDE> ) ... ] '}'
|
||||
|
||||
@@ -75,11 +75,37 @@ B<PROGRAMHAT> = '^' (non-whitespace characters; see aa_change_hat(2) for a desc
|
||||
|
||||
B<PROGRAMCHILD> = I<SUBPROFILE> name
|
||||
|
||||
B<MOUNT RULE> = ( I<MOUNT> | I<REMOUNT> | I<UMOUNT> | I<PIVOT ROOT> )
|
||||
|
||||
B<MOUNT> = [ 'audit' ] [ 'deny' ] 'mount' [ I<MOUNT CONDITIONS> ] [ I<SOURCE FILEGLOB> ] [ -> [ I<MOUNTPOINT FILEGLOB> ]
|
||||
|
||||
B<REMOUNT> = [ 'audit' ] [ 'deny' ] 'remount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
|
||||
|
||||
B<UMOUNT> = [ 'audit' ] [ 'deny' ] 'umount' [ I<MOUNT CONDITIONS> ] I<MOUNTPOINT FILEGLOB>
|
||||
|
||||
B<PIVOT ROOT> = [ 'audit' ] [ 'deny' ] pivot_root [ I<OLD ABS PATH> ] [ I<MOUNTPOINT ABS PATH> ] [ -> I<PROGRAMCHILD> ]
|
||||
|
||||
B<MOUNT CONDITIONS> = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' ) I<MOUNT FSTYPE EXPRESSION> ] [ 'options' ( '=' | 'in' ) I<MOUNT FLAGS EXPRESSION> ]
|
||||
|
||||
B<MOUNT FSTYPE EXPRESSION> = ( I<MOUNT FSTYPE LIST> | I<MOUNT EXPRESSION> )
|
||||
|
||||
B<MOUNT FSTYPE LIST> = Comma separated list of valid filesystem and virtual filesystem types (eg ext4, debugfs, devfs, etc)
|
||||
|
||||
B<MOUNT FLAGS EXPRESSION> = ( I<MOUNT FLAGS LIST> | I<MOUNT EXPRESSION> )
|
||||
|
||||
B<MOUNT FLAGS LIST> = Comma separated list of I<MOUNT FLAGS>.
|
||||
|
||||
B<MOUNT FLAGS> = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | 'noexec' | 'exec' | 'sync' | 'async' | 'remount' | 'mand' | 'nomand' | 'dirsync' | 'nodirsync' | 'noatime' | 'atime' | 'nodiratime' | 'diratime' | 'bind' | 'move' | 'rec' | 'verbose' | 'silent' | 'load' | 'acl' | 'noacl' | 'unbindable' | 'private' | 'slave' | 'shared' | 'relative' | 'norelative' | 'iversion' | 'noiversion' | 'strictatime' | 'nouser' | 'user' )
|
||||
|
||||
B<MOUNT EXPRESSION> = ( I<ALPHANUMERIC> | I<AARE> ) ...
|
||||
|
||||
B<AARE> = B<?*[]{}^> (see below for meanings)
|
||||
|
||||
B<FILE RULE> = I<RULE QUALIFIER> ( '"' I<FILEGLOB> '"' | I<FILEGLOB> ) I<ACCESS> ','
|
||||
|
||||
B<RULE QUALIFIER> = [ 'audit' ] [ 'deny' ] [ 'owner' ]
|
||||
|
||||
B<FILEGLOB> = (must start with '/' (after variable expansion), B<?*[]{}^> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted. Rules must end with '/' to apply to directories.)
|
||||
B<FILEGLOB> = (must start with '/' (after variable expansion), B<AARE> have special meanings; see below. May include I<VARIABLE>. Rules with embedded spaces or tabs must be quoted. Rules must end with '/' to apply to directories.)
|
||||
|
||||
B<ACCESS> = ( 'r' | 'w' | 'l' | 'ix' | 'ux' | 'Ux' | 'px' | 'Px' | 'cx -> ' I<PROGRAMCHILD> | 'Cx -> ' I<PROGRAMCHILD> | 'm' ) [ I<ACCESS> ... ] (not all combinations are allowed; see below.)
|
||||
|
||||
@@ -303,10 +329,6 @@ access is not granted, some capabilities allow loading kernel modules,
|
||||
arbitrary access to IPC, ability to bypass discretionary access controls,
|
||||
and other operations that are typically reserved for the root user.
|
||||
|
||||
The only operations that cannot be controlled in this manner are mount(2),
|
||||
umount(2), and loading new AppArmor policy into the kernel, which are
|
||||
always denied to confined processes.
|
||||
|
||||
=head2 Network Rules
|
||||
|
||||
AppArmor supports simple coarse grained network mediation. The network
|
||||
@@ -328,6 +350,281 @@ eg.
|
||||
network inet tcp, #allow access to tcp only for inet4 addresses
|
||||
network inet6 tcp, #allow access to tcp only for inet6 addresses
|
||||
|
||||
=head2 Mount Rules
|
||||
|
||||
AppArmor supports mount mediation and allows specifying filesystem types and
|
||||
mount flags. The syntax of mount rules in AppArmor is based on the mount(8)
|
||||
command syntax. Mount rules must contain one of the mount, remount, umount or
|
||||
pivot_root keywords, but all mount conditions are optional. Unspecified
|
||||
optional conditionals are assumed to match all entries (eg, not specifying
|
||||
fstype means all fstypes are matched). Due to the complexity of the mount
|
||||
command and how options may be specified, AppArmor allows specifying
|
||||
conditionals three different ways:
|
||||
|
||||
=over 4
|
||||
|
||||
=item 1.
|
||||
|
||||
If a conditional is specified using '=', then the rule only grants permission
|
||||
for mounts matching the exactly specified options. For example, an AppArmor
|
||||
policy with the following rule:
|
||||
|
||||
=over 4
|
||||
|
||||
mount options=ro /dev/foo -> /mnt/,
|
||||
|
||||
=back
|
||||
|
||||
Would match:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro /dev/foo /mnt
|
||||
|
||||
=back
|
||||
|
||||
but not either of these:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro,atime /dev/foo /mnt
|
||||
|
||||
$ mount -o rw /dev/foo /mnt
|
||||
|
||||
=back
|
||||
|
||||
=item 2.
|
||||
|
||||
If a conditional is specified using 'in', then the rule grants permission for
|
||||
mounts matching any combination of the specified options. For example, if an
|
||||
AppArmor policy has the following rule:
|
||||
|
||||
=over 4
|
||||
|
||||
mount options in (ro,atime) /dev/foo -> /mnt/,
|
||||
|
||||
=back
|
||||
|
||||
all of these mount commands will match:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro /dev/foo /mnt
|
||||
|
||||
$ mount -o ro,atime /dev/foo /mnt
|
||||
|
||||
$ mount -o atime /dev/foo /mnt
|
||||
|
||||
=back
|
||||
|
||||
but none of these will:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro,sync /dev/foo /mnt
|
||||
|
||||
$ mount -o ro,atime,sync /dev/foo /mnt
|
||||
|
||||
$ mount -o rw /dev/foo /mnt
|
||||
|
||||
$ mount -o rw,noatime /dev/foo /mnt
|
||||
|
||||
$ mount /dev/foo /mnt
|
||||
|
||||
=back
|
||||
|
||||
=item 3.
|
||||
|
||||
If multiple conditionals are specified in a single mount rule, then the rule
|
||||
grants permission for each set of options. This provides a shorthand when
|
||||
writing mount rules which might help to logically break up a conditional. For
|
||||
example, if an AppArmor policy has the following rule:
|
||||
|
||||
=over 4
|
||||
|
||||
mount options=ro options=atime
|
||||
|
||||
=back
|
||||
|
||||
both of these mount commands will match:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro /dev/foo /mnt
|
||||
|
||||
$ mount -o atime /dev/foo /mnt
|
||||
|
||||
=back
|
||||
|
||||
but this one will not:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro,atime /dev/foo /mnt
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
Note that separate mount rules are distinct and the options do not accumulate.
|
||||
For example, these AppArmor mount rules:
|
||||
|
||||
=over 4
|
||||
|
||||
mount options=ro,
|
||||
mount options=atime,
|
||||
|
||||
=back
|
||||
|
||||
are not equivalent to either of these mount rules:
|
||||
|
||||
=over 4
|
||||
|
||||
mount options=(ro,atime),
|
||||
|
||||
mount options in (ro,atime),
|
||||
|
||||
=back
|
||||
|
||||
To help clarify the flexibility and complexity of mount rules, here are some
|
||||
example rules with accompanying matching commands:
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<mount,>
|
||||
|
||||
the 'mount' rule without any conditionals is the most generic and allows any
|
||||
mount. Equivalent to 'mount fstype=** options=** ** -> /**'.
|
||||
|
||||
=item B<mount /dev/foo,>
|
||||
|
||||
allow mounting of /dev/foo anywhere with any options. Some matching mount
|
||||
commands:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount /dev/foo /mnt
|
||||
|
||||
$ mount -t ext3 /dev/foo /mnt
|
||||
|
||||
$ mount -t vfat /dev/foo /mnt
|
||||
|
||||
$ mount -o ro,atime,noexec,nodiratime /dev/foo /srv/some/mountpoint
|
||||
|
||||
=back
|
||||
|
||||
=item B<mount options=ro /dev/foo,>
|
||||
|
||||
allow mounting of /dev/foo anywhere, as read only. Some matching mount
|
||||
commands:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro /dev/foo /mnt
|
||||
|
||||
$ mount -o ro /dev/foo /some/where/else
|
||||
|
||||
=back
|
||||
|
||||
=item B<mount options=(ro,atime) /dev/foo,>
|
||||
|
||||
allow mount of /dev/foo anywhere, as read only and using inode access times.
|
||||
Some matching mount commands:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro,atime /dev/foo /mnt
|
||||
|
||||
$ mount -o ro,atime /dev/foo /some/where/else
|
||||
|
||||
=back
|
||||
|
||||
=item B<mount options in (ro,atime) /dev/foo,>
|
||||
|
||||
allow mount of /dev/foo anywhere using some combination of 'ro' and 'atime'
|
||||
(see above). Some matching mount commands:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro /dev/foo /mnt
|
||||
|
||||
$ mount -o atime /dev/foo /some/where/else
|
||||
|
||||
$ mount -o ro,atime /dev/foo /some/other/place
|
||||
|
||||
=back
|
||||
|
||||
=item B<mount options=ro /dev/foo, mount options=atime /dev/foo,>
|
||||
|
||||
allow mount of /dev/foo anywhere as read only, and allow mount of /dev/foo
|
||||
anywhere using inode access times. Note this is expressed as two different
|
||||
rules. Matches:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro /dev/foo /mnt/1
|
||||
|
||||
$ mount -o atime /dev/foo /mnt/2
|
||||
|
||||
=back
|
||||
|
||||
=item B<< mount -> /mnt/**, >>
|
||||
|
||||
allow mounting anything under a directory in /mnt/**. Some matching mount
|
||||
commands:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount /dev/foo1 /mnt/1
|
||||
|
||||
$ mount -o ro,atime,noexec,nodiratime /dev/foo2 /mnt/deep/path/foo2
|
||||
|
||||
=back
|
||||
|
||||
=item B<< mount options=ro -> /mnt/**, >>
|
||||
|
||||
allow mounting anything under /mnt/**, as read only. Some matching mount
|
||||
commands:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro /dev/foo1 /mnt/1
|
||||
|
||||
$ mount -o ro /dev/foo2 /mnt/deep/path/foo2
|
||||
|
||||
=back
|
||||
|
||||
=item B<< mount fstype=ext3 options=(rw,atime) /dev/sdb1 -> /mnt/stick/, >>
|
||||
|
||||
allow mounting an ext3 filesystem in /dev/sdb1 on /mnt/stick as read/write and
|
||||
using inode access times. Matches only:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o rw,atime /dev/sdb1 /mnt/stick
|
||||
|
||||
=back
|
||||
|
||||
=item B<< mount options=(ro, atime) options in (nodev, user) /dev/foo -> /mnt/, >>
|
||||
|
||||
allow mounting /dev/foo on /mmt/ read only and using inode access times or
|
||||
allow mounting /dev/foo on /mnt/ with some combination of 'nodev' and 'user'.
|
||||
Matches only:
|
||||
|
||||
=over 4
|
||||
|
||||
$ mount -o ro,atime /dev/foo /mnt
|
||||
|
||||
$ mount -o nodev /dev/foo /mnt
|
||||
|
||||
$ mount -o user /dev/foo /mnt
|
||||
|
||||
$ mount -o nodev,user /dev/foo /mnt
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 Variables
|
||||
|
||||
AppArmor's policy language allows embedding variables into file rules
|
||||
@@ -605,6 +902,29 @@ An example AppArmor profile:
|
||||
|
||||
=back
|
||||
|
||||
=head1 KNOWN BUGS
|
||||
|
||||
=over 4
|
||||
|
||||
Mount options support the use of pattern matching but mount flags are not
|
||||
correctly intersected against specified patterns. Eg, 'mount options=**,'
|
||||
should be equivalent to 'mount,', but it is not. (LP: #965690)
|
||||
|
||||
The fstype may not be matched against when certain mount command flags are
|
||||
used. Specifically fstype matching currently only works when creating a new
|
||||
mount and not remount, bind, etc.
|
||||
|
||||
Mount rules with multiple 'options' conditionals are not applied as documented
|
||||
but instead merged such that 'options in (ro,nodev) options in (atime)' is
|
||||
equivalent to 'options in (ro,nodev,atime)'.
|
||||
|
||||
When specifying mount options with the 'in' conditional, both the positive and
|
||||
negative values match when specifying one or the other. Eg, 'rw' matches when
|
||||
'ro' is specified and 'dev' matches when 'nodev' is specified such that
|
||||
'options in (ro,nodev)' is equivalent to 'options in (rw,dev)'.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
apparmor(7), apparmor_parser(8), aa-complain(1),
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# 2008, 2009
|
||||
# NOVELL (All rights reserved)
|
||||
#
|
||||
# Copyright (c) 2010
|
||||
# Copyright (c) 2010 - 2012
|
||||
# Canonical Ltd. (All rights reserved)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
@@ -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
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# 2008, 2009
|
||||
# NOVELL (All rights reserved)
|
||||
#
|
||||
# Copyright (c) 2010
|
||||
# Copyright (c) 2010 - 2012
|
||||
# Canonical Ltd. (All rights reserved)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
@@ -127,16 +127,21 @@ Perform no caching at all: disables -W, implies -T.
|
||||
|
||||
=item -T, --skip-read-cache
|
||||
|
||||
By default, if a profile's cache is found in /etc/apparmor.d/cache/ and
|
||||
the timestamp is newer than the profile, it will be loaded from the cache.
|
||||
This option disables this cache loading behavior.
|
||||
By default, if a profile's cache is found in the location specified by
|
||||
--cache-loc and the timestamp is newer than the profile, it will be loaded
|
||||
from the cache. This option disables this cache loading behavior.
|
||||
|
||||
=item -W, --write-cache
|
||||
|
||||
Write out cached profiles to /etc/apparmor.d/cache/. Off by default.
|
||||
In cases where abstractions have been changed, and the parser is running
|
||||
with "--replace", it may make sense to also use "--skip-read-cache" with
|
||||
the "--write-cache" option.
|
||||
Write out cached profiles to the location specified in --cache-loc. Off
|
||||
by default. In cases where abstractions have been changed, and the parser
|
||||
is running with "--replace", it may make sense to also use
|
||||
"--skip-read-cache" with the "--write-cache" option.
|
||||
|
||||
=item -L, --cache-loc
|
||||
|
||||
Set the location of the cache directory. If not specified the cache location
|
||||
defaults to /etc/apparmor.d/cache
|
||||
|
||||
=item -Q, --skip-kernel-load
|
||||
|
||||
|
@@ -61,6 +61,7 @@
|
||||
#define AA_PTRACE_PERMS (AA_USER_PTRACE | AA_OTHER_PTRACE)
|
||||
|
||||
#define AA_CHANGE_HAT (1 << 30)
|
||||
#define AA_ONEXEC (1 << 30)
|
||||
#define AA_CHANGE_PROFILE (1 << 31)
|
||||
#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE)
|
||||
|
||||
@@ -96,6 +97,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))
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
TARGET=libapparmor_re.a
|
||||
|
||||
CFLAGS ?= -g -Wall -O2 ${EXTRA_CFLAGS}
|
||||
CXXFLAGS := ${CFLAGS}
|
||||
CXXFLAGS := ${CFLAGS} -std=c++0x
|
||||
|
||||
ARFLAGS=-rcs
|
||||
|
||||
@@ -12,16 +12,16 @@ BISON := bison
|
||||
|
||||
all : ${TARGET}
|
||||
|
||||
libapparmor_re.a: parse.o expr-tree.o hfa.o compressed_hfa.o aare_rules.o
|
||||
libapparmor_re.a: parse.o expr-tree.o hfa.o chfa.o aare_rules.o
|
||||
ar ${ARFLAGS} $@ $^
|
||||
|
||||
expr-tree.o: expr-tree.cc expr-tree.h
|
||||
|
||||
hfa.o: hfa.cc apparmor_re.h hfa.h ../immunix.h
|
||||
|
||||
aare_rules.o: aare_rules.cc aare_rules.h apparmor_re.h expr-tree.h hfa.h compressed_hfa.h parse.h ../immunix.h
|
||||
aare_rules.o: aare_rules.cc aare_rules.h apparmor_re.h expr-tree.h hfa.h chfa.h parse.h ../immunix.h
|
||||
|
||||
compressed_hfa.o: compressed_hfa.cc compressed_hfa.h ../immunix.h
|
||||
chfa.o: chfa.cc chfa.h ../immunix.h
|
||||
|
||||
parse.o : parse.cc apparmor_re.h expr-tree.h
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
@@ -31,7 +31,7 @@
|
||||
#include "expr-tree.h"
|
||||
#include "parse.h"
|
||||
#include "hfa.h"
|
||||
#include "compressed_hfa.h"
|
||||
#include "chfa.h"
|
||||
#include "../immunix.h"
|
||||
|
||||
struct aare_ruleset {
|
||||
@@ -98,6 +98,7 @@ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
|
||||
{
|
||||
Node *tree = NULL, *accept;
|
||||
int exact_match;
|
||||
uint32_t allow = perms;
|
||||
|
||||
assert(perms != 0);
|
||||
|
||||
@@ -220,7 +221,11 @@ extern "C" int aare_add_rule_vec(aare_ruleset_t *rules, int deny,
|
||||
}
|
||||
cerr << " -> ";
|
||||
tree->dump(cerr);
|
||||
cerr << "\n\n";
|
||||
if (deny)
|
||||
cerr << " deny";
|
||||
cerr << " (0x" << hex << allow <<"/" << audit << dec << ")";
|
||||
accept->dump(cerr);
|
||||
cerr << "\n\n";
|
||||
}
|
||||
|
||||
if (rules->root)
|
||||
@@ -270,6 +275,23 @@ extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
|
||||
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("minimized dfa");
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_FILTER_DENY &&
|
||||
flags & DFA_CONTROL_MINIMIZE &&
|
||||
dfa.apply_and_clear_deny()) {
|
||||
/* Do a second minimization pass as removal of deny
|
||||
* information has moved some states from accepting
|
||||
* to none accepting partitions
|
||||
*
|
||||
* TODO: add this as a tail pass to minimization
|
||||
* so we don't need to do a full second pass
|
||||
*/
|
||||
dfa.minimize(flags);
|
||||
|
||||
if (flags & DFA_DUMP_MIN_UNIQ_PERMS)
|
||||
dfa.dump_uniq_perms("minimized dfa");
|
||||
}
|
||||
|
||||
if (flags & DFA_CONTROL_REMOVE_UNREACHABLE)
|
||||
dfa.remove_unreachable(flags);
|
||||
|
||||
@@ -291,10 +313,10 @@ extern "C" void *aare_create_dfa(aare_ruleset_t *rules, size_t *size,
|
||||
} else if (flags & DFA_DUMP_EQUIV)
|
||||
cerr << "\nDFA did not generate an equivalence class\n";
|
||||
|
||||
TransitionTable transition_table(dfa, eq, flags);
|
||||
CHFA chfa(dfa, eq, flags);
|
||||
if (flags & DFA_DUMP_TRANS_TABLE)
|
||||
transition_table.dump(cerr);
|
||||
transition_table.flex_table(stream, "");
|
||||
chfa.dump(cerr);
|
||||
chfa.flex_table(stream, "");
|
||||
}
|
||||
catch(int error) {
|
||||
*size = 0;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
@@ -26,7 +26,7 @@ typedef enum dfaflags {
|
||||
DFA_CONTROL_TREE_LEFT = 1 << 3,
|
||||
DFA_CONTROL_MINIMIZE = 1 << 4,
|
||||
DFA_CONTROL_MINIMIZE_HASH_TRANS = 1 << 5,
|
||||
DFA_CONTROL_MINIMIZE_HASH_PERMS = 1 << 6,
|
||||
DFA_CONTROL_FILTER_DENY = 1 << 6,
|
||||
DFA_CONTROL_REMOVE_UNREACHABLE = 1 << 7,
|
||||
DFA_CONTROL_TRANS_HIGH = 1 << 8,
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
@@ -30,9 +30,10 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "hfa.h"
|
||||
#include "compressed_hfa.h"
|
||||
#include "chfa.h"
|
||||
#include "../immunix.h"
|
||||
|
||||
void TransitionTable::init_free_list(vector<pair<size_t, size_t> > &free_list,
|
||||
void CHFA::init_free_list(vector<pair<size_t, size_t> > &free_list,
|
||||
size_t prev, size_t start)
|
||||
{
|
||||
for (size_t i = start; i < free_list.size(); i++) {
|
||||
@@ -47,11 +48,10 @@ void TransitionTable::init_free_list(vector<pair<size_t, size_t> > &free_list,
|
||||
/**
|
||||
* new Construct the transition table.
|
||||
*/
|
||||
TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
|
||||
dfaflags_t flags): eq(eq)
|
||||
CHFA::CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags): eq(eq)
|
||||
{
|
||||
if (flags & DFA_DUMP_TRANS_PROGRESS)
|
||||
fprintf(stderr, "Compressing trans table:\r");
|
||||
fprintf(stderr, "Compressing HFA:\r");
|
||||
|
||||
if (eq.empty())
|
||||
max_eq = 255;
|
||||
@@ -74,14 +74,14 @@ TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
|
||||
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
|
||||
if (*i == dfa.start || *i == dfa.nonmatching)
|
||||
continue;
|
||||
optimal += (*i)->cases.cases.size();
|
||||
optimal += (*i)->trans.size();
|
||||
if (flags & DFA_CONTROL_TRANS_HIGH) {
|
||||
size_t range = 0;
|
||||
if ((*i)->cases.cases.size())
|
||||
if ((*i)->trans.size())
|
||||
range =
|
||||
(*i)->cases.cases.rbegin()->first -
|
||||
(*i)->cases.begin()->first;
|
||||
size_t ord = ((256 - (*i)->cases.cases.size()) << 8) | (256 - range);
|
||||
(*i)->trans.rbegin()->first -
|
||||
(*i)->trans.begin()->first;
|
||||
size_t ord = ((256 - (*i)->trans.size()) << 8) | (256 - range);
|
||||
/* reverse sort by entry count, most entries first */
|
||||
order.insert(make_pair(ord, *i));
|
||||
}
|
||||
@@ -113,8 +113,8 @@ TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
|
||||
for (Partition::iterator i = dfa.states.begin(); i != dfa.states.end(); i++) {
|
||||
if (*i != dfa.nonmatching && *i != dfa.start) {
|
||||
insert_state(free_list, *i, dfa);
|
||||
accept[num.size()] = (*i)->accept;
|
||||
accept2[num.size()] = (*i)->audit;
|
||||
accept[num.size()] = (*i)->perms.allow;
|
||||
accept2[num.size()] = PACK_AUDIT_CTL((*i)->perms.audit, (*i)->perms.quiet & (*i)->perms.deny);
|
||||
num.insert(make_pair(*i, num.size()));
|
||||
}
|
||||
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
|
||||
@@ -130,8 +130,8 @@ TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
|
||||
if (i->second != dfa.nonmatching &&
|
||||
i->second != dfa.start) {
|
||||
insert_state(free_list, i->second, dfa);
|
||||
accept[num.size()] = i->second->accept;
|
||||
accept2[num.size()] = i->second->audit;
|
||||
accept[num.size()] = i->second->perms.allow;
|
||||
accept2[num.size()] = PACK_AUDIT_CTL(i->second->perms.audit, i->second->perms.quiet & i->second->perms.deny);
|
||||
num.insert(make_pair(i->second, num.size()));
|
||||
}
|
||||
if (flags & (DFA_DUMP_TRANS_PROGRESS)) {
|
||||
@@ -154,14 +154,14 @@ TransitionTable::TransitionTable(DFA &dfa, map<uchar, uchar> &eq,
|
||||
}
|
||||
|
||||
/**
|
||||
* Does <cases> fit into position <base> of the transition table?
|
||||
* Does <trans> fit into position <base> of the transition table?
|
||||
*/
|
||||
bool TransitionTable::fits_in(vector<pair<size_t, size_t> > &free_list
|
||||
bool CHFA::fits_in(vector<pair<size_t, size_t> > &free_list
|
||||
__attribute__ ((unused)), size_t pos,
|
||||
Cases &cases)
|
||||
StateTrans &trans)
|
||||
{
|
||||
size_t c, base = pos - cases.begin()->first;
|
||||
for (Cases::iterator i = cases.begin(); i != cases.end(); i++) {
|
||||
size_t c, base = pos - trans.begin()->first;
|
||||
for (StateTrans::iterator i = trans.begin(); i != trans.end(); i++) {
|
||||
c = base + i->first;
|
||||
/* if it overflows the next_check array it fits in as we will
|
||||
* resize */
|
||||
@@ -177,21 +177,21 @@ bool TransitionTable::fits_in(vector<pair<size_t, size_t> > &free_list
|
||||
/**
|
||||
* Insert <state> of <dfa> into the transition table.
|
||||
*/
|
||||
void TransitionTable::insert_state(vector<pair<size_t, size_t> > &free_list,
|
||||
void CHFA::insert_state(vector<pair<size_t, size_t> > &free_list,
|
||||
State *from, DFA &dfa)
|
||||
{
|
||||
State *default_state = dfa.nonmatching;
|
||||
size_t base = 0;
|
||||
int resize;
|
||||
|
||||
Cases &cases = from->cases;
|
||||
size_t c = cases.begin()->first;
|
||||
StateTrans &trans = from->trans;
|
||||
size_t c = trans.begin()->first;
|
||||
size_t prev = 0;
|
||||
size_t x = first_free;
|
||||
|
||||
if (cases.otherwise)
|
||||
default_state = cases.otherwise;
|
||||
if (cases.cases.empty())
|
||||
if (from->otherwise)
|
||||
default_state = from->otherwise;
|
||||
if (trans.empty())
|
||||
goto do_insert;
|
||||
|
||||
repeat:
|
||||
@@ -203,16 +203,16 @@ repeat:
|
||||
}
|
||||
|
||||
/* try inserting until we succeed. */
|
||||
while (x && !fits_in(free_list, x, cases)) {
|
||||
while (x && !fits_in(free_list, x, trans)) {
|
||||
prev = x;
|
||||
x = free_list[x].second;
|
||||
}
|
||||
if (!x) {
|
||||
resize = 256 - cases.begin()->first;
|
||||
resize = 256 - trans.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));
|
||||
} else if (x + 255 - trans.begin()->first >= next_check.size()) {
|
||||
resize = (255 - trans.begin()->first - (next_check.size() - 1 - x));
|
||||
for (size_t y = x; y; y = free_list[y].second)
|
||||
prev = y;
|
||||
}
|
||||
@@ -229,7 +229,7 @@ repeat:
|
||||
}
|
||||
|
||||
base = x - c;
|
||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||
for (StateTrans::iterator j = trans.begin(); j != trans.end(); j++) {
|
||||
next_check[base + j->first] = make_pair(j->second, from);
|
||||
size_t prev = free_list[base + j->first].first;
|
||||
size_t next = free_list[base + j->first].second;
|
||||
@@ -248,7 +248,7 @@ do_insert:
|
||||
/**
|
||||
* Text-dump the transition table (for debugging).
|
||||
*/
|
||||
void TransitionTable::dump(ostream &os)
|
||||
void CHFA::dump(ostream &os)
|
||||
{
|
||||
map<size_t, const State *> st;
|
||||
for (map<const State *, size_t>::iterator i = num.begin(); i != num.end(); i++) {
|
||||
@@ -342,7 +342,7 @@ template<class Iter>
|
||||
os << fill64(sizeof(td) + sizeof(*pos) * size);
|
||||
}
|
||||
|
||||
void TransitionTable::flex_table(ostream &os, const char *name)
|
||||
void CHFA::flex_table(ostream &os, const char *name)
|
||||
{
|
||||
const char th_version[] = "notflex";
|
||||
struct table_set_header th = { 0, 0, 0, 0 };
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
@@ -16,10 +16,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* Create a compressed hfa from and hfa
|
||||
* Create a compressed hfa (chfa) from and hfa
|
||||
*/
|
||||
#ifndef __LIBAA_RE_COMPRESSED_HFA_H
|
||||
#define __LIBAA_RE_COMPRESSED_HFA_H
|
||||
#ifndef __LIBAA_RE_CHFA_H
|
||||
#define __LIBAA_RE_CHFA_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -28,17 +28,17 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
class TransitionTable {
|
||||
class CHFA {
|
||||
typedef vector<pair<const State *, size_t> > DefaultBase;
|
||||
typedef vector<pair<const State *, const State *> > NextCheck;
|
||||
public:
|
||||
TransitionTable(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags);
|
||||
CHFA(DFA &dfa, map<uchar, uchar> &eq, dfaflags_t flags);
|
||||
void dump(ostream & os);
|
||||
void flex_table(ostream &os, const char *name);
|
||||
void init_free_list(vector<pair<size_t, size_t> > &free_list,
|
||||
size_t prev, size_t start);
|
||||
bool fits_in(vector<pair<size_t, size_t> > &free_list, size_t base,
|
||||
Cases &cases);
|
||||
StateTrans &cases);
|
||||
void insert_state(vector<pair<size_t, size_t> > &free_list,
|
||||
State *state, DFA &dfa);
|
||||
|
||||
@@ -53,4 +53,4 @@ class TransitionTable {
|
||||
size_t first_free;
|
||||
};
|
||||
|
||||
#endif /* __LIBAA_RE_COMPRESSED_HFA_H */
|
||||
#endif /* __LIBAA_RE_CHFA_H */
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the 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) &&
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
@@ -81,17 +81,15 @@ ostream &operator<<(ostream &os, const NodeSet &state);
|
||||
* (i.e., following an AnyCharNode or NotCharSetNode). This avoids
|
||||
* enumerating all the explicit tranitions for default matches.
|
||||
*/
|
||||
typedef struct NodeCases {
|
||||
typedef struct Cases {
|
||||
typedef map<uchar, NodeSet *>::iterator iterator;
|
||||
iterator begin() { return cases.begin(); }
|
||||
iterator end() { return cases.end(); }
|
||||
|
||||
NodeCases(): otherwise(0) { }
|
||||
Cases(): otherwise(0) { }
|
||||
map<uchar, NodeSet *> cases;
|
||||
NodeSet *otherwise;
|
||||
}
|
||||
|
||||
NodeCases;
|
||||
} Cases;
|
||||
|
||||
ostream &operator<<(ostream &os, Node &node);
|
||||
|
||||
@@ -205,7 +203,8 @@ public:
|
||||
ImportantNode(): LeafNode() { }
|
||||
void compute_firstpos() { firstpos.insert(this); }
|
||||
void compute_lastpos() { lastpos.insert(this); }
|
||||
virtual void follow(NodeCases &cases) = 0;
|
||||
virtual void follow(Cases &cases) = 0;
|
||||
virtual int is_accept(void) = 0;
|
||||
};
|
||||
|
||||
/* common base class for all the different classes that contain
|
||||
@@ -214,13 +213,14 @@ public:
|
||||
class CNode: public ImportantNode {
|
||||
public:
|
||||
CNode(): ImportantNode() { }
|
||||
int is_accept(void) { return false; }
|
||||
};
|
||||
|
||||
/* Match one specific character (/c/). */
|
||||
class CharNode: public CNode {
|
||||
public:
|
||||
CharNode(uchar c): c(c) { }
|
||||
void follow(NodeCases &cases)
|
||||
void follow(Cases &cases)
|
||||
{
|
||||
NodeSet **x = &cases.cases[c];
|
||||
if (!*x) {
|
||||
@@ -251,7 +251,7 @@ public:
|
||||
class CharSetNode: public CNode {
|
||||
public:
|
||||
CharSetNode(Chars &chars): chars(chars) { }
|
||||
void follow(NodeCases &cases)
|
||||
void follow(Cases &cases)
|
||||
{
|
||||
for (Chars::iterator i = chars.begin(); i != chars.end(); i++) {
|
||||
NodeSet **x = &cases.cases[*i];
|
||||
@@ -292,7 +292,7 @@ public:
|
||||
class NotCharSetNode: public CNode {
|
||||
public:
|
||||
NotCharSetNode(Chars &chars): chars(chars) { }
|
||||
void follow(NodeCases & cases)
|
||||
void follow(Cases &cases)
|
||||
{
|
||||
if (!cases.otherwise)
|
||||
cases.otherwise = new NodeSet;
|
||||
@@ -305,7 +305,7 @@ public:
|
||||
* the old otherwise state for the matching characters.
|
||||
*/
|
||||
cases.otherwise->insert(followpos.begin(), followpos.end());
|
||||
for (NodeCases::iterator i = cases.begin(); i != cases.end();
|
||||
for (Cases::iterator i = cases.begin(); i != cases.end();
|
||||
i++) {
|
||||
if (chars.find(i->first) == chars.end())
|
||||
i->second->insert(followpos.begin(),
|
||||
@@ -340,12 +340,12 @@ public:
|
||||
class AnyCharNode: public CNode {
|
||||
public:
|
||||
AnyCharNode() { }
|
||||
void follow(NodeCases &cases)
|
||||
void follow(Cases &cases)
|
||||
{
|
||||
if (!cases.otherwise)
|
||||
cases.otherwise = new NodeSet;
|
||||
cases.otherwise->insert(followpos.begin(), followpos.end());
|
||||
for (NodeCases::iterator i = cases.begin(); i != cases.end();
|
||||
for (Cases::iterator i = cases.begin(); i != cases.end();
|
||||
i++)
|
||||
i->second->insert(followpos.begin(), followpos.end());
|
||||
}
|
||||
@@ -365,6 +365,7 @@ public:
|
||||
class AcceptNode: public ImportantNode {
|
||||
public:
|
||||
AcceptNode() { }
|
||||
int is_accept(void) { return true; }
|
||||
void release(void)
|
||||
{
|
||||
/* don't delete AcceptNode via release as they are shared, and
|
||||
@@ -372,7 +373,7 @@ public:
|
||||
*/
|
||||
}
|
||||
|
||||
void follow(NodeCases &cases __attribute__ ((unused)))
|
||||
void follow(Cases &cases __attribute__ ((unused)))
|
||||
{
|
||||
/* Nothing to follow. */
|
||||
}
|
||||
@@ -573,22 +574,6 @@ void label_nodes(Node *root);
|
||||
unsigned long hash_NodeSet(NodeSet *ns);
|
||||
void flip_tree(Node *node);
|
||||
|
||||
/* Comparison operator for sets of <NodeSet *>.
|
||||
* Compare set hashes, and if the sets have the same hash
|
||||
* do compare pointer comparison on set of <Node *>, the pointer comparison
|
||||
* allows us to determine which Sets of <Node *> we have seen already from
|
||||
* new ones when constructing the DFA.
|
||||
*/
|
||||
struct deref_less_than {
|
||||
bool operator()(pair<unsigned long, NodeSet *>const &lhs,
|
||||
pair<unsigned long, NodeSet *>const &rhs)const
|
||||
{
|
||||
if (lhs.first == rhs.first)
|
||||
return *(lhs.second) < *(rhs.second);
|
||||
else
|
||||
return lhs.first < rhs.first;
|
||||
}
|
||||
};
|
||||
|
||||
class MatchFlag: public AcceptNode {
|
||||
public:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
@@ -30,11 +30,40 @@
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string.h>
|
||||
|
||||
#include "expr-tree.h"
|
||||
#include "hfa.h"
|
||||
#include "../immunix.h"
|
||||
|
||||
|
||||
ostream &operator<<(ostream &os, const CacheStats &cache)
|
||||
{
|
||||
/* dump the state label */
|
||||
os << "cache: size=";
|
||||
os << cache.size();
|
||||
os << " dups=";
|
||||
os << cache.dup;
|
||||
os << " longest=";
|
||||
os << cache.max;
|
||||
if (cache.size()) {
|
||||
os << " avg=";
|
||||
os << cache.sum / cache.size();
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &os, const ProtoState &proto)
|
||||
{
|
||||
/* dump the state label */
|
||||
os << '{';
|
||||
os << proto.nnodes;
|
||||
os << ',';
|
||||
os << proto.anodes;
|
||||
os << '}';
|
||||
return os;
|
||||
}
|
||||
|
||||
ostream &operator<<(ostream &os, const State &state)
|
||||
{
|
||||
/* dump the state label */
|
||||
@@ -44,46 +73,48 @@ ostream &operator<<(ostream &os, const State &state)
|
||||
return os;
|
||||
}
|
||||
|
||||
State *DFA::add_new_state(NodeMap &nodemap,
|
||||
pair<unsigned long, NodeSet *> index,
|
||||
NodeSet *nodes, dfa_stats_t &stats)
|
||||
static void split_node_types(NodeSet *nodes, NodeSet **anodes, NodeSet **nnodes
|
||||
)
|
||||
{
|
||||
State *state = new State(nodemap.size(), nodes);
|
||||
states.push_back(state);
|
||||
nodemap.insert(make_pair(index, state));
|
||||
stats.proto_sum += nodes->size();
|
||||
if (nodes->size() > stats.proto_max)
|
||||
stats.proto_max = nodes->size();
|
||||
return state;
|
||||
*anodes = *nnodes = NULL;
|
||||
for (NodeSet::iterator i = nodes->begin(); i != nodes->end(); ) {
|
||||
if ((*i)->is_accept()) {
|
||||
if (!*anodes)
|
||||
*anodes = new NodeSet;
|
||||
(*anodes)->insert(*i);
|
||||
NodeSet::iterator k = i++;
|
||||
nodes->erase(k);
|
||||
} else
|
||||
i++;
|
||||
}
|
||||
*nnodes = nodes;
|
||||
}
|
||||
|
||||
State *DFA::find_target_state(NodeMap &nodemap, list<State *> &work_queue,
|
||||
NodeSet *nodes, dfa_stats_t &stats)
|
||||
State *DFA::add_new_state(NodeSet *nodes, State *other)
|
||||
{
|
||||
State *target;
|
||||
/* The splitting of nodes should probably get pushed down into
|
||||
* follow(), ie. put in separate lists from the start
|
||||
*/
|
||||
NodeSet *anodes, *nnodes;
|
||||
hashedNodeVec *nnodev;
|
||||
split_node_types(nodes, &anodes, &nnodes);
|
||||
nnodev = nnodes_cache.insert(nnodes);
|
||||
anodes = anodes_cache.insert(anodes);
|
||||
|
||||
pair<unsigned long, NodeSet *> index = make_pair(hash_NodeSet(nodes), nodes);
|
||||
|
||||
map<pair<unsigned long, NodeSet *>, State *, deref_less_than>::iterator x = nodemap.find(index);
|
||||
|
||||
if (x == nodemap.end()) {
|
||||
/* set of nodes isn't known so create new state, and nodes to
|
||||
* state mapping
|
||||
*/
|
||||
target = add_new_state(nodemap, index, nodes, stats);
|
||||
work_queue.push_back(target);
|
||||
ProtoState proto(nnodev, anodes);
|
||||
State *state = new State(node_map.size(), proto, other);
|
||||
pair<NodeMap::iterator,bool> x = node_map.insert(proto, state);
|
||||
if (x.second == false) {
|
||||
delete state;
|
||||
} else {
|
||||
/* set of nodes already has a mapping so free this one */
|
||||
stats.duplicates++;
|
||||
delete(nodes);
|
||||
target = x->second;
|
||||
states.push_back(state);
|
||||
work_queue.push_back(state);
|
||||
}
|
||||
|
||||
return target;
|
||||
return x.first->second;
|
||||
}
|
||||
|
||||
void DFA::update_state_transitions(NodeMap &nodemap, list<State *> &work_queue,
|
||||
State *state, dfa_stats_t &stats)
|
||||
void DFA::update_state_transitions(State *state)
|
||||
{
|
||||
/* Compute possible transitions for state->nodes. This is done by
|
||||
* iterating over all the nodes in state->nodes and combining the
|
||||
@@ -91,9 +122,12 @@ void DFA::update_state_transitions(NodeMap &nodemap, list<State *> &work_queue,
|
||||
*
|
||||
* The resultant transition set is a mapping of characters to
|
||||
* sets of nodes.
|
||||
*
|
||||
* Note: the follow set for accept nodes is always empty so we don't
|
||||
* need to compute follow for the accept nodes in a protostate
|
||||
*/
|
||||
NodeCases cases;
|
||||
for (NodeSet::iterator i = state->nodes->begin(); i != state->nodes->end(); i++)
|
||||
Cases cases;
|
||||
for (hashedNodeVec::iterator i = state->proto.nnodes->begin(); i != state->proto.nnodes->end(); i++)
|
||||
(*i)->follow(cases);
|
||||
|
||||
/* Now for each set of nodes in the computed transitions, make
|
||||
@@ -103,22 +137,22 @@ void DFA::update_state_transitions(NodeMap &nodemap, list<State *> &work_queue,
|
||||
|
||||
/* check the default transition first */
|
||||
if (cases.otherwise)
|
||||
state->cases.otherwise = find_target_state(nodemap, work_queue,
|
||||
cases.otherwise,
|
||||
stats);;
|
||||
state->otherwise = add_new_state(cases.otherwise, nonmatching);
|
||||
else
|
||||
state->otherwise = nonmatching;
|
||||
|
||||
/* For each transition from *from, check if the set of nodes it
|
||||
* transitions to already has been mapped to a state
|
||||
*/
|
||||
for (NodeCases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||
State *target;
|
||||
target = find_target_state(nodemap, work_queue, j->second, stats);
|
||||
target = add_new_state(j->second, nonmatching);
|
||||
|
||||
/* Don't insert transition that the default transition
|
||||
/* Don't insert transition that the otherwise transition
|
||||
* already covers
|
||||
*/
|
||||
if (target != state->cases.otherwise)
|
||||
state->cases.cases[j->first] = target;
|
||||
if (target != state->otherwise)
|
||||
state->trans[j->first] = target;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +165,7 @@ void DFA::dump_node_to_dfa(void)
|
||||
" State <= Nodes\n"
|
||||
"-------------------\n";
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
cerr << " " << (*i)->label << " <= " << *(*i)->nodes << "\n";
|
||||
cerr << " " << (*i)->label << " <= " << (*i)->proto << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +173,6 @@ void DFA::dump_node_to_dfa(void)
|
||||
*/
|
||||
DFA::DFA(Node *root, dfaflags_t flags): root(root)
|
||||
{
|
||||
dfa_stats_t stats = { 0, 0, 0 };
|
||||
int i = 0;
|
||||
|
||||
if (flags & DFA_DUMP_PROGRESS)
|
||||
@@ -157,15 +190,8 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
|
||||
(*i)->compute_followpos();
|
||||
}
|
||||
|
||||
NodeMap nodemap;
|
||||
NodeSet *emptynode = new NodeSet;
|
||||
nonmatching = add_new_state(nodemap,
|
||||
make_pair(hash_NodeSet(emptynode), emptynode),
|
||||
emptynode, stats);
|
||||
|
||||
NodeSet *first = new NodeSet(root->firstpos);
|
||||
start = add_new_state(nodemap, make_pair(hash_NodeSet(first), first),
|
||||
first, stats);
|
||||
nonmatching = add_new_state(new NodeSet, NULL);
|
||||
start = add_new_state(new NodeSet(root->firstpos), nonmatching);
|
||||
|
||||
/* the work_queue contains the states that need to have their
|
||||
* transitions computed. This could be done with a recursive
|
||||
@@ -177,14 +203,18 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
|
||||
* manner, this may help reduce the number of entries on the
|
||||
* work_queue at any given time, thus reducing peak memory use.
|
||||
*/
|
||||
list<State *> work_queue;
|
||||
work_queue.push_back(start);
|
||||
|
||||
while (!work_queue.empty()) {
|
||||
if (i % 1000 == 0 && (flags & DFA_DUMP_PROGRESS))
|
||||
fprintf(stderr, "\033[2KCreating dfa: queue %zd\tstates %zd\teliminated duplicates %d\r",
|
||||
work_queue.size(), states.size(),
|
||||
stats.duplicates);
|
||||
if (i % 1000 == 0 && (flags & DFA_DUMP_PROGRESS)) {
|
||||
cerr << "\033[2KCreating dfa: queue "
|
||||
<< work_queue.size()
|
||||
<< "\tstates "
|
||||
<< states.size()
|
||||
<< "\teliminated duplicates "
|
||||
<< node_map.dup
|
||||
<< "\r";
|
||||
}
|
||||
i++;
|
||||
|
||||
State *from = work_queue.front();
|
||||
@@ -193,7 +223,7 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
|
||||
/* Update 'from's transitions, and if it transitions to any
|
||||
* unknown State create it and add it to the work_queue
|
||||
*/
|
||||
update_state_transitions(nodemap, work_queue, from, stats);
|
||||
update_state_transitions(from);
|
||||
|
||||
} /* while (!work_queue.empty()) */
|
||||
|
||||
@@ -209,34 +239,68 @@ DFA::DFA(Node *root, dfaflags_t flags): root(root)
|
||||
if (flags & DFA_DUMP_NODE_TO_DFA)
|
||||
dump_node_to_dfa();
|
||||
|
||||
for (NodeMap::iterator i = nodemap.begin(); i != nodemap.end(); i++)
|
||||
delete i->first.second;
|
||||
nodemap.clear();
|
||||
|
||||
if (flags & (DFA_DUMP_STATS))
|
||||
fprintf(stderr, "\033[2KCreated dfa: states %zd,\teliminated duplicates %d,\tprotostate sets: longest %u, avg %u\n",
|
||||
states.size(), stats.duplicates, stats.proto_max,
|
||||
(unsigned int)(stats.proto_sum / states.size()));
|
||||
if (flags & (DFA_DUMP_STATS)) {
|
||||
cerr << "\033[2KCreated dfa: states "
|
||||
<< states.size()
|
||||
<< " proto { "
|
||||
<< node_map
|
||||
<< " }, nnodes { "
|
||||
<< nnodes_cache
|
||||
<< " }, anodes { "
|
||||
<< anodes_cache
|
||||
<< " }\n";
|
||||
}
|
||||
|
||||
/* Clear out uniq_nnodes as they are no longer needed.
|
||||
* Do not clear out uniq_anodes, as we need them for minimizations
|
||||
* diffs, unions, ...
|
||||
*/
|
||||
nnodes_cache.clear();
|
||||
node_map.clear();
|
||||
}
|
||||
|
||||
DFA::~DFA()
|
||||
{
|
||||
anodes_cache.clear();
|
||||
nnodes_cache.clear();
|
||||
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
delete *i;
|
||||
}
|
||||
|
||||
State *DFA::match_len(State *state, const char *str, size_t len)
|
||||
{
|
||||
for (; len > 0; ++str, --len)
|
||||
state = state->next(*str);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
State *DFA::match_until(State *state, const char *str, const char term)
|
||||
{
|
||||
while (*str != term)
|
||||
state = state->next(*str++);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
State *DFA::match(const char *str)
|
||||
{
|
||||
return match_until(start, str, 0);
|
||||
}
|
||||
|
||||
void DFA::dump_uniq_perms(const char *s)
|
||||
{
|
||||
set<pair<uint32_t, uint32_t> > uniq;
|
||||
set<perms_t> uniq;
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
uniq.insert(make_pair((*i)->accept, (*i)->audit));
|
||||
uniq.insert((*i)->perms);
|
||||
|
||||
cerr << "Unique Permission sets: " << s << " (" << uniq.size() << ")\n";
|
||||
cerr << "----------------------\n";
|
||||
for (set<pair<uint32_t, uint32_t> >::iterator i = uniq.begin();
|
||||
i != uniq.end(); i++) {
|
||||
cerr << " " << hex << i->first << " " << i->second << dec << "\n";
|
||||
for (set<perms_t >::iterator i = uniq.begin(); i != uniq.end(); i++) {
|
||||
cerr << " allow:" << hex << i->allow << " deny:"
|
||||
<< i->deny << " audit:" << i->audit
|
||||
<< " quiet:" << i->quiet << dec << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +308,6 @@ void DFA::dump_uniq_perms(const char *s)
|
||||
void DFA::remove_unreachable(dfaflags_t flags)
|
||||
{
|
||||
set<State *> reachable;
|
||||
list<State *> work_queue;
|
||||
|
||||
/* find the set of reachable states */
|
||||
reachable.insert(nonmatching);
|
||||
@@ -254,11 +317,11 @@ void DFA::remove_unreachable(dfaflags_t flags)
|
||||
work_queue.pop_front();
|
||||
reachable.insert(from);
|
||||
|
||||
if (from->cases.otherwise &&
|
||||
(reachable.find(from->cases.otherwise) == reachable.end()))
|
||||
work_queue.push_back(from->cases.otherwise);
|
||||
if (from->otherwise != nonmatching &&
|
||||
reachable.find(from->otherwise) == reachable.end())
|
||||
work_queue.push_back(from->otherwise);
|
||||
|
||||
for (Cases::iterator j = from->cases.begin(); j != from->cases.end(); j++) {
|
||||
for (StateTrans::iterator j = from->trans.begin(); j != from->trans.end(); j++) {
|
||||
if (reachable.find(j->second) == reachable.end())
|
||||
work_queue.push_back(j->second);
|
||||
}
|
||||
@@ -277,12 +340,8 @@ void DFA::remove_unreachable(dfaflags_t flags)
|
||||
cerr << "unreachable: " << **i;
|
||||
if (*i == start)
|
||||
cerr << " <==";
|
||||
if ((*i)->accept) {
|
||||
cerr << " (0x" << hex
|
||||
<< (*i)->accept << " "
|
||||
<< (*i)->audit << dec
|
||||
<< ')';
|
||||
}
|
||||
if ((*i)->perms.is_accept())
|
||||
(*i)->perms.dump(cerr);
|
||||
cerr << "\n";
|
||||
}
|
||||
State *current = *i;
|
||||
@@ -301,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->cases.otherwise && s1->cases.otherwise != nonmatching) {
|
||||
if (!s2->cases.otherwise || s2->cases.otherwise == nonmatching)
|
||||
return false;
|
||||
Partition *p1 = s1->cases.otherwise->partition;
|
||||
Partition *p2 = s2->cases.otherwise->partition;
|
||||
if (p1 != p2)
|
||||
return false;
|
||||
} else if (s2->cases.otherwise && s2->cases.otherwise != nonmatching) {
|
||||
if (s1->otherwise->partition != s2->otherwise->partition)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s1->cases.cases.size() != s2->cases.cases.size())
|
||||
if (s1->trans.size() != s2->trans.size())
|
||||
return false;
|
||||
for (Cases::iterator j1 = s1->cases.begin(); j1 != s1->cases.end(); j1++) {
|
||||
Cases::iterator j2 = s2->cases.cases.find(j1->first);
|
||||
if (j2 == s2->cases.end())
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -330,7 +380,7 @@ bool DFA::same_mappings(State *s1, State *s2)
|
||||
/* Do simple djb2 hashing against a States transition cases
|
||||
* this provides a rough initial guess at state equivalence as if a state
|
||||
* has a different number of transitions or has transitions on different
|
||||
* cases they will never be equivalent.
|
||||
* trans they will never be equivalent.
|
||||
* Note: this only hashes based off of the alphabet (not destination)
|
||||
* as different destinations could end up being equiv
|
||||
*/
|
||||
@@ -338,22 +388,31 @@ size_t DFA::hash_trans(State *s)
|
||||
{
|
||||
unsigned long hash = 5381;
|
||||
|
||||
for (Cases::iterator j = s->cases.begin(); j != s->cases.end(); j++) {
|
||||
for (StateTrans::iterator j = s->trans.begin(); j != s->trans.end(); j++) {
|
||||
hash = ((hash << 5) + hash) + j->first;
|
||||
State *k = j->second;
|
||||
hash = ((hash << 5) + hash) + k->cases.cases.size();
|
||||
hash = ((hash << 5) + hash) + k->trans.size();
|
||||
}
|
||||
|
||||
if (s->cases.otherwise && s->cases.otherwise != nonmatching) {
|
||||
if (s->otherwise != nonmatching) {
|
||||
hash = ((hash << 5) + hash) + 5381;
|
||||
State *k = s->cases.otherwise;
|
||||
hash = ((hash << 5) + hash) + k->cases.cases.size();
|
||||
State *k = s->otherwise;
|
||||
hash = ((hash << 5) + hash) + k->trans.size();
|
||||
}
|
||||
|
||||
hash = (hash << 8) | s->cases.cases.size();
|
||||
hash = (hash << 8) | s->trans.size();
|
||||
return hash;
|
||||
}
|
||||
|
||||
int DFA::apply_and_clear_deny(void)
|
||||
{
|
||||
int c = 0;
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++)
|
||||
c += (*i)->apply_and_clear_deny();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* minimize the number of dfa states */
|
||||
void DFA::minimize(dfaflags_t flags)
|
||||
{
|
||||
@@ -373,19 +432,11 @@ void DFA::minimize(dfaflags_t flags)
|
||||
int accept_count = 0;
|
||||
int final_accept = 0;
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
uint64_t perm_hash = 0;
|
||||
if (flags & DFA_CONTROL_MINIMIZE_HASH_PERMS) {
|
||||
/* make every unique perm create a new partition */
|
||||
perm_hash = ((uint64_t) (*i)->audit) << 32 |
|
||||
(uint64_t) (*i)->accept;
|
||||
} else if ((*i)->audit || (*i)->accept) {
|
||||
/* combine all perms together into a single parition */
|
||||
perm_hash = 1;
|
||||
} /* else not an accept state so 0 for perm_hash */
|
||||
size_t trans_hash = 0;
|
||||
size_t hash = 0;
|
||||
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)
|
||||
trans_hash = hash_trans(*i);
|
||||
pair<uint64_t, size_t> group = make_pair(perm_hash, trans_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();
|
||||
@@ -393,7 +444,7 @@ void DFA::minimize(dfaflags_t flags)
|
||||
perm_map.insert(make_pair(group, part));
|
||||
partitions.push_back(part);
|
||||
(*i)->partition = part;
|
||||
if (perm_hash)
|
||||
if (permtype)
|
||||
accept_count++;
|
||||
} else {
|
||||
(*i)->partition = p->second;
|
||||
@@ -487,11 +538,9 @@ void DFA::minimize(dfaflags_t flags)
|
||||
cerr << *rep << " : ";
|
||||
|
||||
/* update representative state's transitions */
|
||||
if (rep->cases.otherwise) {
|
||||
Partition *partition = rep->cases.otherwise->partition;
|
||||
rep->cases.otherwise = *partition->begin();
|
||||
}
|
||||
for (Cases::iterator c = rep->cases.begin(); c != rep->cases.end(); c++) {
|
||||
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();
|
||||
}
|
||||
@@ -505,10 +554,9 @@ void DFA::minimize(dfaflags_t flags)
|
||||
if (flags & DFA_DUMP_MIN_PARTS)
|
||||
cerr << **i << ", ";
|
||||
(*i)->label = -1;
|
||||
rep->accept |= (*i)->accept;
|
||||
rep->audit |= (*i)->audit;
|
||||
rep->perms.add((*i)->perms);
|
||||
}
|
||||
if (rep->accept || rep->audit)
|
||||
if (rep->perms.is_accept())
|
||||
final_accept++;
|
||||
//if ((*p)->size() > 1)
|
||||
//cerr << "\n";
|
||||
@@ -563,26 +611,52 @@ out:
|
||||
void DFA::dump(ostream & os)
|
||||
{
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
if (*i == start || (*i)->accept) {
|
||||
if (*i == start || (*i)->perms.is_accept()) {
|
||||
os << **i;
|
||||
if (*i == start)
|
||||
os << " <==";
|
||||
if ((*i)->accept) {
|
||||
os << " (0x" << hex << (*i)->accept << " "
|
||||
<< (*i)->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)->cases.otherwise)
|
||||
os << **i << " -> " << (*i)->cases.otherwise << "\n";
|
||||
for (Cases::iterator j = (*i)->cases.begin();
|
||||
j != (*i)->cases.end(); j++) {
|
||||
os << **i << " -> " << j->second << ": "
|
||||
<< j->first << "\n";
|
||||
Chars excluded;
|
||||
|
||||
for (StateTrans::iterator j = (*i)->trans.begin();
|
||||
j != (*i)->trans.end(); j++) {
|
||||
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";
|
||||
@@ -603,35 +677,42 @@ void DFA::dump_dot_graph(ostream & os)
|
||||
if (*i == start) {
|
||||
os << "\t\tstyle=bold" << "\n";
|
||||
}
|
||||
uint32_t perms = (*i)->accept;
|
||||
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";
|
||||
}
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
Cases &cases = (*i)->cases;
|
||||
Chars excluded;
|
||||
|
||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++) {
|
||||
for (StateTrans::iterator j = (*i)->trans.begin(); j != (*i)->trans.end(); j++) {
|
||||
if (j->second == nonmatching)
|
||||
excluded.insert(j->first);
|
||||
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 (cases.otherwise && cases.otherwise != nonmatching) {
|
||||
os << "\t\"" << **i << "\" -> \"" << *cases.otherwise
|
||||
if ((*i)->otherwise != nonmatching) {
|
||||
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";
|
||||
}
|
||||
@@ -651,11 +732,9 @@ map<uchar, uchar> DFA::equivalence_classes(dfaflags_t flags)
|
||||
uchar next_class = 1;
|
||||
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
Cases & cases = (*i)->cases;
|
||||
|
||||
/* Group edges to the same next state together */
|
||||
map<const State *, Chars> node_sets;
|
||||
for (Cases::iterator j = cases.begin(); j != cases.end(); j++)
|
||||
for (StateTrans::iterator j = (*i)->trans.begin(); j != (*i)->trans.end(); j++)
|
||||
node_sets[j->second].insert(j->first);
|
||||
|
||||
for (map<const State *, Chars>::iterator j = node_sets.begin();
|
||||
@@ -742,10 +821,9 @@ void DFA::apply_equivalence_classes(map<uchar, uchar> &eq)
|
||||
*/
|
||||
for (Partition::iterator i = states.begin(); i != states.end(); i++) {
|
||||
map<uchar, State *> tmp;
|
||||
tmp.swap((*i)->cases.cases);
|
||||
for (Cases::iterator j = tmp.begin(); j != tmp.end(); j++)
|
||||
(*i)->cases.cases.
|
||||
insert(make_pair(eq[j->first], j->second));
|
||||
tmp.swap((*i)->trans);
|
||||
for (StateTrans::iterator j = tmp.begin(); j != tmp.end(); j++)
|
||||
(*i)->trans.insert(make_pair(eq[j->first], j->second));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,81 +867,66 @@ static inline int diff_qualifiers(uint32_t perm1, uint32_t perm2)
|
||||
* have any exact matches, then they override the execute and safe
|
||||
* execute flags.
|
||||
*/
|
||||
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error)
|
||||
int accept_perms(NodeSet *state, perms_t &perms)
|
||||
{
|
||||
uint32_t perms = 0, exact_match_perms = 0;
|
||||
uint32_t audit = 0, exact_audit = 0, quiet = 0, deny = 0;
|
||||
int error = 0;
|
||||
uint32_t exact_match_allow = 0;
|
||||
uint32_t exact_audit = 0;
|
||||
|
||||
perms.clear();
|
||||
|
||||
if (!state)
|
||||
return error;
|
||||
|
||||
if (error)
|
||||
*error = 0;
|
||||
for (NodeSet::iterator i = state->begin(); i != state->end(); i++) {
|
||||
MatchFlag *match;
|
||||
if (!(match = dynamic_cast<MatchFlag *>(*i)))
|
||||
continue;
|
||||
if (dynamic_cast<ExactMatchFlag *>(match)) {
|
||||
/* exact match only ever happens with x */
|
||||
if (!is_merged_x_consistent(exact_match_perms,
|
||||
match->flag) && error)
|
||||
*error = 1;;
|
||||
exact_match_perms |= match->flag;
|
||||
if (!is_merged_x_consistent(exact_match_allow,
|
||||
match->flag))
|
||||
error = 1;;
|
||||
exact_match_allow |= match->flag;
|
||||
exact_audit |= match->audit;
|
||||
} else if (dynamic_cast<DenyMatchFlag *>(match)) {
|
||||
deny |= match->flag;
|
||||
quiet |= match->audit;
|
||||
perms.deny |= match->flag;
|
||||
perms.quiet |= match->audit;
|
||||
} else {
|
||||
if (!is_merged_x_consistent(perms, match->flag)
|
||||
&& error)
|
||||
*error = 1;
|
||||
perms |= match->flag;
|
||||
audit |= match->audit;
|
||||
if (!is_merged_x_consistent(perms.allow, match->flag))
|
||||
error = 1;
|
||||
perms.allow |= match->flag;
|
||||
perms.audit |= match->audit;
|
||||
}
|
||||
}
|
||||
|
||||
//if (audit || quiet)
|
||||
//fprintf(stderr, "perms: 0x%x, audit: 0x%x exact: 0x%x eaud: 0x%x deny: 0x%x quiet: 0x%x\n", perms, audit, exact_match_perms, exact_audit, deny, quiet);
|
||||
perms.allow |= exact_match_allow & ~(ALL_AA_EXEC_TYPE);
|
||||
|
||||
perms |= exact_match_perms & ~(AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE);
|
||||
|
||||
if (exact_match_perms & AA_USER_EXEC_TYPE) {
|
||||
perms = (exact_match_perms & AA_USER_EXEC_TYPE) |
|
||||
(perms & ~AA_USER_EXEC_TYPE);
|
||||
audit = (exact_audit & AA_USER_EXEC_TYPE) |
|
||||
(audit & ~AA_USER_EXEC_TYPE);
|
||||
if (exact_match_allow & AA_USER_EXEC_TYPE) {
|
||||
perms.allow = (exact_match_allow & AA_USER_EXEC_TYPE) |
|
||||
(perms.allow & ~AA_USER_EXEC_TYPE);
|
||||
perms.audit = (exact_audit & AA_USER_EXEC_TYPE) |
|
||||
(perms.audit & ~AA_USER_EXEC_TYPE);
|
||||
perms.exact = AA_USER_EXEC_TYPE;
|
||||
}
|
||||
if (exact_match_perms & AA_OTHER_EXEC_TYPE) {
|
||||
perms = (exact_match_perms & AA_OTHER_EXEC_TYPE) |
|
||||
(perms & ~AA_OTHER_EXEC_TYPE);
|
||||
audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
|
||||
(audit & ~AA_OTHER_EXEC_TYPE);
|
||||
if (exact_match_allow & AA_OTHER_EXEC_TYPE) {
|
||||
perms.allow = (exact_match_allow & AA_OTHER_EXEC_TYPE) |
|
||||
(perms.allow & ~AA_OTHER_EXEC_TYPE);
|
||||
perms.audit = (exact_audit & AA_OTHER_EXEC_TYPE) |
|
||||
(perms.audit & ~AA_OTHER_EXEC_TYPE);
|
||||
perms.exact |= AA_OTHER_EXEC_TYPE;
|
||||
}
|
||||
if (perms & AA_USER_EXEC & deny)
|
||||
perms &= ~AA_USER_EXEC_TYPE;
|
||||
if (AA_USER_EXEC & perms.deny)
|
||||
perms.deny |= AA_USER_EXEC_TYPE;
|
||||
|
||||
if (perms & AA_OTHER_EXEC & deny)
|
||||
perms &= ~AA_OTHER_EXEC_TYPE;
|
||||
if (AA_OTHER_EXEC & perms.deny)
|
||||
perms.deny |= AA_OTHER_EXEC_TYPE;
|
||||
|
||||
perms &= ~deny;
|
||||
perms.allow &= ~perms.deny;
|
||||
perms.quiet &= perms.deny;
|
||||
|
||||
if (audit_ctl)
|
||||
*audit_ctl = PACK_AUDIT_CTL(audit, quiet & deny);
|
||||
|
||||
// if (perms & AA_ERROR_BIT) {
|
||||
// fprintf(stderr, "error bit 0x%x\n", perms);
|
||||
// exit(255);
|
||||
//}
|
||||
|
||||
//if (perms & AA_EXEC_BITS)
|
||||
//fprintf(stderr, "accept perm: 0x%x\n", perms);
|
||||
/*
|
||||
if (perms & ~AA_VALID_PERMS)
|
||||
yyerror(_("Internal error accumulated invalid perm 0x%llx\n"), perms);
|
||||
*/
|
||||
|
||||
//if (perms & AA_CHANGE_HAT)
|
||||
// fprintf(stderr, "change_hat 0x%x\n", perms);
|
||||
|
||||
if (*error)
|
||||
if (error)
|
||||
fprintf(stderr, "profile has merged rule with conflicting x modifiers\n");
|
||||
|
||||
return perms;
|
||||
return error;
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* (C) 2006, 2007 Andreas Gruenbacher <agruen@suse.de>
|
||||
* Copyright (c) 2003-2008 Novell, Inc. (All rights reserved)
|
||||
* Copyright 2009-2010 Canonical Ltd.
|
||||
* Copyright 2009-2012 Canonical Ltd.
|
||||
*
|
||||
* The libapparmor library is licensed under the terms of the GNU
|
||||
* Lesser General Public License, version 2.1. Please see the file
|
||||
@@ -33,28 +33,298 @@
|
||||
#include "expr-tree.h"
|
||||
|
||||
class State;
|
||||
/**
|
||||
* State cases are identical to NodesCases except they map to State *
|
||||
* instead of NodeSet.
|
||||
* Out-edges from a state to another: we store the follow State
|
||||
* for each input character that is not a default match in cases and
|
||||
* default matches in otherwise as well as in all matching explicit cases
|
||||
* This avoids enumerating all the explicit tranitions for default matches.
|
||||
*/
|
||||
typedef struct Cases {
|
||||
typedef map<uchar, State *>::iterator iterator;
|
||||
iterator begin() { return cases.begin(); }
|
||||
iterator end() { return cases.end(); }
|
||||
|
||||
Cases(): otherwise(0) { }
|
||||
|
||||
map<uchar, State *> cases;
|
||||
State *otherwise;
|
||||
} Cases;
|
||||
|
||||
typedef map<uchar, State *> StateTrans;
|
||||
typedef list<State *> Partition;
|
||||
|
||||
uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
|
||||
#include "../immunix.h"
|
||||
|
||||
class perms_t {
|
||||
public:
|
||||
perms_t(void) throw(int): allow(0), deny(0), audit(0), quiet(0), exact(0) { };
|
||||
|
||||
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 & ALL_USER_EXEC,
|
||||
rhs.allow & ALL_USER_EXEC)) {
|
||||
if ((exact & AA_USER_EXEC_TYPE) &&
|
||||
!(rhs.exact & AA_USER_EXEC_TYPE)) {
|
||||
/* do nothing */
|
||||
} else if ((rhs.exact & AA_USER_EXEC_TYPE) &&
|
||||
!(exact & AA_USER_EXEC_TYPE)) {
|
||||
allow = (allow & ~AA_USER_EXEC_TYPE) |
|
||||
(rhs.allow & AA_USER_EXEC_TYPE);
|
||||
} else
|
||||
throw 1;
|
||||
} else
|
||||
allow |= rhs.allow & AA_USER_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 */
|
||||
} else if ((rhs.exact & AA_OTHER_EXEC_TYPE) &&
|
||||
!(exact & AA_OTHER_EXEC_TYPE)) {
|
||||
allow = (allow & ~AA_OTHER_EXEC_TYPE) |
|
||||
(rhs.allow & AA_OTHER_EXEC_TYPE);
|
||||
} else
|
||||
throw 1;
|
||||
} else
|
||||
allow |= rhs.allow & AA_OTHER_EXEC_TYPE;
|
||||
|
||||
|
||||
allow = (allow | (rhs.allow & ~ALL_AA_EXEC_TYPE));
|
||||
audit |= rhs.audit;
|
||||
quiet = (quiet | rhs.quiet);
|
||||
|
||||
/*
|
||||
if (exec & AA_USER_EXEC_TYPE &&
|
||||
(exec & AA_USER_EXEC_TYPE) != (allow & AA_USER_EXEC_TYPE))
|
||||
throw 1;
|
||||
if (exec & AA_OTHER_EXEC_TYPE &&
|
||||
(exec & AA_OTHER_EXEC_TYPE) != (allow & AA_OTHER_EXEC_TYPE))
|
||||
throw 1;
|
||||
*/
|
||||
}
|
||||
|
||||
int apply_and_clear_deny(void)
|
||||
{
|
||||
if (deny) {
|
||||
allow &= ~deny;
|
||||
quiet &= deny;
|
||||
deny = 0;
|
||||
return !is_accept();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool operator<(perms_t const &rhs)const
|
||||
{
|
||||
if (allow < rhs.allow)
|
||||
return allow < rhs.allow;
|
||||
if (deny < rhs.deny)
|
||||
return deny < rhs.deny;
|
||||
if (audit < rhs.audit)
|
||||
return audit < rhs.audit;
|
||||
return quiet < rhs.quiet;
|
||||
}
|
||||
|
||||
uint32_t allow, deny, audit, quiet, exact;
|
||||
};
|
||||
|
||||
int accept_perms(NodeSet *state, perms_t &perms);
|
||||
|
||||
/*
|
||||
* hashedNodes - for efficient set comparison
|
||||
*/
|
||||
class hashedNodeSet {
|
||||
public:
|
||||
unsigned long hash;
|
||||
NodeSet *nodes;
|
||||
|
||||
hashedNodeSet(NodeSet *n): nodes(n)
|
||||
{
|
||||
hash = hash_NodeSet(n);
|
||||
}
|
||||
|
||||
bool operator<(hashedNodeSet const &rhs)const
|
||||
{
|
||||
if (hash == rhs.hash) {
|
||||
if (nodes->size() == rhs.nodes->size())
|
||||
return *nodes < *(rhs.nodes);
|
||||
else
|
||||
return nodes->size() < rhs.nodes->size();
|
||||
} else {
|
||||
return hash < rhs.hash;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class hashedNodeVec {
|
||||
public:
|
||||
typedef ImportantNode ** iterator;
|
||||
iterator begin() { return nodes; }
|
||||
iterator end() { iterator t = nodes ? &nodes[len] : NULL; return t; }
|
||||
|
||||
unsigned long hash;
|
||||
unsigned long len;
|
||||
ImportantNode **nodes;
|
||||
|
||||
hashedNodeVec(NodeSet *n)
|
||||
{
|
||||
hash = hash_NodeSet(n);
|
||||
len = n->size();
|
||||
nodes = new ImportantNode *[n->size()];
|
||||
|
||||
unsigned int j = 0;
|
||||
for (NodeSet::iterator i = n->begin(); i != n->end(); i++, j++) {
|
||||
nodes[j] = *i;
|
||||
}
|
||||
}
|
||||
|
||||
hashedNodeVec(NodeSet *n, unsigned long h): hash(h)
|
||||
{
|
||||
len = n->size();
|
||||
nodes = new ImportantNode *[n->size()];
|
||||
ImportantNode **j = nodes;
|
||||
for (NodeSet::iterator i = n->begin(); i != n->end(); i++) {
|
||||
*(j++) = *i;
|
||||
}
|
||||
}
|
||||
|
||||
~hashedNodeVec()
|
||||
{
|
||||
delete nodes;
|
||||
}
|
||||
|
||||
unsigned long size()const { return len; }
|
||||
|
||||
bool operator<(hashedNodeVec const &rhs)const
|
||||
{
|
||||
if (hash == rhs.hash) {
|
||||
if (len == rhs.size()) {
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
if (nodes[i] != rhs.nodes[i])
|
||||
return nodes[i] < rhs.nodes[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return len < rhs.size();
|
||||
}
|
||||
return hash < rhs.hash;
|
||||
}
|
||||
};
|
||||
|
||||
class CacheStats {
|
||||
public:
|
||||
unsigned long dup, sum, max;
|
||||
|
||||
CacheStats(void): dup(0), sum(0), max(0) { };
|
||||
|
||||
void clear(void) { dup = sum = max = 0; }
|
||||
virtual unsigned long size(void) const = 0;
|
||||
};
|
||||
|
||||
class NodeCache: public CacheStats {
|
||||
public:
|
||||
set<hashedNodeSet> cache;
|
||||
|
||||
NodeCache(void): cache() { };
|
||||
~NodeCache() { clear(); };
|
||||
|
||||
virtual unsigned long size(void) const { return cache.size(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (set<hashedNodeSet>::iterator i = cache.begin();
|
||||
i != cache.end(); i++) {
|
||||
delete i->nodes;
|
||||
}
|
||||
cache.clear();
|
||||
CacheStats::clear();
|
||||
}
|
||||
|
||||
NodeSet *insert(NodeSet *nodes)
|
||||
{
|
||||
if (!nodes)
|
||||
return NULL;
|
||||
pair<set<hashedNodeSet>::iterator,bool> uniq;
|
||||
uniq = cache.insert(hashedNodeSet(nodes));
|
||||
if (uniq.second == false) {
|
||||
delete(nodes);
|
||||
dup++;
|
||||
} else {
|
||||
sum += nodes->size();
|
||||
if (nodes->size() > max)
|
||||
max = nodes->size();
|
||||
}
|
||||
return uniq.first->nodes;
|
||||
}
|
||||
};
|
||||
|
||||
struct deref_less_than {
|
||||
bool operator()(hashedNodeVec * const &lhs, hashedNodeVec * const &rhs)const
|
||||
{
|
||||
return *lhs < *rhs;
|
||||
}
|
||||
};
|
||||
|
||||
class NodeVecCache: public CacheStats {
|
||||
public:
|
||||
set<hashedNodeVec *, deref_less_than> cache;
|
||||
|
||||
NodeVecCache(void): cache() { };
|
||||
~NodeVecCache() { clear(); };
|
||||
|
||||
virtual unsigned long size(void) const { return cache.size(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (set<hashedNodeVec *>::iterator i = cache.begin();
|
||||
i != cache.end(); i++) {
|
||||
delete *i;
|
||||
}
|
||||
cache.clear();
|
||||
CacheStats::clear();
|
||||
}
|
||||
|
||||
hashedNodeVec *insert(NodeSet *nodes)
|
||||
{
|
||||
if (!nodes)
|
||||
return NULL;
|
||||
pair<set<hashedNodeVec *>::iterator,bool> uniq;
|
||||
hashedNodeVec *nv = new hashedNodeVec(nodes);
|
||||
uniq = cache.insert(nv);
|
||||
if (uniq.second == false) {
|
||||
delete nv;
|
||||
dup++;
|
||||
} else {
|
||||
sum += nodes->size();
|
||||
if (nodes->size() > max)
|
||||
max = nodes->size();
|
||||
}
|
||||
delete(nodes);
|
||||
return (*uniq.first);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ProtoState - NodeSet and ancillery information used to create a state
|
||||
*/
|
||||
class ProtoState {
|
||||
public:
|
||||
hashedNodeVec *nnodes;
|
||||
NodeSet *anodes;
|
||||
|
||||
ProtoState(hashedNodeVec *n, NodeSet *a = NULL): nnodes(n), anodes(a) { };
|
||||
bool operator<(ProtoState const &rhs)const
|
||||
{
|
||||
if (nnodes == rhs.nnodes)
|
||||
return anodes < rhs.anodes;
|
||||
return nnodes < rhs.nnodes;
|
||||
}
|
||||
|
||||
unsigned long size(void)
|
||||
{
|
||||
if (anodes)
|
||||
return nnodes->size() + anodes->size();
|
||||
return nnodes->size();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* State - DFA individual state information
|
||||
@@ -63,7 +333,8 @@ uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
|
||||
* the start state is setup to have label == 1
|
||||
* audit: the audit permission mask for the state
|
||||
* accept: the accept permissions for the state
|
||||
* cases: set of transitions from this state
|
||||
* trans: set of transitions from this state
|
||||
* otherwise: the default state for transitions not in @trans
|
||||
* parition: Is a temporary work variable used during dfa minimization.
|
||||
* it can be replaced with a map, but that is slower and uses more
|
||||
* memory.
|
||||
@@ -72,62 +343,110 @@ uint32_t accept_perms(NodeSet *state, uint32_t *audit_ctl, int *error);
|
||||
*/
|
||||
class State {
|
||||
public:
|
||||
State(): label(0), audit(0), accept(0), cases(), nodes(NULL) { };
|
||||
State(int l): label(l), audit(0), accept(0), cases(), nodes(NULL) { };
|
||||
State(int l, NodeSet * n) throw(int):
|
||||
label(l), audit(0), accept(0), cases(), nodes(n)
|
||||
State(int l, ProtoState &n, State *other) throw(int):
|
||||
label(l), perms(), trans()
|
||||
{
|
||||
int error;
|
||||
|
||||
if (other)
|
||||
otherwise = other;
|
||||
else
|
||||
otherwise = this;
|
||||
|
||||
proto = n;
|
||||
|
||||
/* Compute permissions associated with the State. */
|
||||
accept = accept_perms(nodes, &audit, &error);
|
||||
error = accept_perms(n.anodes, perms);
|
||||
if (error) {
|
||||
//cerr << "Failing on accept perms " << error << "\n";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
State *next(uchar c) {
|
||||
StateTrans::iterator i = trans.find(c);
|
||||
if (i != trans.end())
|
||||
return i->second;
|
||||
return otherwise;
|
||||
};
|
||||
|
||||
int apply_and_clear_deny(void) { return perms.apply_and_clear_deny(); }
|
||||
|
||||
int label;
|
||||
uint32_t audit, accept;
|
||||
Cases cases;
|
||||
perms_t perms;
|
||||
StateTrans trans;
|
||||
State *otherwise;
|
||||
|
||||
/* temp storage for State construction */
|
||||
union {
|
||||
Partition *partition;
|
||||
NodeSet *nodes;
|
||||
ProtoState proto;
|
||||
};
|
||||
};
|
||||
|
||||
ostream &operator<<(ostream &os, const State &state);
|
||||
|
||||
typedef map<pair<unsigned long, NodeSet *>, State *, deref_less_than> NodeMap;
|
||||
/* Transitions in the DFA. */
|
||||
class NodeMap: public CacheStats
|
||||
{
|
||||
public:
|
||||
typedef map<ProtoState, State *>::iterator iterator;
|
||||
iterator begin() { return cache.begin(); }
|
||||
iterator end() { return cache.end(); }
|
||||
|
||||
/* dfa_stats - structure to group various stats about dfa creation
|
||||
* duplicates - how many duplicate NodeSets where encountered and discarded
|
||||
* proto_max - maximum length of a NodeSet encountered during dfa construction
|
||||
* proto_sum - sum of NodeSet length during dfa construction. Used to find
|
||||
* average length.
|
||||
*/
|
||||
typedef struct dfa_stats {
|
||||
unsigned int duplicates, proto_max, proto_sum;
|
||||
} dfa_stats_t;
|
||||
map<ProtoState, State *> cache;
|
||||
|
||||
NodeMap(void): cache() { };
|
||||
~NodeMap() { clear(); };
|
||||
|
||||
virtual unsigned long size(void) const { return cache.size(); }
|
||||
|
||||
void clear()
|
||||
{
|
||||
cache.clear();
|
||||
CacheStats::clear();
|
||||
}
|
||||
|
||||
pair<iterator,bool> insert(ProtoState &proto, State *state)
|
||||
{
|
||||
pair<iterator,bool> uniq;
|
||||
uniq = cache.insert(make_pair(proto, state));
|
||||
if (uniq.second == false) {
|
||||
dup++;
|
||||
} else {
|
||||
sum += proto.size();
|
||||
if (proto.size() > max)
|
||||
max = proto.size();
|
||||
}
|
||||
return uniq;
|
||||
}
|
||||
};
|
||||
|
||||
/* Transitions in the DFA. */
|
||||
|
||||
class DFA {
|
||||
void dump_node_to_dfa(void);
|
||||
State *add_new_state(NodeMap &nodemap,
|
||||
pair<unsigned long, NodeSet *> index,
|
||||
NodeSet *nodes, dfa_stats_t &stats);
|
||||
void update_state_transitions(NodeMap &nodemap,
|
||||
list<State *> &work_queue,
|
||||
State *state, dfa_stats_t &stats);
|
||||
State *find_target_state(NodeMap &nodemap, list<State *> &work_queue,
|
||||
NodeSet *nodes, dfa_stats_t &stats);
|
||||
State *add_new_state(NodeSet *nodes, State *other);
|
||||
void update_state_transitions(State *state);
|
||||
|
||||
/* temporary values used during computations */
|
||||
NodeCache anodes_cache;
|
||||
NodeVecCache nnodes_cache;
|
||||
NodeMap node_map;
|
||||
list<State *> work_queue;
|
||||
|
||||
public:
|
||||
DFA(Node *root, dfaflags_t flags);
|
||||
virtual ~DFA();
|
||||
|
||||
State *match_len(State *state, const char *str, size_t len);
|
||||
State *match_until(State *state, const char *str, const char term);
|
||||
State *match(const char *str);
|
||||
|
||||
void remove_unreachable(dfaflags_t flags);
|
||||
bool same_mappings(State *s1, State *s2);
|
||||
size_t hash_trans(State *s);
|
||||
void minimize(dfaflags_t flags);
|
||||
int apply_and_clear_deny(void);
|
||||
void dump(ostream &os);
|
||||
void dump_dot_graph(ostream &os);
|
||||
void dump_uniq_perms(const char *s);
|
||||
|
@@ -169,7 +169,7 @@ int hexdigit(char c)
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return 10 + c - 'A';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return 10 + c - 'A';
|
||||
return 10 + c - 'a';
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
526
parser/mount.c
Normal file
526
parser/mount.c
Normal file
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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. or Canonical
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The mount command, its mix of options and flags, its permissions and
|
||||
* mapping are a mess.
|
||||
* mount [-lhV]
|
||||
*
|
||||
* mount -a [-fFnrsvw] [-t vfstype] [-O optlist]
|
||||
*
|
||||
* mount [-fnrsvw] [-o option[,option]...] device|dir
|
||||
*
|
||||
* mount [-fnrsvw] [-t vfstype] [-o options] device dir
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* Mount flags of no interest for apparmor mediation
|
||||
* -a, --all
|
||||
* -F fork for simultaneous mount
|
||||
* -f fake, do everything except that actual system call
|
||||
* -h --help
|
||||
* -i, --internal-only
|
||||
* -n mount without writing in /etc/mtab
|
||||
* -O <optlist> limits what is auto mounted
|
||||
* -p, --pass-fd num
|
||||
* -s Tolerate sloppy mount options
|
||||
* -U uuid
|
||||
* -V --version
|
||||
* --no-canonicalize
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* what do we do with these
|
||||
* -l list?
|
||||
* -L <label> label
|
||||
* -v --verbose deprecated
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* Filesystem type
|
||||
* -t <vfstype>
|
||||
* vfstype=<vfstype>
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* Mount Flags/options (-o --options)
|
||||
* -o option[,option]
|
||||
*
|
||||
* The Linux kernel has 32 fs - independent mount flags, that mount command
|
||||
* is responsible for stripping out and mapping to a 32 bit flags field.
|
||||
* The mount commands mapping is documented below.
|
||||
*
|
||||
* Unfortunately we can not directly use this mapping as we need to be able
|
||||
* represent, whether none, 1 or both options of a flag can be present for
|
||||
* example
|
||||
* ro, and rw information is stored in a single bit. But we need 2 bits
|
||||
* of information.
|
||||
* ro - the mount can only be readonly
|
||||
* rw - the mount can only be rw
|
||||
* ro/rw - the mount can be either ro/rw
|
||||
* the fourth state of neither ro/rw does not exist, but still we need
|
||||
* >1 bit to represent the possible choices
|
||||
*
|
||||
* The fs specific mount options are passed into the kernel as a string
|
||||
* to be interpreted by the filesystem.
|
||||
*
|
||||
*
|
||||
* #define MS_RDONLY 1 Mount read-only
|
||||
* ro -r --read-only [source] dest
|
||||
* rw -w
|
||||
* #define MS_NOSUID 2 Ignore suid and sgid bits
|
||||
* nosuid
|
||||
* suid
|
||||
* #define MS_NODEV 4 Disallow access to device special files
|
||||
* nodev
|
||||
* dev
|
||||
* #define MS_NOEXEC 8 Disallow program execution
|
||||
* noexec
|
||||
* exec
|
||||
* #define MS_SYNCHRONOUS 16 Writes are synced at once
|
||||
* sync
|
||||
* async
|
||||
* #define MS_REMOUNT 32 Alter flags of a mounted FS
|
||||
* remount source dest
|
||||
* #define MS_MANDLOCK 64 Allow mandatory locks on an FS
|
||||
* mand
|
||||
* nomand
|
||||
* #define MS_DIRSYNC 128 Directory modifications are synchronous
|
||||
* dirsync
|
||||
* #define MS_NOATIME 1024 Do not update access times
|
||||
* noatime
|
||||
* atime
|
||||
* #define MS_NODIRATIME 2048 Do not update directory access times
|
||||
* nodiratime
|
||||
* diratime
|
||||
* #define MS_BIND 4096
|
||||
* --bind -B source dest
|
||||
* #define MS_MOVE 8192
|
||||
* --move -M source dest
|
||||
* #define MS_REC 16384
|
||||
* --rbind -R source dest
|
||||
* --make-rshared dest
|
||||
* --make-rslave dest
|
||||
* --make-rprivate dest
|
||||
* --make-runbindable dest
|
||||
* #define MS_VERBOSE 32768 MS_VERBOSE is deprecated
|
||||
* #define MS_SILENT 32768
|
||||
* silent
|
||||
* load
|
||||
* #define MS_POSIXACL (1<<16) VFS does not apply the umask
|
||||
* acl
|
||||
* noacl
|
||||
* #define MS_UNBINDABLE (1<<17) change to unbindable
|
||||
* --make-unbindable dest
|
||||
* #define MS_PRIVATE (1<<18) change to private
|
||||
* --make-private dest
|
||||
* #define MS_SLAVE (1<<19) change to slave
|
||||
* --make-slave dest
|
||||
* #define MS_SHARED (1<<20) change to shared
|
||||
* --make-shared dest
|
||||
* #define MS_RELATIME (1<<21) Update atime relative to mtime/ctime
|
||||
* relatime
|
||||
* norelatime
|
||||
* #define MS_KERNMOUNT (1<<22) this is a kern_mount call
|
||||
* #define MS_I_VERSION (1<<23) Update inode I_version field
|
||||
* iversion
|
||||
* noiversion
|
||||
* #define MS_STRICTATIME (1<<24) Always perform atime updates
|
||||
* strictatime
|
||||
* nostrictatime
|
||||
* #define MS_NOSEC (1<<28)
|
||||
* #define MS_BORN (1<<29)
|
||||
* #define MS_ACTIVE (1<<30)
|
||||
* #define MS_NOUSER (1<<31)
|
||||
* nouser
|
||||
* user
|
||||
*
|
||||
* other mount options of interest
|
||||
*
|
||||
* selinux
|
||||
* context=<context>
|
||||
* fscontext=<context>
|
||||
* defcontext=<context>,
|
||||
* rootcontext=<context>
|
||||
*
|
||||
* defaults -> rw, suid, dev, exec, auto, nouser, async
|
||||
* owner -> implies nosuid and nodev
|
||||
* users -> implies noexec, nosuid, and nodev
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* AppArmor mount rules
|
||||
*
|
||||
* AppArmor mount rules try to leverage mount syntax within apparmor syntax
|
||||
* this can not be done entirely but it is largely covered.
|
||||
*
|
||||
* The general mount syntax is
|
||||
* [audit] [deny] [owner] mount [conds]* [source] [ -> [conds] path],
|
||||
* [audit] [deny] remount [conds]* [path],
|
||||
* [audit] [deny] umount [conds]* [path],
|
||||
*
|
||||
* Note: leading owner option applies owner condition to both sours and dest
|
||||
* path.
|
||||
*
|
||||
* where [conds] can be
|
||||
* fstype=<expr>
|
||||
* options=<expr>
|
||||
* owner[=<expr>]
|
||||
*
|
||||
* <expr> := <re> | '(' (<re>[,])+ ')'
|
||||
*
|
||||
* If a condition is not specified then it is assumed to match all possible
|
||||
* entries for it. ie. a missing fstype means all fstypes are matched.
|
||||
* However if a condition is specified then the rule only grants permission
|
||||
* for mounts matching the specified pattern.
|
||||
*
|
||||
* Examples.
|
||||
* mount, # allow any mount
|
||||
* mount /dev/foo, # allow mounting of /dev/foo anywhere
|
||||
* 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 fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) overlay -> /mnt/
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* pivotroot
|
||||
* pivotroot [oldroot=<value>] <path> -> <profile>
|
||||
* pivotroot <path> -> { }
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* chroot
|
||||
* chroot <path> -> <profile>
|
||||
* chroot <path> -> { }
|
||||
*
|
||||
*----------------------------------------------------------------------
|
||||
* AppArmor mount rule encoding
|
||||
*
|
||||
* TODO:
|
||||
* add semantic checking of options against specified filesytem types
|
||||
* to catch mount options that can't be covered.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "parser.h"
|
||||
#include "mount.h"
|
||||
|
||||
struct mnt_keyword_table {
|
||||
char *keyword;
|
||||
unsigned int set;
|
||||
unsigned int clear;
|
||||
};
|
||||
|
||||
static struct mnt_keyword_table mnt_opts_table[] = {
|
||||
{"ro", MS_RDONLY, 0},
|
||||
{"r", MS_RDONLY, 0},
|
||||
{"read-only", MS_RDONLY, 0},
|
||||
{"rw", 0, MS_RDONLY},
|
||||
{"w", 0, MS_RDONLY},
|
||||
{"suid", 0, MS_NOSUID},
|
||||
{"nosuid", MS_NOSUID, 0},
|
||||
{"dev", 0, MS_NODEV},
|
||||
{"nodev", MS_NODEV, 0},
|
||||
{"exec", 0, MS_NOEXEC},
|
||||
{"noexec", MS_NOEXEC, 0},
|
||||
{"sync", MS_SYNC, 0},
|
||||
{"async", 0, MS_SYNC},
|
||||
{"remount", MS_REMOUNT, 0},
|
||||
{"mand", MS_MAND, 0},
|
||||
{"nomand", 0, MS_MAND},
|
||||
{"dirsync", MS_DIRSYNC, 0},
|
||||
{"atime", 0, MS_NOATIME},
|
||||
{"noatime", MS_NOATIME, 0},
|
||||
{"diratime", 0, MS_NODIRATIME},
|
||||
{"nodiratime", MS_NODIRATIME, 0},
|
||||
{"bind", MS_BIND, 0},
|
||||
{"B", MS_BIND, 0},
|
||||
{"move", MS_MOVE, 0},
|
||||
{"M", MS_MOVE, 0},
|
||||
{"rbind", MS_RBIND, 0},
|
||||
{"R", MS_RBIND, 0},
|
||||
{"verbose", MS_VERBOSE, 0},
|
||||
{"silent", MS_SILENT, 0},
|
||||
{"load", 0, MS_SILENT},
|
||||
{"acl", MS_ACL, 0},
|
||||
{"noacl", 0, MS_ACL},
|
||||
{"make-unbindable", MS_UNBINDABLE, 0},
|
||||
{"make-runbindable", MS_RUNBINDABLE, 0},
|
||||
{"make-private", MS_PRIVATE, 0},
|
||||
{"make-rprivate", MS_RPRIVATE, 0},
|
||||
{"make-slave", MS_SLAVE, 0},
|
||||
{"make-rslave", MS_RSLAVE, 0},
|
||||
{"make-shared", MS_SHARED, 0},
|
||||
{"make-rshared", MS_RSHARED, 0},
|
||||
|
||||
{"relatime", MS_RELATIME, 0},
|
||||
{"norelatime", 0, MS_NORELATIME},
|
||||
{"iversion", MS_IVERSION, 0},
|
||||
{"noiversion", 0, MS_IVERSION},
|
||||
{"strictatime", MS_STRICTATIME, 0},
|
||||
{"user", 0, MS_NOUSER},
|
||||
{"nouser", MS_NOUSER, 0},
|
||||
|
||||
{NULL, 0, 0}
|
||||
};
|
||||
|
||||
static struct mnt_keyword_table mnt_conds_table[] = {
|
||||
{"options", MNT_SRC_OPT, MNT_COND_OPTIONS},
|
||||
{"option", MNT_SRC_OPT, MNT_COND_OPTIONS},
|
||||
{"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)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; table[i].keyword; i++) {
|
||||
if (strcmp(name, table[i].keyword) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int is_valid_mnt_cond(const char *name, int src)
|
||||
{
|
||||
int i;
|
||||
i = find_mnt_keyword(mnt_conds_table, name);
|
||||
if (i != -1)
|
||||
return (mnt_conds_table[i].set & src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static unsigned int extract_flags(struct value_list **list, unsigned int *inv)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
*inv = 0;
|
||||
|
||||
struct value_list *entry, *tmp, *prev = NULL;
|
||||
list_for_each_safe(*list, entry, tmp) {
|
||||
int i;
|
||||
i = find_mnt_keyword(mnt_opts_table, entry->value);
|
||||
if (i != -1) {
|
||||
flags |= mnt_opts_table[i].set;
|
||||
*inv |= mnt_opts_table[i].clear;
|
||||
PDEBUG(" extracting mount flag %s req: 0x%x inv: 0x%x"
|
||||
" => req: 0x%x inv: 0x%x\n",
|
||||
entry->value, mnt_opts_table[i].set,
|
||||
mnt_opts_table[i].clear, flags, *inv);
|
||||
if (prev)
|
||||
prev->next = tmp;
|
||||
if (entry == *list)
|
||||
*list = tmp;
|
||||
entry->next = NULL;
|
||||
free_value_list(entry);
|
||||
} else
|
||||
prev = entry;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static struct value_list *extract_fstype(struct cond_entry **conds)
|
||||
{
|
||||
struct value_list *list = NULL;
|
||||
|
||||
struct cond_entry *entry, *tmp, *prev = NULL;
|
||||
|
||||
list_for_each_safe(*conds, entry, tmp) {
|
||||
if (strcmp(entry->name, "fstype") == 0 ||
|
||||
strcmp(entry->name, "vfstype") == 0) {
|
||||
PDEBUG(" extracting fstype\n");
|
||||
if (prev)
|
||||
prev->next = tmp;
|
||||
if (entry == *conds)
|
||||
*conds = tmp;
|
||||
entry->next = NULL;
|
||||
list_append(entry->vals, list);
|
||||
list = entry->vals;
|
||||
entry->vals = NULL;
|
||||
free_cond_entry(entry);
|
||||
} else
|
||||
prev = entry;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static struct value_list *extract_options(struct cond_entry **conds, int eq)
|
||||
{
|
||||
struct value_list *list = NULL;
|
||||
|
||||
struct cond_entry *entry, *tmp, *prev = NULL;
|
||||
|
||||
list_for_each_safe(*conds, entry, tmp) {
|
||||
if ((strcmp(entry->name, "options") == 0 ||
|
||||
strcmp(entry->name, "option") == 0) &&
|
||||
entry->eq == eq) {
|
||||
if (prev)
|
||||
prev->next = tmp;
|
||||
if (entry == *conds)
|
||||
*conds = tmp;
|
||||
entry->next = NULL;
|
||||
PDEBUG(" extracting option %s\n", entry->name);
|
||||
list_append(entry->vals, list);
|
||||
list = entry->vals;
|
||||
entry->vals = NULL;
|
||||
free_cond_entry(entry);
|
||||
} else
|
||||
prev = entry;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
struct mnt_entry *new_mnt_entry(struct cond_entry *src_conds, char *device,
|
||||
struct cond_entry *dst_conds __unused, char *mnt_point,
|
||||
int allow)
|
||||
{
|
||||
/* FIXME: dst_conds are ignored atm */
|
||||
|
||||
struct mnt_entry *ent;
|
||||
ent = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
|
||||
if (ent) {
|
||||
ent->mnt_point = mnt_point;
|
||||
ent->device = device;
|
||||
ent->dev_type = extract_fstype(&src_conds);
|
||||
|
||||
ent->flags = 0;
|
||||
ent->inv_flags = 0;
|
||||
|
||||
if (src_conds) {
|
||||
unsigned int flags = 0, inv_flags = 0;
|
||||
struct value_list *list = extract_options(&src_conds, 0);
|
||||
|
||||
ent->opts = extract_options(&src_conds, 1);
|
||||
if (ent->opts)
|
||||
ent->flags = extract_flags(&ent->opts,
|
||||
&ent->inv_flags);
|
||||
|
||||
if (list) {
|
||||
flags = extract_flags(&list, &inv_flags);
|
||||
/* these flags are optional so set both */
|
||||
flags |= inv_flags;
|
||||
inv_flags |= flags;
|
||||
|
||||
ent->flags |= flags;
|
||||
ent->inv_flags |= inv_flags;
|
||||
|
||||
if (ent->opts)
|
||||
list_append(ent->opts, list);
|
||||
else if (list)
|
||||
ent->opts = list;
|
||||
}
|
||||
}
|
||||
|
||||
if (allow & AA_DUMMY_REMOUNT) {
|
||||
allow = AA_MAY_MOUNT;
|
||||
ent->flags |= MS_REMOUNT;
|
||||
ent->inv_flags = 0;
|
||||
} else if (!(ent->flags | ent->inv_flags)) {
|
||||
/* no flag options, and not remount, allow everything */
|
||||
ent->flags = MS_ALL_FLAGS;
|
||||
ent->inv_flags = MS_ALL_FLAGS;
|
||||
}
|
||||
|
||||
ent->allow = allow;
|
||||
|
||||
if (src_conds) {
|
||||
PERROR(" unsupported mount conditions\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
void free_mnt_entry(struct mnt_entry *ent)
|
||||
{
|
||||
if (!ent)
|
||||
return;
|
||||
|
||||
free_mnt_entry(ent->next);
|
||||
free_value_list(ent->opts);
|
||||
free_value_list(ent->dev_type);
|
||||
free(ent->device);
|
||||
free(ent->mnt_point);
|
||||
free(ent->trans);
|
||||
|
||||
free(ent);
|
||||
}
|
||||
|
||||
|
||||
struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig)
|
||||
{
|
||||
struct mnt_entry *entry = NULL;
|
||||
|
||||
entry = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry));
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
entry->mnt_point = orig->mnt_point ? strdup(orig->mnt_point) : NULL;
|
||||
entry->device = orig->device ? strdup(orig->device) : NULL;
|
||||
entry->trans = orig->trans ? strdup(orig->trans) : NULL;
|
||||
|
||||
entry->dev_type = dup_value_list(orig->dev_type);
|
||||
entry->opts = dup_value_list(orig->opts);
|
||||
|
||||
entry->flags = orig->flags;
|
||||
entry->inv_flags = orig->inv_flags;
|
||||
|
||||
entry->allow = orig->allow;
|
||||
entry->audit = orig->audit;
|
||||
entry->deny = orig->deny;
|
||||
|
||||
entry->next = orig->next;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void print_mnt_entry(struct mnt_entry *entry)
|
||||
{
|
||||
if (entry->allow & AA_MAY_MOUNT)
|
||||
fprintf(stderr, "mount");
|
||||
else if (entry->allow & AA_MAY_UMOUNT)
|
||||
fprintf(stderr, "umount");
|
||||
else if (entry->allow & AA_MAY_PIVOTROOT)
|
||||
fprintf(stderr, "pivotroot");
|
||||
else
|
||||
fprintf(stderr, "error: unknonwn mount perm");
|
||||
|
||||
fprintf(stderr, " (0x%x - 0x%x) ", entry->flags, entry->inv_flags);
|
||||
if (entry->dev_type) {
|
||||
fprintf(stderr, " type=");
|
||||
print_value_list(entry->dev_type);
|
||||
}
|
||||
if (entry->opts) {
|
||||
fprintf(stderr, " options=");
|
||||
print_value_list(entry->opts);
|
||||
}
|
||||
if (entry->device)
|
||||
fprintf(stderr, " %s", entry->device);
|
||||
if (entry->mnt_point)
|
||||
fprintf(stderr, " -> %s", entry->mnt_point);
|
||||
if (entry->trans)
|
||||
fprintf(stderr, " -> %s", entry->trans);
|
||||
|
||||
fprintf(stderr, " %s (0x%x/0x%x)", entry->deny ? "deny" : "", entry->allow, entry->audit);
|
||||
fprintf(stderr, ",\n");
|
||||
}
|
136
parser/mount.h
Normal file
136
parser/mount.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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. or Canonical
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AA_MOUNT_H
|
||||
#define __AA_MOUNT_H
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
#define MS_RDONLY (1 << 0)
|
||||
#define MS_RW 0
|
||||
#define MS_NOSUID (1 << 1)
|
||||
#define MS_SUID 0
|
||||
#define MS_NODEV (1 << 2)
|
||||
#define MS_DEV 0
|
||||
#define MS_NOEXEC (1 << 3)
|
||||
#define MS_EXEC 0
|
||||
#define MS_SYNC (1 << 4)
|
||||
#define MS_ASYNC 0
|
||||
#define MS_REMOUNT (1 << 5)
|
||||
#define MS_MAND (1 << 6)
|
||||
#define MS_NOMAND 0
|
||||
#define MS_DIRSYNC (1 << 7)
|
||||
#define MS_NODIRSYNC 0
|
||||
#define MS_NOATIME (1 << 10)
|
||||
#define MS_ATIME 0
|
||||
#define MS_NODIRATIME (1 << 11)
|
||||
#define MS_DIRATIME 0
|
||||
#define MS_BIND (1 << 12)
|
||||
#define MS_MOVE (1 << 13)
|
||||
#define MS_REC (1 << 14)
|
||||
#define MS_VERBOSE (1 << 15)
|
||||
#define MS_SILENT (1 << 15)
|
||||
#define MS_LOAD 0
|
||||
#define MS_ACL (1 << 16)
|
||||
#define MS_NOACL 0
|
||||
#define MS_UNBINDABLE (1 << 17)
|
||||
#define MS_PRIVATE (1 << 18)
|
||||
#define MS_SLAVE (1 << 19)
|
||||
#define MS_SHARED (1 << 20)
|
||||
#define MS_RELATIME (1 << 21)
|
||||
#define MS_NORELATIME 0
|
||||
#define MS_IVERSION (1 << 23)
|
||||
#define MS_NOIVERSION 0
|
||||
#define MS_STRICTATIME (1 << 24)
|
||||
#define MS_NOUSER (1 << 31)
|
||||
#define MS_USER 0
|
||||
|
||||
#define MS_ALL_FLAGS (MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC | \
|
||||
MS_SYNC | MS_REMOUNT | MS_MAND | MS_DIRSYNC | \
|
||||
MS_NOATIME | MS_NODIRATIME | MS_BIND | MS_MOVE | \
|
||||
MS_REC | MS_VERBOSE | MS_ACL | MS_UNBINDABLE | \
|
||||
MS_PRIVATE | MS_SLAVE | MS_SHARED | MS_RELATIME | \
|
||||
MS_IVERSION | MS_STRICTATIME | MS_USER)
|
||||
|
||||
#define MS_RBIND (MS_BIND | MS_REC)
|
||||
#define MS_RUNBINDABLE (MS_UNBINDABLE | MS_REC)
|
||||
#define MS_RPRIVATE (MS_PRIVATE | MS_REC)
|
||||
#define MS_RSLAVE (MS_SLAVE | MS_REC)
|
||||
#define MS_RSHARED (MS_SHARED | MS_REC)
|
||||
|
||||
/* set of flags we don't use but define (but not with the kernel values)
|
||||
* for MNT_FLAGS
|
||||
*/
|
||||
#define MS_ACTIVE 0
|
||||
#define MS_BORN 0
|
||||
#define MS_KERNMOUNT 0
|
||||
|
||||
/* from kernel fs/namespace.c - set of flags masked off */
|
||||
#define MNT_FLAGS (MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | \
|
||||
MS_BORN | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| \
|
||||
MS_KERNMOUNT | MS_STRICTATIME)
|
||||
|
||||
#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)))
|
||||
#define MS_MOVE_FLAGS (MS_MOVE)
|
||||
|
||||
#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
|
||||
|
||||
#define MNT_COND_FSTYPE 1
|
||||
#define MNT_COND_OPTIONS 2
|
||||
|
||||
#define AA_MAY_PIVOTROOT 1
|
||||
#define AA_MAY_MOUNT 2
|
||||
#define AA_MAY_UMOUNT 4
|
||||
#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 {
|
||||
char *mnt_point;
|
||||
char *device;
|
||||
char *trans;
|
||||
struct value_list *dev_type;
|
||||
struct value_list *opts;
|
||||
|
||||
unsigned int flags, inv_flags;
|
||||
|
||||
int allow, audit;
|
||||
int deny;
|
||||
struct mnt_entry *next;
|
||||
};
|
||||
|
||||
void print_mnt_entry(struct mnt_entry *entry);
|
||||
|
||||
int is_valid_mnt_cond(const char *name, int src);
|
||||
struct mnt_entry *new_mnt_entry(struct cond_entry *sconds, char *device,
|
||||
struct cond_entry *dconds, char *mnt_point,
|
||||
int mode);
|
||||
struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig);
|
||||
void free_mnt_entry(struct mnt_entry *ent);
|
||||
|
||||
|
||||
#endif /* __AA_MOUNT_H */
|
@@ -2,8 +2,8 @@
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
*
|
||||
* Copyright (c) 2010
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
* Copyright (c) 2010 - 2012
|
||||
* 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
|
||||
@@ -19,12 +19,22 @@
|
||||
* Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AA_PARSER_H
|
||||
#define __AA_PARSER_H
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <sys/resource.h>
|
||||
#include "immunix.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
|
||||
struct mnt_ent;
|
||||
|
||||
/* Global variable to pass token to lexer. Will be replaced by parameter
|
||||
* when lexer and parser are made reentrant
|
||||
*/
|
||||
extern int parser_token;
|
||||
|
||||
typedef enum pattern_t pattern_t;
|
||||
|
||||
struct flagval {
|
||||
@@ -44,6 +54,20 @@ struct cod_pattern {
|
||||
char *regex; // posix regex
|
||||
};
|
||||
|
||||
struct value_list {
|
||||
char *value;
|
||||
|
||||
struct value_list *next;
|
||||
};
|
||||
|
||||
struct cond_entry {
|
||||
char *name;
|
||||
int eq; /* where equals was used in specifying list */
|
||||
struct value_list *vals;
|
||||
|
||||
struct cond_entry *next;
|
||||
};
|
||||
|
||||
struct cod_entry {
|
||||
char *namespace;
|
||||
char *name;
|
||||
@@ -107,7 +131,6 @@ struct codomain {
|
||||
uint64_t audit_caps;
|
||||
uint64_t deny_caps;
|
||||
uint64_t quiet_caps;
|
||||
uint64_t set_caps;
|
||||
|
||||
unsigned int *network_allowed; /* array of type masks
|
||||
* indexed by AF_FAMILY */
|
||||
@@ -119,6 +142,8 @@ struct codomain {
|
||||
|
||||
char *exec_table[AA_EXEC_COUNT];
|
||||
struct cod_entry *entries;
|
||||
struct mnt_entry *mnt_ents;
|
||||
|
||||
void *hat_table;
|
||||
//struct codomain *next;
|
||||
|
||||
@@ -126,6 +151,11 @@ struct codomain {
|
||||
int dfarule_count;
|
||||
void *dfa;
|
||||
size_t dfa_size;
|
||||
|
||||
aare_ruleset_t *policy_rules;
|
||||
int policy_rule_count;
|
||||
void *policy_dfa;
|
||||
size_t policy_dfa_size;
|
||||
};
|
||||
|
||||
struct sd_hat {
|
||||
@@ -216,12 +246,19 @@ extern int preprocess_only;
|
||||
#define __unused __attribute__ ((unused))
|
||||
#endif
|
||||
|
||||
|
||||
#define list_for_each(LIST, ENTRY) \
|
||||
for ((ENTRY) = (LIST); (ENTRY); (ENTRY) = (ENTRY)->next)
|
||||
#define list_for_each_safe(LIST, ENTRY, TMP) \
|
||||
for ((ENTRY) = (LIST), (TMP) = (LIST) ? (LIST)->next : NULL; (ENTRY); (ENTRY) = (TMP), (TMP) = (TMP) ? (TMP)->next : NULL)
|
||||
#define list_last_entry(LIST, ENTRY) \
|
||||
for ((ENTRY) = (LIST); (ENTRY) && (ENTRY)->next; (ENTRY) = (ENTRY)->next)
|
||||
#define list_append(LISTA, LISTB) \
|
||||
do { \
|
||||
typeof(LISTA) ___tmp; \
|
||||
list_last_entry((LISTA), ___tmp);\
|
||||
___tmp->next = (LISTB); \
|
||||
} while (0)
|
||||
|
||||
/* from parser_common.c */
|
||||
extern int regex_type;
|
||||
@@ -229,6 +266,7 @@ extern int perms_create;
|
||||
extern int net_af_max_override;
|
||||
extern int kernel_load;
|
||||
extern int kernel_supports_network;
|
||||
extern int kernel_supports_mount;
|
||||
extern int flag_changehat_version;
|
||||
extern int conf_verbose;
|
||||
extern int conf_quiet;
|
||||
@@ -254,7 +292,7 @@ extern void update_mru_tstamp(FILE *file);
|
||||
extern FILE *yyin;
|
||||
extern void yyrestart(FILE *fp);
|
||||
extern int yyparse(void);
|
||||
extern void yyerror(char *msg, ...);
|
||||
extern void yyerror(const char *msg, ...);
|
||||
extern int yylex(void);
|
||||
|
||||
/* parser_include.c */
|
||||
@@ -265,12 +303,24 @@ extern int process_regex(struct codomain *cod);
|
||||
extern int post_process_entry(struct cod_entry *entry);
|
||||
extern void reset_regex(void);
|
||||
|
||||
extern int process_policydb(struct codomain *cod);
|
||||
|
||||
extern int process_policy_ents(struct codomain *cod);
|
||||
|
||||
/* parser_variable.c */
|
||||
extern int process_variables(struct codomain *cod);
|
||||
extern struct var_string *split_out_var(char *string);
|
||||
extern void free_var_string(struct var_string *var);
|
||||
|
||||
/* parser_misc.c */
|
||||
extern struct value_list *new_value_list(char *value);
|
||||
extern struct value_list *dup_value_list(struct value_list *list);
|
||||
extern void free_value_list(struct value_list *list);
|
||||
extern void print_value_list(struct value_list *list);
|
||||
extern struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list);
|
||||
extern void free_cond_entry(struct cond_entry *ent);
|
||||
extern void print_cond_entry(struct cond_entry *ent);
|
||||
extern char *processid(char *string, int len);
|
||||
extern char *processquoted(char *string, int len);
|
||||
extern char *processunquoted(char *string, int len);
|
||||
extern int get_keyword_token(const char *keyword);
|
||||
@@ -293,6 +343,7 @@ extern void debug_cod_list(struct codomain *list);
|
||||
extern int str_to_boolean(const char* str);
|
||||
extern struct cod_entry *copy_cod_entry(struct cod_entry *cod);
|
||||
extern void free_cod_entries(struct cod_entry *list);
|
||||
extern void free_mnt_entries(struct mnt_entry *list);
|
||||
|
||||
/* parser_symtab.c */
|
||||
struct set_value {;
|
||||
@@ -330,10 +381,12 @@ extern int cache_fd;
|
||||
extern void add_to_list(struct codomain *codomain);
|
||||
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 void post_process_file_entries(struct codomain *cod);
|
||||
extern void post_process_mnt_entries(struct codomain *cod);
|
||||
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 process_hat_policydb(struct codomain *cod);
|
||||
extern int post_merge_rules(void);
|
||||
extern int merge_hat_rules(struct codomain *cod);
|
||||
extern struct codomain *merge_policy(struct codomain *a, struct codomain *b);
|
||||
@@ -354,7 +407,7 @@ void free_policies(void);
|
||||
*/
|
||||
|
||||
/* parser_yacc.y */
|
||||
void yyerror(char *msg, ...)
|
||||
void yyerror(const char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[PATH_MAX];
|
||||
@@ -375,3 +428,5 @@ void yyerror(char *msg, ...)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /** __AA_PARSER_H */
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2011
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
* Copyright (c) 2010 - 2012
|
||||
* 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
|
||||
@@ -27,6 +27,7 @@ int perms_create = 0; /* perms contain create flag */
|
||||
int net_af_max_override = -1; /* use kernel to determine af_max */
|
||||
int kernel_load = 1;
|
||||
int kernel_supports_network = 1; /* kernel supports network rules */
|
||||
int kernel_supports_mount = 0; /* kernel supports mount rules */
|
||||
int flag_changehat_version = FLAG_CHANGEHAT_1_5;
|
||||
int conf_verbose = 0;
|
||||
int conf_quiet = 0;
|
||||
@@ -34,7 +35,7 @@ int names_only = 0;
|
||||
int current_lineno = 1;
|
||||
int option = OPTION_ADD;
|
||||
|
||||
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS | DFA_CONTROL_MINIMIZE_HASH_PERMS;
|
||||
dfaflags_t dfaflags = DFA_CONTROL_TREE_NORMAL | DFA_CONTROL_TREE_SIMPLE | DFA_CONTROL_MINIMIZE | DFA_CONTROL_MINIMIZE_HASH_TRANS;
|
||||
|
||||
char *subdomainbase = NULL;
|
||||
char *progname = __FILE__;
|
||||
@@ -54,18 +55,16 @@ void pwarn(char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char *newfmt;
|
||||
int rc;
|
||||
|
||||
if (conf_quiet || names_only || option == OPTION_REMOVE)
|
||||
return;
|
||||
|
||||
rc = asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
|
||||
profilename ? profilename : "stdin",
|
||||
current_filename ? current_filename : "",
|
||||
current_filename ? " " : "",
|
||||
current_lineno,
|
||||
fmt);
|
||||
if (!newfmt)
|
||||
if (asprintf(&newfmt, _("Warning from %s (%s%sline %d): %s"),
|
||||
profilename ? profilename : "stdin",
|
||||
current_filename ? current_filename : "",
|
||||
current_filename ? " " : "",
|
||||
current_lineno,
|
||||
fmt) == -1)
|
||||
return;
|
||||
|
||||
va_start(arg, fmt);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
* Copyright (c) 2010
|
||||
* Canonical, Ltd.
|
||||
* Copyright (c) 2010 - 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
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
* Copyright (c) 2010
|
||||
* Canonical, Ltd.
|
||||
* Copyright (c) 2010 - 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
|
||||
|
@@ -59,6 +59,7 @@
|
||||
|
||||
#define SUBDOMAIN_INTERFACE_VERSION 2
|
||||
#define SUBDOMAIN_INTERFACE_DFA_VERSION 5
|
||||
#define SUBDOMAIN_INTERFACE_POLICY_DB 16
|
||||
|
||||
int sd_serialize_codomain(int option, struct codomain *cod);
|
||||
|
||||
@@ -609,15 +610,14 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
|
||||
|
||||
#define low_caps(X) ((u32) ((X) & 0xffffffff))
|
||||
#define high_caps(X) ((u32) (((X) >> 32) & 0xffffffff))
|
||||
allowed_caps = (profile->capabilities | profile->set_caps) &
|
||||
~profile->deny_caps;
|
||||
allowed_caps = (profile->capabilities) & ~profile->deny_caps;
|
||||
if (!sd_write32(p, low_caps(allowed_caps)))
|
||||
return 0;
|
||||
if (!sd_write32(p, low_caps(allowed_caps & profile->audit_caps)))
|
||||
return 0;
|
||||
if (!sd_write32(p, low_caps(profile->deny_caps & profile->quiet_caps)))
|
||||
return 0;
|
||||
if (!sd_write32(p, low_caps(profile->set_caps & ~profile->deny_caps)))
|
||||
if (!sd_write32(p, 0))
|
||||
return 0;
|
||||
|
||||
if (!sd_write_struct(p, "caps64"))
|
||||
@@ -628,7 +628,7 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
|
||||
return 0;
|
||||
if (!sd_write32(p, high_caps(profile->deny_caps & profile->quiet_caps)))
|
||||
return 0;
|
||||
if (!sd_write32(p, high_caps(profile->set_caps & ~profile->deny_caps)))
|
||||
if (!sd_write32(p, 0))
|
||||
return 0;
|
||||
if (!sd_write_structend(p))
|
||||
return 0;
|
||||
@@ -655,6 +655,15 @@ int sd_serialize_profile(sd_serialize *p, struct codomain *profile,
|
||||
} else if (profile->network_allowed)
|
||||
pwarn(_("profile %s network rules not enforced\n"), profile->name);
|
||||
|
||||
if (profile->policy_dfa && regex_type == AARE_DFA) {
|
||||
if (!sd_write_struct(p, "policydb"))
|
||||
return 0;
|
||||
if (!sd_serialize_dfa(p, profile->policy_dfa, profile->policy_dfa_size))
|
||||
return 0;
|
||||
if (!sd_write_structend(p))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* either have a single dfa or lists of different entry types */
|
||||
if (regex_type == AARE_DFA) {
|
||||
if (!sd_serialize_dfa(p, profile->dfa, profile->dfa_size))
|
||||
@@ -686,9 +695,13 @@ int sd_serialize_top_profile(sd_serialize *p, struct codomain *profile)
|
||||
{
|
||||
int version;
|
||||
|
||||
if (regex_type == AARE_DFA)
|
||||
version = SUBDOMAIN_INTERFACE_DFA_VERSION;
|
||||
else
|
||||
if (regex_type == AARE_DFA) {
|
||||
/* Not yet
|
||||
if (profile->policy_dfa)
|
||||
version = SUBDOMAIN_INTERFACE_POLICYDB;
|
||||
else */
|
||||
version = SUBDOMAIN_INTERFACE_DFA_VERSION;
|
||||
} else
|
||||
version = SUBDOMAIN_INTERFACE_VERSION;
|
||||
|
||||
|
||||
@@ -763,10 +776,10 @@ int sd_serialize_codomain(int option, struct codomain *cod)
|
||||
int len = 0;
|
||||
|
||||
if (profile_namespace) {
|
||||
len += strlen(profile_namespace) + 1;
|
||||
len += strlen(profile_namespace) + 2;
|
||||
ns = profile_namespace;
|
||||
} else if (cod->namespace) {
|
||||
len += strlen(cod->namespace) + 1;
|
||||
len += strlen(cod->namespace) + 2;
|
||||
ns = cod->namespace;
|
||||
}
|
||||
if (cod->parent) {
|
||||
@@ -778,7 +791,7 @@ int sd_serialize_codomain(int option, struct codomain *cod)
|
||||
goto exit;
|
||||
}
|
||||
if (ns)
|
||||
sprintf(name, "%s:%s//%s", ns,
|
||||
sprintf(name, ":%s:%s//%s", ns,
|
||||
cod->parent->name, cod->name);
|
||||
else
|
||||
sprintf(name, "%s//%s", cod->parent->name,
|
||||
@@ -790,7 +803,7 @@ int sd_serialize_codomain(int option, struct codomain *cod)
|
||||
error = -errno;
|
||||
goto exit;
|
||||
}
|
||||
sprintf(name, "%s:%s", ns, cod->name);
|
||||
sprintf(name, ":%s:%s", ns, cod->name);
|
||||
} else {
|
||||
name = cod->name;
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
* Copyright (c) 2010
|
||||
* Canonical, Ltd.
|
||||
* Copyright (c) 2010 - 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
|
||||
@@ -23,6 +23,7 @@
|
||||
/* eliminates need to link with libfl */
|
||||
%option noyywrap
|
||||
%option nounput
|
||||
%option stack
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
@@ -44,7 +45,7 @@
|
||||
#endif
|
||||
/* #define DEBUG */
|
||||
#ifdef DEBUG
|
||||
#define PDEBUG(fmt, args...) printf("Lexer: " fmt, ## args)
|
||||
#define PDEBUG(fmt, args...) printf("Lexer (state %d): " fmt, YY_START, ## args)
|
||||
#else
|
||||
#define PDEBUG(fmt, args...) /* Do nothing */
|
||||
#endif
|
||||
@@ -167,25 +168,35 @@ void include_filename(char *filename, int search)
|
||||
|
||||
%}
|
||||
|
||||
UP "^"
|
||||
CARET "^"
|
||||
OPEN_BRACE \{
|
||||
CLOSE_BRACE \}
|
||||
SLASH \/
|
||||
COLON :
|
||||
END_OF_RULE [,]
|
||||
SEPARATOR {UP}
|
||||
RANGE -
|
||||
MODE_CHARS ([RrWwaLlMmkXx])|(([Pp]|[Cc])[Xx])|(([Pp]|[Cc])?([IiUu])[Xx])
|
||||
MODES {MODE_CHARS}+
|
||||
WS [[:blank:]]
|
||||
NUMBER [[:digit:]]+
|
||||
ID [^ \t\n"!,]|(,[^ \t\n"!])
|
||||
POST_VAR_ID [^ =\+\t\n"!,]|(,[^ =\+\t\n"!])
|
||||
IP {NUMBER}\.{NUMBER}\.{NUMBER}\.{NUMBER}
|
||||
|
||||
ID_CHARS [^ \t\n"!,]
|
||||
ID {ID_CHARS}|(,{ID_CHARS})
|
||||
IDS {ID}+
|
||||
POST_VAR_ID_CHARS [^ \t\n"!,]{-}[=\+]
|
||||
POST_VAR_ID {POST_VAR_ID_CHARS}|(,{POST_VAR_ID_CHARS})
|
||||
LIST_VALUE_ID_CHARS [^ \t\n"!,]{-}[()]
|
||||
LIST_VALUE_ID {LIST_VALUE_ID_CHARS}+
|
||||
ID_CHARS_NOEQ [^ \t\n"!,]{-}[=]
|
||||
ID_NOEQ {ID_CHARS_NOEQ}|(,{ID_CHARS_NOEQ})
|
||||
IDS_NOEQ {ID_NOEQ}+
|
||||
ALLOWED_QUOTED_ID [^\0"]|\\\"
|
||||
QUOTED_ID \"{ALLOWED_QUOTED_ID}*\"
|
||||
|
||||
HAT hat[ \t]+
|
||||
IP {NUMBER}\.{NUMBER}\.{NUMBER}\.{NUMBER}
|
||||
|
||||
HAT hat{WS}*
|
||||
PROFILE profile{WS}*
|
||||
KEYWORD [[:alpha:]_]+
|
||||
VARIABLE_NAME [[:alpha:]][[:alnum:]_]*
|
||||
SET_VAR_PREFIX @
|
||||
@@ -195,25 +206,36 @@ BOOL_VARIABLE $(\{{VARIABLE_NAME}\}|{VARIABLE_NAME})
|
||||
PATHNAME (\/|{SET_VARIABLE}{POST_VAR_ID}){ID}*
|
||||
QPATHNAME \"(\/|{SET_VAR_PREFIX})([^\0"]|\\\")*\"
|
||||
|
||||
FLAGOPEN_PAREN \(
|
||||
FLAGCLOSE_PAREN \)
|
||||
FLAGSEP \,
|
||||
OPEN_PAREN \(
|
||||
CLOSE_PAREN \)
|
||||
COMMA \,
|
||||
EQUALS =
|
||||
ADD_ASSIGN \+=
|
||||
ARROW ->
|
||||
LT_EQUAL <=
|
||||
|
||||
%x SUB_NAME
|
||||
%x SUB_NAME2
|
||||
%x SUB_ID
|
||||
%x SUB_VALUE
|
||||
%x EXTCOND_MODE
|
||||
%x NETWORK_MODE
|
||||
%x FLAGS_MODE
|
||||
%x LIST_VAL_MODE
|
||||
%x ASSIGN_MODE
|
||||
%x RLIMIT_MODE
|
||||
%x MOUNT_MODE
|
||||
%x CHANGE_PROFILE_MODE
|
||||
%x INCLUDE
|
||||
|
||||
%%
|
||||
|
||||
%{
|
||||
/* Copied directly into yylex function */
|
||||
if (parser_token) {
|
||||
int t = parser_token;
|
||||
parser_token = 0;
|
||||
return t;
|
||||
}
|
||||
%}
|
||||
|
||||
<INCLUDE>{
|
||||
{WS}+ { /* Eat whitespace */ }
|
||||
\<([^\> \t\n]+)\> { /* <filename> */
|
||||
@@ -221,7 +243,7 @@ LT_EQUAL <=
|
||||
filename[strlen(filename) - 1] = '\0';
|
||||
include_filename(filename + 1, 1);
|
||||
free(filename);
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
}
|
||||
|
||||
\"([^\" \t\n]+)\" { /* "filename" */
|
||||
@@ -229,12 +251,12 @@ LT_EQUAL <=
|
||||
filename[strlen(filename) - 1] = '\0';
|
||||
include_filename(filename + 1, 0);
|
||||
free(filename);
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
}
|
||||
|
||||
[^\<\>\"{WS}]+ { /* filename */
|
||||
include_filename(yytext, 0);
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,112 +267,100 @@ LT_EQUAL <=
|
||||
if ( !YY_CURRENT_BUFFER ) yyterminate();
|
||||
}
|
||||
|
||||
<SUB_NAME>{
|
||||
{ID}+ {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {ID}+ to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processunquoted(yytext, yyleng);
|
||||
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
|
||||
BEGIN(INITIAL);
|
||||
return TOK_ID;
|
||||
}
|
||||
{QUOTED_ID} {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {ID}+ to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processquoted(yytext, yyleng);
|
||||
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
|
||||
BEGIN(INITIAL);
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
[^\n] {
|
||||
DUMP_PREPROCESS;
|
||||
/* Something we didn't expect */
|
||||
yyerror(_("Found unexpected character: '%s'"), yytext);
|
||||
}
|
||||
}
|
||||
|
||||
<SUB_NAME2>{
|
||||
{ID}+ {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {ID}+ to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processunquoted(yytext, yyleng);
|
||||
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
|
||||
BEGIN(INITIAL);
|
||||
return TOK_ID;
|
||||
}
|
||||
{QUOTED_ID} {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {ID}+ to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processquoted(yytext, yyleng);
|
||||
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
|
||||
BEGIN(INITIAL);
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
|
||||
[^\n] {
|
||||
DUMP_PREPROCESS;
|
||||
/* Something we didn't expect */
|
||||
yyerror(_("Found unexpected character: '%s'"), yytext);
|
||||
}
|
||||
}
|
||||
|
||||
<FLAGS_MODE>{
|
||||
{FLAGOPEN_PAREN} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("FLag (\n");
|
||||
return TOK_FLAG_OPENPAREN;
|
||||
<INITIAL,MOUNT_MODE>{
|
||||
{VARIABLE_NAME}/{WS}*= {
|
||||
/* we match to the = in the lexer so that
|
||||
* can switch scanner state. By the time
|
||||
* the parser see the = it may be to late
|
||||
* as bison may have requested the next
|
||||
* token from the scanner
|
||||
*/
|
||||
PDEBUG("conditional %s=\n", yytext);
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
yy_push_state(EXTCOND_MODE);
|
||||
return TOK_CONDID;
|
||||
}
|
||||
{FLAGCLOSE_PAREN} {
|
||||
{VARIABLE_NAME}/{WS}+in{WS}*\( {
|
||||
/* we match to 'in' in the lexer so that
|
||||
* we can switch scanner state. By the time
|
||||
* the parser see the 'in' it may be to late
|
||||
* as bison may have requested the next
|
||||
* token from the scanner
|
||||
*/
|
||||
PDEBUG("conditional %s=\n", yytext);
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
yy_push_state(EXTCOND_MODE);
|
||||
return TOK_CONDID;
|
||||
}
|
||||
}
|
||||
|
||||
<SUB_ID>{
|
||||
({IDS}|{QUOTED_ID}) {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {IDS} to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Flag )\n");
|
||||
BEGIN(INITIAL);
|
||||
return TOK_FLAG_CLOSEPAREN;
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
PDEBUG("Found sub name: \"%s\"\n", yylval.id);
|
||||
yy_pop_state();
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
[^\n] {
|
||||
DUMP_PREPROCESS;
|
||||
/* Something we didn't expect */
|
||||
yyerror(_("Found unexpected character: '%s'"), yytext);
|
||||
}
|
||||
}
|
||||
|
||||
<SUB_VALUE>{
|
||||
({IDS}|{QUOTED_ID}) {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {IDS} to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
PDEBUG("Found sub value: \"%s\"\n", yylval.id);
|
||||
yy_pop_state();
|
||||
return TOK_VALUE;
|
||||
}
|
||||
|
||||
[^\n] {
|
||||
DUMP_PREPROCESS;
|
||||
/* Something we didn't expect */
|
||||
yyerror(_("Found unexpected character: '%s'"), yytext);
|
||||
}
|
||||
}
|
||||
|
||||
<LIST_VAL_MODE>{
|
||||
{CLOSE_PAREN} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("listval: )\n");
|
||||
yy_pop_state();
|
||||
return TOK_CLOSEPAREN;
|
||||
}
|
||||
|
||||
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
|
||||
|
||||
{FLAGSEP} {
|
||||
{COMMA} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Flag , \n");
|
||||
return TOK_FLAG_SEP;
|
||||
PDEBUG("listval: , \n");
|
||||
/* East comma, its an optional separator */
|
||||
}
|
||||
|
||||
{EQUALS} {
|
||||
({LIST_VALUE_ID}|{QUOTED_ID}) {
|
||||
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;
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
PDEBUG("listval: \"%s\"\n", yylval.id);
|
||||
return TOK_VALUE;
|
||||
}
|
||||
|
||||
[^\n] {
|
||||
@@ -360,19 +370,51 @@ LT_EQUAL <=
|
||||
}
|
||||
}
|
||||
|
||||
<EXTCOND_MODE>{
|
||||
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
|
||||
|
||||
{EQUALS}{WS}*/[^(\n]{-}{WS} {
|
||||
DUMP_PREPROCESS;
|
||||
BEGIN(SUB_VALUE);
|
||||
return TOK_EQUALS;
|
||||
}
|
||||
|
||||
{EQUALS} {
|
||||
DUMP_PREPROCESS;
|
||||
return TOK_EQUALS;
|
||||
}
|
||||
|
||||
{OPEN_PAREN} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("extcond listv\n");
|
||||
/* Don't push state here as this is a transition
|
||||
* start condition and we want to return to the start
|
||||
* condition that invoked <EXTCOND_MODE> when
|
||||
* LIST_VAL_ID is done
|
||||
*/
|
||||
BEGIN(LIST_VAL_MODE);
|
||||
return TOK_OPENPAREN;
|
||||
}
|
||||
|
||||
in {
|
||||
DUMP_PREPROCESS;
|
||||
return TOK_IN;
|
||||
}
|
||||
|
||||
[^\n] {
|
||||
DUMP_PREPROCESS;
|
||||
/* Something we didn't expect */
|
||||
yyerror(_("Found unexpected character: '%s' %d"), yytext, *yytext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
<ASSIGN_MODE>{
|
||||
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
|
||||
|
||||
{ID}+ {
|
||||
({IDS}|{QUOTED_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);
|
||||
yylval.var_val = processid(yytext, yyleng);
|
||||
PDEBUG("Found assignment value: \"%s\"\n", yylval.var_val);
|
||||
return TOK_VALUE;
|
||||
}
|
||||
@@ -388,7 +430,7 @@ LT_EQUAL <=
|
||||
\r?\n {
|
||||
DUMP_PREPROCESS;
|
||||
current_lineno++;
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
}
|
||||
[^\n] {
|
||||
DUMP_PREPROCESS;
|
||||
@@ -400,14 +442,14 @@ LT_EQUAL <=
|
||||
<NETWORK_MODE>{
|
||||
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
|
||||
|
||||
{ID}+ {
|
||||
{IDS} {
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = strdup(yytext);
|
||||
return TOK_ID;
|
||||
}
|
||||
{END_OF_RULE} {
|
||||
DUMP_PREPROCESS;
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
return TOK_END_OF_RULE;
|
||||
}
|
||||
[^\n] {
|
||||
@@ -430,32 +472,18 @@ LT_EQUAL <=
|
||||
return TOK_ARROW;
|
||||
}
|
||||
|
||||
{ID}+ {
|
||||
({IDS}|{QUOTED_ID}) {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {ID}+ to match all TOK_IDs, but that would
|
||||
* {IDS} to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processunquoted(yytext, yyleng);
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
PDEBUG("Found change profile name: \"%s\"\n", yylval.id);
|
||||
BEGIN(INITIAL);
|
||||
return TOK_ID;
|
||||
}
|
||||
{QUOTED_ID} {
|
||||
/* Ugh, this is a gross hack. I used to use
|
||||
* {ID}+ to match all TOK_IDs, but that would
|
||||
* also match TOK_MODE + TOK_END_OF_RULE
|
||||
* without any spaces in between (because it's
|
||||
* a longer match). So now, when I want to
|
||||
* match any random string, I go into a
|
||||
* separate state. */
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processquoted(yytext, yyleng);
|
||||
PDEBUG("Found change profile quoted name: \"%s\"\n", yylval.id);
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
@@ -467,43 +495,6 @@ LT_EQUAL <=
|
||||
}
|
||||
}
|
||||
|
||||
#include/.*\r?\n { /* include */
|
||||
PDEBUG("Matched #include\n");
|
||||
BEGIN(INCLUDE);
|
||||
}
|
||||
|
||||
#.*\r?\n { /* normal comment */
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("comment(%d): %s\n", current_lineno, yytext);
|
||||
current_lineno++;
|
||||
BEGIN(INITIAL);
|
||||
}
|
||||
|
||||
{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;
|
||||
}
|
||||
<RLIMIT_MODE>{
|
||||
{WS}+ { DUMP_PREPROCESS; /* Eat whitespace */ }
|
||||
|
||||
@@ -526,23 +517,94 @@ LT_EQUAL <=
|
||||
|
||||
{END_OF_RULE} {
|
||||
DUMP_PREPROCESS;
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
return TOK_END_OF_RULE;
|
||||
}
|
||||
|
||||
\\\n {
|
||||
DUMP_PREPROCESS;
|
||||
current_lineno++;
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
}
|
||||
|
||||
\r?\n {
|
||||
DUMP_PREPROCESS;
|
||||
current_lineno++;
|
||||
BEGIN(INITIAL);
|
||||
yy_pop_state();
|
||||
}
|
||||
}
|
||||
|
||||
<MOUNT_MODE>{
|
||||
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
|
||||
|
||||
{ARROW} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Matched arrow\n");
|
||||
return TOK_ARROW;
|
||||
}
|
||||
|
||||
({IDS_NOEQ}|{PATHNAME}|{QUOTED_ID}) {
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processid(yytext, yyleng);
|
||||
PDEBUG("Found ID: \"%s\"\n", yylval.id);
|
||||
return TOK_ID;
|
||||
}
|
||||
|
||||
{END_OF_RULE} {
|
||||
DUMP_PREPROCESS;
|
||||
yy_pop_state();
|
||||
return TOK_END_OF_RULE;
|
||||
}
|
||||
|
||||
[^\n] {
|
||||
DUMP_PREPROCESS;
|
||||
/* Something we didn't expect */
|
||||
yyerror(_("Found unexpected character: '%s'"), yytext);
|
||||
}
|
||||
|
||||
\r?\n {
|
||||
DUMP_PREPROCESS;
|
||||
current_lineno++;
|
||||
yy_pop_state();
|
||||
}
|
||||
}
|
||||
|
||||
#include/.*\r?\n { /* include */
|
||||
PDEBUG("Matched #include\n");
|
||||
yy_push_state(INCLUDE);
|
||||
}
|
||||
|
||||
#.*\r?\n { /* normal comment */
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("comment(%d): %s\n", current_lineno, yytext);
|
||||
current_lineno++;
|
||||
}
|
||||
|
||||
{END_OF_RULE} { DUMP_PREPROCESS; return TOK_END_OF_RULE; }
|
||||
|
||||
{CARET} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Matched hat ^\n");
|
||||
yy_push_state(SUB_ID);
|
||||
return TOK_CARET;
|
||||
}
|
||||
{ARROW} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Matched a arrow\n");
|
||||
return TOK_ARROW;
|
||||
}
|
||||
{EQUALS} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Matched equals for assignment\n");
|
||||
yy_push_state(ASSIGN_MODE);
|
||||
return TOK_EQUALS;
|
||||
}
|
||||
{ADD_ASSIGN} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Matched additive value assignment\n");
|
||||
yy_push_state(ASSIGN_MODE);
|
||||
return TOK_ADD_ASSIGN;
|
||||
}
|
||||
{SET_VARIABLE} {
|
||||
DUMP_PREPROCESS;
|
||||
yylval.set_var = strdup(yytext);
|
||||
@@ -568,21 +630,14 @@ LT_EQUAL <=
|
||||
return TOK_CLOSE;
|
||||
}
|
||||
|
||||
{PATHNAME} {
|
||||
({PATHNAME}|{QPATHNAME}) {
|
||||
DUMP_PREPROCESS;
|
||||
yylval.id = processunquoted(yytext, yyleng);
|
||||
yylval.id = processid(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} {
|
||||
({MODES})/([[:space:],]) {
|
||||
DUMP_PREPROCESS;
|
||||
yylval.mode = strdup(yytext);
|
||||
PDEBUG("Found modes: %s\n", yylval.mode);
|
||||
@@ -591,21 +646,27 @@ LT_EQUAL <=
|
||||
|
||||
{HAT} {
|
||||
DUMP_PREPROCESS;
|
||||
BEGIN(SUB_NAME2);
|
||||
yy_push_state(SUB_ID);
|
||||
return TOK_HAT;
|
||||
}
|
||||
|
||||
{PROFILE} {
|
||||
DUMP_PREPROCESS;
|
||||
yy_push_state(SUB_ID);
|
||||
return TOK_PROFILE;
|
||||
}
|
||||
|
||||
{COLON} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Found a colon\n");
|
||||
return TOK_COLON;
|
||||
}
|
||||
|
||||
{FLAGOPEN_PAREN} {
|
||||
{OPEN_PAREN} {
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("FLag (\n");
|
||||
BEGIN(FLAGS_MODE);
|
||||
return TOK_FLAG_OPENPAREN;
|
||||
PDEBUG("listval (\n");
|
||||
yy_push_state(LIST_VAL_MODE);
|
||||
return TOK_OPENPAREN;
|
||||
}
|
||||
|
||||
{VARIABLE_NAME} {
|
||||
@@ -620,20 +681,21 @@ LT_EQUAL <=
|
||||
PDEBUG("Found (var) id: \"%s\"\n", yylval.id);
|
||||
return TOK_ID;
|
||||
break;
|
||||
case TOK_PROFILE:
|
||||
BEGIN(SUB_NAME2);
|
||||
break;
|
||||
case TOK_FLAGS:
|
||||
BEGIN(FLAGS_MODE);
|
||||
break;
|
||||
case TOK_RLIMIT:
|
||||
BEGIN(RLIMIT_MODE);
|
||||
yy_push_state(RLIMIT_MODE);
|
||||
break;
|
||||
case TOK_NETWORK:
|
||||
BEGIN(NETWORK_MODE);
|
||||
yy_push_state(NETWORK_MODE);
|
||||
break;
|
||||
case TOK_CHANGE_PROFILE:
|
||||
BEGIN(CHANGE_PROFILE_MODE);
|
||||
yy_push_state(CHANGE_PROFILE_MODE);
|
||||
break;
|
||||
case TOK_MOUNT:
|
||||
case TOK_REMOUNT:
|
||||
case TOK_UMOUNT:
|
||||
DUMP_PREPROCESS;
|
||||
PDEBUG("Entering mount\n");
|
||||
yy_push_state(MOUNT_MODE);
|
||||
break;
|
||||
default: /* nothing */
|
||||
break;
|
||||
|
@@ -2,8 +2,8 @@
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
*
|
||||
* Copyright (c) 2010, 2011
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
* Copyright (c) 2010 - 2012
|
||||
* 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
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <mntent.h>
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
#include <dirent.h>
|
||||
#define _(s) gettext(s)
|
||||
|
||||
/* enable the following line to get voluminous debug info */
|
||||
@@ -59,7 +60,7 @@
|
||||
#define UNPRIVILEGED_OPS (!(PRIVILEGED_OPS))
|
||||
|
||||
const char *parser_title = "AppArmor parser";
|
||||
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2011 Canonical Ltd.";
|
||||
const char *parser_copyright = "Copyright (C) 1999-2008 Novell Inc.\nCopyright 2009-2012 Canonical Ltd.";
|
||||
|
||||
char *progname;
|
||||
int opt_force_complain = 0;
|
||||
@@ -74,8 +75,10 @@ int preprocess_only = 0;
|
||||
int skip_mode_force = 0;
|
||||
struct timespec mru_tstamp;
|
||||
|
||||
#define FLAGS_STRING_SIZE 1024
|
||||
char *match_string = NULL;
|
||||
char *flags_string = NULL;
|
||||
char *cacheloc = NULL;
|
||||
|
||||
/* per-profile settings */
|
||||
int force_complain = 0;
|
||||
@@ -106,6 +109,7 @@ struct option long_options[] = {
|
||||
{"skip-read-cache", 0, 0, 'T'},
|
||||
{"write-cache", 0, 0, 'W'},
|
||||
{"show-cache", 0, 0, 'k'},
|
||||
{"cache-loc", 1, 0, 'L'},
|
||||
{"debug", 0, 0, 'd'},
|
||||
{"dump", 1, 0, 'D'},
|
||||
{"Dump", 1, 0, 'D'},
|
||||
@@ -147,6 +151,7 @@ static void display_usage(char *command)
|
||||
"-K, --skip-cache Do not attempt to load or save cached profiles\n"
|
||||
"-T, --skip-read-cache Do not attempt to load cached profiles\n"
|
||||
"-W, --write-cache Save cached profile (force with -T)\n"
|
||||
"-L, --cache-loc n Set the location of the profile cache\n"
|
||||
"-q, --quiet Don't emit warnings\n"
|
||||
"-v, --verbose Show profile names as they load\n"
|
||||
"-Q, --skip-kernel-load Do everything except loading into kernel\n"
|
||||
@@ -225,10 +230,10 @@ optflag_table_t optflag_table[] = {
|
||||
{ 2, "expr-right-simplify", "right simplification first",
|
||||
DFA_CONTROL_TREE_LEFT },
|
||||
{ 1, "minimize", "dfa state minimization", DFA_CONTROL_MINIMIZE },
|
||||
{ 1, "hash-perms", "minimization - hash permissions during setup",
|
||||
DFA_CONTROL_MINIMIZE_HASH_PERMS },
|
||||
{ 1, "hash-trans", "minimization - hash transitions during setup",
|
||||
DFA_CONTROL_MINIMIZE_HASH_TRANS },
|
||||
{ 1, "filter-deny", "filter out deny information from final dfa",
|
||||
DFA_CONTROL_FILTER_DENY },
|
||||
{ 1, "remove-unreachable", "dfa unreachable state removal",
|
||||
DFA_CONTROL_REMOVE_UNREACHABLE },
|
||||
{ 0, "compress-small",
|
||||
@@ -522,6 +527,9 @@ static int process_arg(int c, char *optarg)
|
||||
case 'T':
|
||||
skip_read_cache = 1;
|
||||
break;
|
||||
case 'L':
|
||||
cacheloc = strdup(optarg);
|
||||
break;
|
||||
case 'Q':
|
||||
kernel_load = 0;
|
||||
break;
|
||||
@@ -667,17 +675,139 @@ int have_enough_privilege(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *snprintf_buffer(char *buf, char *pos, ssize_t size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int i, remaining = size - (pos - buf);
|
||||
|
||||
va_start(args, fmt);
|
||||
i = vsnprintf(pos, remaining, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (i >= size) {
|
||||
PERROR(_("Feature buffer full."));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return pos + i;
|
||||
}
|
||||
|
||||
static char *handle_features_dir(const char *filename, char **buffer, int size,
|
||||
char *pos)
|
||||
{
|
||||
DIR *dir = NULL;
|
||||
char *dirent_path = NULL;
|
||||
struct dirent *dirent;
|
||||
struct stat my_stat;
|
||||
int len;
|
||||
|
||||
PDEBUG("Opened features directory \"%s\"\n", filename);
|
||||
if (!(dir = opendir(filename))) {
|
||||
PDEBUG("opendir failed '%s'", filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while ((dirent = readdir(dir)) != NULL) {
|
||||
int name_len;
|
||||
/* skip dotfiles silently. */
|
||||
if (dirent->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (dirent_path)
|
||||
free(dirent_path);
|
||||
if (asprintf(&dirent_path, "%s/%s", filename, dirent->d_name) < 0)
|
||||
{
|
||||
PERROR(_("Memory allocation error."));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
name_len = strlen(dirent->d_name);
|
||||
if (!name_len)
|
||||
continue;
|
||||
|
||||
if (stat(dirent_path, &my_stat)) {
|
||||
PERROR(_("stat failed for '%s'"), dirent_path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pos = snprintf_buffer(*buffer, pos, size, "%s {", dirent->d_name);
|
||||
if (S_ISREG(my_stat.st_mode)) {
|
||||
int file;
|
||||
int remaining = size - (pos - *buffer);
|
||||
if (!(file = open(dirent_path, O_RDONLY))) {
|
||||
PDEBUG("Could not open '%s' in '%s'", dirent_path, filename);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
PDEBUG("Opened features \"%s\" in \"%s\"\n", dirent_path, filename);
|
||||
if (my_stat.st_size > remaining) {
|
||||
PERROR(_("Feature buffer full."));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
do {
|
||||
len = read(file, pos, remaining);
|
||||
if (len > 0) {
|
||||
remaining -= len;
|
||||
pos += len;
|
||||
*pos = 0;
|
||||
}
|
||||
} while (len > 0);
|
||||
if (len < 0) {
|
||||
PDEBUG("Error reading feature file '%s'\n",
|
||||
dirent_path);
|
||||
exit(1);
|
||||
}
|
||||
close(file);
|
||||
|
||||
} else if (S_ISDIR(my_stat.st_mode)) {
|
||||
pos = handle_features_dir(dirent_path, buffer, size,
|
||||
pos);
|
||||
if (!pos)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
pos = snprintf_buffer(*buffer, pos, size, "}\n");
|
||||
}
|
||||
if (dirent_path)
|
||||
free(dirent_path);
|
||||
closedir(dir);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* match_string == NULL --> no match_string available
|
||||
match_string != NULL --> either a matching string specified on the
|
||||
command line, or the kernel supplied a match string */
|
||||
static void get_match_string(void) {
|
||||
|
||||
FILE *ms = NULL;
|
||||
struct stat stat_file;
|
||||
|
||||
/* has process_args() already assigned a match string? */
|
||||
if (match_string)
|
||||
goto out;
|
||||
|
||||
if (stat(FLAGS_FILE, &stat_file) == -1)
|
||||
goto out;
|
||||
|
||||
if (S_ISDIR(stat_file.st_mode)) {
|
||||
/* if we have a features directory default to */
|
||||
regex_type = AARE_DFA;
|
||||
perms_create = 1;
|
||||
|
||||
flags_string = malloc(FLAGS_STRING_SIZE);
|
||||
handle_features_dir(FLAGS_FILE, &flags_string, FLAGS_STRING_SIZE, flags_string);
|
||||
if (strstr(flags_string, "network"))
|
||||
kernel_supports_network = 1;
|
||||
else
|
||||
kernel_supports_network = 0;
|
||||
if (strstr(flags_string, "mount"))
|
||||
kernel_supports_mount = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
ms = fopen(MATCH_STRING, "r");
|
||||
if (!ms)
|
||||
goto out;
|
||||
@@ -716,20 +846,24 @@ 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) return;
|
||||
if (!flags || *flags)
|
||||
return;
|
||||
|
||||
f = fopen(flags_file, "r");
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
*flags = malloc(1024);
|
||||
*flags = malloc(FLAGS_STRING_SIZE);
|
||||
if (!*flags)
|
||||
goto fail;
|
||||
|
||||
if (!fgets(*flags, 1024, 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=");
|
||||
@@ -920,6 +1054,15 @@ int process_profile(int option, char *profilename)
|
||||
if (retval != 0)
|
||||
goto out;
|
||||
|
||||
/* Test to see if profile is for another namespace, if so disable
|
||||
* caching for now
|
||||
* TODO: Add support for caching profiles in an alternate namespace
|
||||
* TODO: Add support for embedded namespace defines if they aren't
|
||||
* removed from the language.
|
||||
*/
|
||||
if (profile_namespace)
|
||||
skip_cache = 1;
|
||||
|
||||
/* Do secondary test to see if cached binary profile is good,
|
||||
* instead of checking against a presupplied list of files
|
||||
* use the timestamps from the files that were parsed.
|
||||
@@ -928,8 +1071,14 @@ int process_profile(int option, char *profilename)
|
||||
*/
|
||||
if ((profilename && option != OPTION_REMOVE) && !force_complain &&
|
||||
!skip_cache) {
|
||||
if (asprintf(&cachename, "%s/%s/%s", basedir, "cache", basename)<0) {
|
||||
perror("asprintf");
|
||||
if (cacheloc) {
|
||||
cachename = strdup(cacheloc);
|
||||
if (!cachename) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
exit(1);
|
||||
}
|
||||
} else if (asprintf(&cachename, "%s/%s/%s", basedir, "cache", basename)<0) {
|
||||
PERROR(_("Memory allocation error."));
|
||||
exit(1);
|
||||
}
|
||||
/* Load a binary cache if it exists and is newest */
|
||||
|
@@ -17,6 +17,7 @@
|
||||
|
||||
/* assistance routines */
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -36,6 +37,7 @@
|
||||
|
||||
#include "parser.h"
|
||||
#include "parser_yacc.h"
|
||||
#include "mount.h"
|
||||
|
||||
/* #define DEBUG */
|
||||
#ifdef DEBUG
|
||||
@@ -53,8 +55,6 @@ struct keyword_table {
|
||||
};
|
||||
|
||||
static struct keyword_table keyword_table[] = {
|
||||
/* flags */
|
||||
{"flags", TOK_FLAGS},
|
||||
/* network */
|
||||
{"network", TOK_NETWORK},
|
||||
/* misc keywords */
|
||||
@@ -73,12 +73,18 @@ static struct keyword_table keyword_table[] = {
|
||||
{"subset", TOK_SUBSET},
|
||||
{"audit", TOK_AUDIT},
|
||||
{"deny", TOK_DENY},
|
||||
{"profile", TOK_PROFILE},
|
||||
{"set", TOK_SET},
|
||||
{"rlimit", TOK_RLIMIT},
|
||||
{"alias", TOK_ALIAS},
|
||||
{"rewrite", TOK_ALIAS},
|
||||
{"ptrace", TOK_PTRACE},
|
||||
{"file", TOK_FILE},
|
||||
{"mount", TOK_MOUNT},
|
||||
{"remount", TOK_REMOUNT},
|
||||
{"umount", TOK_UMOUNT},
|
||||
{"unmount", TOK_UMOUNT},
|
||||
{"pivot_root", TOK_PIVOTROOT},
|
||||
{"in", TOK_IN},
|
||||
/* terminate */
|
||||
{NULL, 0}
|
||||
};
|
||||
@@ -397,6 +403,16 @@ char *processunquoted(char *string, int len)
|
||||
return tmp;
|
||||
}
|
||||
|
||||
char *processid(char *string, int len)
|
||||
{
|
||||
/* lexer should never call this fn if len <= 0 */
|
||||
assert(len > 0);
|
||||
|
||||
if (*string == '"')
|
||||
return processquoted(string, len);
|
||||
return processunquoted(string, len);
|
||||
}
|
||||
|
||||
/* rewrite a quoted string substituting escaped characters for the
|
||||
* real thing. Strip the quotes around the string */
|
||||
|
||||
@@ -767,6 +783,20 @@ void free_cod_entries(struct cod_entry *list)
|
||||
free(list);
|
||||
}
|
||||
|
||||
void free_mnt_entries(struct mnt_entry *list)
|
||||
{
|
||||
if (!list)
|
||||
return;
|
||||
if (list->next)
|
||||
free_mnt_entries(list->next);
|
||||
free(list->mnt_point);
|
||||
free(list->device);
|
||||
free_value_list(list->dev_type);
|
||||
free_value_list(list->opts);
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
static void debug_base_perm_mask(int mask)
|
||||
{
|
||||
if (HAS_MAY_READ(mask))
|
||||
@@ -904,8 +934,6 @@ void debug_capabilities(struct codomain *cod)
|
||||
__debug_capabilities(cod->deny_caps, "Deny Caps");
|
||||
if (cod->quiet_caps != 0ull)
|
||||
__debug_capabilities(cod->quiet_caps, "Quiet Caps");
|
||||
if (cod->set_caps != 0ull)
|
||||
__debug_capabilities(cod->set_caps, "Set Capabilities");
|
||||
}
|
||||
|
||||
void debug_cod_list(struct codomain *cod)
|
||||
@@ -932,6 +960,102 @@ void debug_cod_list(struct codomain *cod)
|
||||
dump_policy_hats(cod);
|
||||
}
|
||||
|
||||
struct value_list *new_value_list(char *value)
|
||||
{
|
||||
struct value_list *val = calloc(1, sizeof(struct value_list));
|
||||
if (val)
|
||||
val->value = value;
|
||||
return val;
|
||||
}
|
||||
|
||||
void free_value_list(struct value_list *list)
|
||||
{
|
||||
struct value_list *next;
|
||||
|
||||
while (list) {
|
||||
next = list->next;
|
||||
if (list->value)
|
||||
free(list->value);
|
||||
free(list);
|
||||
list = next;
|
||||
}
|
||||
}
|
||||
|
||||
struct value_list *dup_value_list(struct value_list *list)
|
||||
{
|
||||
struct value_list *entry, *dup, *head = NULL;
|
||||
char *value;
|
||||
|
||||
list_for_each(list, entry) {
|
||||
value = NULL;
|
||||
if (list->value) {
|
||||
value = strdup(list->value);
|
||||
if (!value)
|
||||
goto fail2;
|
||||
}
|
||||
dup = new_value_list(value);
|
||||
if (!dup)
|
||||
goto fail;
|
||||
if (head)
|
||||
list_append(head, dup);
|
||||
else
|
||||
head = dup;
|
||||
}
|
||||
|
||||
return head;
|
||||
|
||||
fail:
|
||||
free(value);
|
||||
fail2:
|
||||
free_value_list(head);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void print_value_list(struct value_list *list)
|
||||
{
|
||||
struct value_list *entry;
|
||||
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "%s", list->value);
|
||||
list = list->next;
|
||||
list_for_each(list, entry) {
|
||||
fprintf(stderr, ", %s", entry->value);
|
||||
}
|
||||
}
|
||||
|
||||
struct cond_entry *new_cond_entry(char *name, int eq, struct value_list *list)
|
||||
{
|
||||
struct cond_entry *ent = calloc(1, sizeof(struct cond_entry));
|
||||
if (ent) {
|
||||
ent->name = name;
|
||||
ent->vals = list;
|
||||
ent->eq = eq;
|
||||
}
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
void free_cond_entry(struct cond_entry *ent)
|
||||
{
|
||||
if (ent) {
|
||||
free(ent->name);
|
||||
free_value_list(ent->vals);
|
||||
free(ent);
|
||||
}
|
||||
}
|
||||
|
||||
void print_cond_entry(struct cond_entry *ent)
|
||||
{
|
||||
if (ent) {
|
||||
fprintf(stderr, "%s=(", ent->name);
|
||||
print_value_list(ent->vals);
|
||||
fprintf(stderr, ")\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
int test_str_to_boolean(void)
|
||||
{
|
||||
|
@@ -2,8 +2,8 @@
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
*
|
||||
* Copyright (c) 2010
|
||||
* Canonical, Ltd. (All rights reserved)
|
||||
* Copyright (c) 2010 - 2012
|
||||
* 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
|
||||
@@ -29,6 +29,7 @@
|
||||
#define _(s) gettext(s)
|
||||
|
||||
#include "parser.h"
|
||||
#include "mount.h"
|
||||
#include "parser_yacc.h"
|
||||
|
||||
/* #define DEBUG */
|
||||
@@ -95,10 +96,26 @@ void add_hat_to_policy(struct codomain *cod, struct codomain *hat)
|
||||
}
|
||||
}
|
||||
|
||||
static int add_entry_to_x_table(struct codomain *cod, char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) {
|
||||
if (!cod->exec_table[i]) {
|
||||
cod->exec_table[i] = name;
|
||||
return i;
|
||||
} else if (strcmp(cod->exec_table[i], name) == 0) {
|
||||
/* name already in table */
|
||||
free(name);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
free(name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
|
||||
{
|
||||
char *name = NULL;
|
||||
int i;
|
||||
|
||||
/* check to see if it is a local transition */
|
||||
if (!entry->namespace) {
|
||||
@@ -146,18 +163,7 @@ static int add_named_transition(struct codomain *cod, struct cod_entry *entry)
|
||||
name = entry->nt_name;
|
||||
}
|
||||
|
||||
for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) {
|
||||
if (!cod->exec_table[i]) {
|
||||
cod->exec_table[i] = name;
|
||||
return i;
|
||||
} else if (strcmp(cod->exec_table[i], name) == 0) {
|
||||
/* name already in table */
|
||||
free(name);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
free(name);
|
||||
return 0;
|
||||
return add_entry_to_x_table(cod, name);
|
||||
}
|
||||
|
||||
void add_entry_to_policy(struct codomain *cod, struct cod_entry *entry)
|
||||
@@ -166,16 +172,17 @@ void add_entry_to_policy(struct codomain *cod, struct cod_entry *entry)
|
||||
cod->entries = entry;
|
||||
}
|
||||
|
||||
void post_process_nt_entries(struct codomain *cod)
|
||||
void post_process_file_entries(struct codomain *cod)
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
int cp_mode = 0;
|
||||
|
||||
list_for_each(cod->entries, entry) {
|
||||
if (entry->nt_name) {
|
||||
int mode = 0;
|
||||
int n = add_named_transition(cod, entry);
|
||||
if (!n) {
|
||||
PERROR("Profile %s has to many specified profile transitions.\n", cod->name);
|
||||
PERROR("Profile %s has too many specified profile transitions.\n", cod->name);
|
||||
exit(1);
|
||||
}
|
||||
if (entry->mode & AA_USER_EXEC)
|
||||
@@ -187,9 +194,56 @@ void post_process_nt_entries(struct codomain *cod)
|
||||
entry->namespace = NULL;
|
||||
entry->nt_name = NULL;
|
||||
}
|
||||
/* FIXME: currently change_profile also implies onexec */
|
||||
cp_mode |= entry->mode & (AA_CHANGE_PROFILE);
|
||||
}
|
||||
|
||||
/* if there are change_profile rules, this implies that we need
|
||||
* access to /proc/self/attr/current
|
||||
*/
|
||||
if (cp_mode & AA_CHANGE_PROFILE) {
|
||||
/* FIXME: should use @{PROC}/@{PID}/attr/{current,exec} */
|
||||
struct cod_entry *new_ent;
|
||||
char *buffer = strdup("/proc/*/attr/{current,exec}");
|
||||
if (!buffer) {
|
||||
PERROR("Memory allocation error\n");
|
||||
exit(1);
|
||||
}
|
||||
new_ent = new_entry(NULL, buffer, AA_MAY_WRITE, NULL);
|
||||
if (!new_ent) {
|
||||
PERROR("Memory allocation error\n");
|
||||
exit(1);
|
||||
}
|
||||
add_entry_to_policy(cod, new_ent);
|
||||
}
|
||||
}
|
||||
|
||||
void post_process_mnt_entries(struct codomain *cod)
|
||||
{
|
||||
struct mnt_entry *entry;
|
||||
|
||||
list_for_each(cod->mnt_ents, entry) {
|
||||
if (entry->trans) {
|
||||
unsigned int mode = 0;
|
||||
int n = add_entry_to_x_table(cod, entry->trans);
|
||||
if (!n) {
|
||||
PERROR("Profile %s has too many specified profile transitions.\n", cod->name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (entry->allow & AA_USER_EXEC)
|
||||
mode |= SHIFT_MODE(n << 10, AA_USER_SHIFT);
|
||||
if (entry->allow & AA_OTHER_EXEC)
|
||||
mode |= SHIFT_MODE(n << 10, AA_OTHER_SHIFT);
|
||||
entry->allow = ((entry->allow & ~AA_ALL_EXEC_MODIFIERS) |
|
||||
(mode & AA_ALL_EXEC_MODIFIERS));
|
||||
|
||||
entry->trans = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void __merge_rules(const void *nodep, const VISIT value,
|
||||
const int __unused depth)
|
||||
{
|
||||
@@ -294,6 +348,33 @@ int process_hat_regex(struct codomain *cod)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __process_policydb(const void *nodep, const VISIT value,
|
||||
const int __unused depth)
|
||||
{
|
||||
struct codomain **t = (struct codomain **) nodep;
|
||||
|
||||
if (value == preorder || value == endorder)
|
||||
return;
|
||||
|
||||
if (process_policydb(*t) != 0) {
|
||||
PERROR(_("ERROR processing policydb rules for profile %s, failed to load\n"),
|
||||
(*t)->name);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int post_process_policydb(void)
|
||||
{
|
||||
twalk(policy_list, __process_policydb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_hat_policydb(struct codomain *cod)
|
||||
{
|
||||
twalk(cod->hat_table, __process_policydb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __process_variables(const void *nodep, const VISIT value,
|
||||
const int __unused depth)
|
||||
{
|
||||
@@ -645,7 +726,6 @@ struct codomain *merge_policy(struct codomain *a, struct codomain *b)
|
||||
a->audit_caps |= b->audit_caps;
|
||||
a->deny_caps |= b->deny_caps;
|
||||
a->quiet_caps |= b->quiet_caps;
|
||||
a->set_caps |= b->set_caps;
|
||||
|
||||
if (a->network_allowed) {
|
||||
size_t i;
|
||||
@@ -707,6 +787,15 @@ int post_process_policy(int debug_only)
|
||||
}
|
||||
}
|
||||
|
||||
if (!debug_only) {
|
||||
retval = post_process_policydb();
|
||||
if (retval != 0) {
|
||||
PERROR(_("%s: Errors found during policydb postprocess. Aborting.\n"),
|
||||
progname);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -728,10 +817,15 @@ void free_policy(struct codomain *cod)
|
||||
return;
|
||||
free_hat_table(cod->hat_table);
|
||||
free_cod_entries(cod->entries);
|
||||
free_mnt_entries(cod->mnt_ents);
|
||||
if (cod->dfarules)
|
||||
aare_delete_ruleset(cod->dfarules);
|
||||
if (cod->dfa)
|
||||
free(cod->dfa);
|
||||
if (cod->policy_rules)
|
||||
aare_delete_ruleset(cod->policy_rules);
|
||||
if (cod->policy_dfa)
|
||||
free(cod->policy_dfa);
|
||||
if (cod->name)
|
||||
free(cod->name);
|
||||
if (cod->attachment)
|
||||
|
@@ -28,6 +28,8 @@
|
||||
#include "parser.h"
|
||||
#include "libapparmor_re/apparmor_re.h"
|
||||
#include "libapparmor_re/aare_rules.h"
|
||||
#include "mount.h"
|
||||
#include "policydb.h"
|
||||
|
||||
enum error_type {
|
||||
e_no_error,
|
||||
@@ -508,19 +510,28 @@ static int process_dfa_entry(aare_ruleset_t *dfarules, struct cod_entry *entry)
|
||||
return FALSE;
|
||||
}
|
||||
if (entry->mode & AA_CHANGE_PROFILE) {
|
||||
char *vec[3];
|
||||
char lbuf[PATH_MAX + 8];
|
||||
int index = 1;
|
||||
|
||||
/* allow change_profile for all execs */
|
||||
vec[0] = "/[^\\x00]*";
|
||||
|
||||
if (entry->namespace) {
|
||||
char *vec[2];
|
||||
char lbuf[PATH_MAX + 8];
|
||||
int pos;
|
||||
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, dfaflags))
|
||||
return FALSE;
|
||||
} else {
|
||||
if (!aare_add_rule(dfarules, tbuf, 0, AA_CHANGE_PROFILE, 0, dfaflags))
|
||||
return FALSE;
|
||||
vec[index++] = lbuf;
|
||||
}
|
||||
vec[index++] = tbuf;
|
||||
|
||||
/* regular change_profile rule */
|
||||
if (!aare_add_rule_vec(dfarules, 0, AA_CHANGE_PROFILE | AA_ONEXEC, 0, index - 1, &vec[1], dfaflags))
|
||||
return FALSE;
|
||||
/* onexec rules - both rules are needed for onexec */
|
||||
if (!aare_add_rule_vec(dfarules, 0, AA_ONEXEC, 0, 1, vec, dfaflags))
|
||||
return FALSE;
|
||||
if (!aare_add_rule_vec(dfarules, 0, AA_ONEXEC, 0, index, vec, dfaflags))
|
||||
return FALSE;
|
||||
}
|
||||
if (entry->mode & (AA_USER_PTRACE | AA_OTHER_PTRACE)) {
|
||||
int mode = entry->mode & (AA_USER_PTRACE | AA_OTHER_PTRACE);
|
||||
@@ -611,6 +622,479 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int build_list_val_expr(char *buffer, int size, struct value_list *list)
|
||||
{
|
||||
struct value_list *ent;
|
||||
char tmp[PATH_MAX + 3];
|
||||
char *p;
|
||||
int len;
|
||||
pattern_t ptype;
|
||||
int pos;
|
||||
|
||||
if (!list) {
|
||||
strncpy(buffer, "[^\\000]*", size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
p = buffer;
|
||||
strncpy(p, "(", size - (p - buffer));
|
||||
p++;
|
||||
if (p > buffer + size)
|
||||
goto fail;
|
||||
|
||||
ptype = convert_aaregex_to_pcre(list->value, 0, tmp, PATH_MAX+3, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
|
||||
len = strlen(tmp);
|
||||
if (len > size - (p - buffer))
|
||||
goto fail;
|
||||
strcpy(p, tmp);
|
||||
p += len;
|
||||
|
||||
list_for_each(list->next, ent) {
|
||||
ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
|
||||
PATH_MAX+3, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
|
||||
strncpy(p, "|", size - (p - buffer));
|
||||
p++;
|
||||
len = strlen(tmp);
|
||||
if (len > size - (p - buffer))
|
||||
goto fail;
|
||||
strcpy(p, tmp);
|
||||
p += len;
|
||||
}
|
||||
strncpy(p, ")", size - (p - buffer));
|
||||
p++;
|
||||
if (p > buffer + size)
|
||||
goto fail;
|
||||
|
||||
return TRUE;
|
||||
fail:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int convert_entry(char *buffer, int size, char *entry)
|
||||
{
|
||||
pattern_t ptype;
|
||||
int pos;
|
||||
|
||||
if (entry) {
|
||||
ptype = convert_aaregex_to_pcre(entry, 0, buffer, size, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
return FALSE;
|
||||
} else {
|
||||
/* match any char except \000 0 or more times */
|
||||
if (size < 8)
|
||||
return FALSE;
|
||||
|
||||
strcpy(buffer, "[^\\000]*");
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int build_mnt_flags(char *buffer, int size, unsigned int flags,
|
||||
unsigned int inv_flags)
|
||||
{
|
||||
char *p = buffer;
|
||||
int i, len = 0;
|
||||
|
||||
if (flags == MS_ALL_FLAGS) {
|
||||
/* all flags are optional */
|
||||
len = snprintf(p, size, "[^\\000]*");
|
||||
if (len < 0 || len >= size)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
for (i = 0; i <= 31; ++i) {
|
||||
if ((flags & inv_flags) & (1 << i))
|
||||
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 */
|
||||
continue;
|
||||
|
||||
if (len < 0 || len >= size)
|
||||
return FALSE;
|
||||
p += len;
|
||||
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
|
||||
*/
|
||||
if (size < 9)
|
||||
return FALSE;
|
||||
|
||||
strcpy(p, "(\\xfe|)");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int build_mnt_opts(char *buffer, int size, struct value_list *opts)
|
||||
{
|
||||
struct value_list *ent;
|
||||
char tmp[PATH_MAX + 3];
|
||||
char *p;
|
||||
int len;
|
||||
pattern_t ptype;
|
||||
int pos;
|
||||
|
||||
if (!opts) {
|
||||
if (size < 8)
|
||||
return FALSE;
|
||||
strncpy(buffer, "[^\\000]*", size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
p = buffer;
|
||||
list_for_each(opts, ent) {
|
||||
ptype = convert_aaregex_to_pcre(ent->value, 0, tmp,
|
||||
PATH_MAX+3, &pos);
|
||||
if (ptype == ePatternInvalid)
|
||||
goto fail;
|
||||
|
||||
len = strlen(tmp);
|
||||
if (len > size - (p - buffer))
|
||||
goto fail;
|
||||
strcpy(p, tmp);
|
||||
p += len;
|
||||
if (ent->next && size - (p - buffer) > 1) {
|
||||
*p++ = ',';
|
||||
*p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry)
|
||||
{
|
||||
char mntbuf[PATH_MAX + 3];
|
||||
char devbuf[PATH_MAX + 3];
|
||||
char typebuf[PATH_MAX + 3];
|
||||
char flagsbuf[PATH_MAX + 3];
|
||||
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
|
||||
*/
|
||||
|
||||
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 */
|
||||
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
|
||||
if (entry->mnt_point) {
|
||||
/* both device && mnt_point or just mnt_point */
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
} else {
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->device))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
}
|
||||
/* skip device */
|
||||
if (!convert_entry(devbuf, PATH_MAX +3, NULL))
|
||||
goto fail;
|
||||
vec[1] = devbuf;
|
||||
/* skip type */
|
||||
vec[2] = devbuf;
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
/* bind mount rules can't be conditional on dev_type or data */
|
||||
p = mntbuf;
|
||||
/* rule class single byte header */
|
||||
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
|
||||
goto fail;
|
||||
vec[1] = devbuf;
|
||||
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
|
||||
goto fail;
|
||||
vec[2] = typebuf;
|
||||
|
||||
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,
|
||||
entry->audit, 4, vec, dfaflags))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
if ((entry->allow & AA_MAY_MOUNT) &&
|
||||
(entry->flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED))
|
||||
&& !entry->device && !entry->dev_type && !entry->opts) {
|
||||
/* change type base rules can not be conditional on device,
|
||||
* device type or data
|
||||
*/
|
||||
p = mntbuf;
|
||||
/* rule class single byte header */
|
||||
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
/* skip device and type */
|
||||
if (!convert_entry(devbuf, PATH_MAX +3, NULL))
|
||||
goto fail;
|
||||
vec[1] = devbuf;
|
||||
vec[2] = devbuf;
|
||||
|
||||
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,
|
||||
entry->audit, 4, vec, dfaflags))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_MOVE)
|
||||
&& !entry->dev_type && !entry->opts) {
|
||||
/* mount move rules can not be conditional on dev_type,
|
||||
* or data
|
||||
*/
|
||||
p = mntbuf;
|
||||
/* rule class single byte header */
|
||||
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
|
||||
goto fail;
|
||||
vec[1] = devbuf;
|
||||
/* skip type */
|
||||
if (!convert_entry(typebuf, PATH_MAX +3, NULL))
|
||||
goto fail;
|
||||
vec[2] = typebuf;
|
||||
|
||||
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,
|
||||
entry->audit, 4, vec, dfaflags))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
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
|
||||
*/
|
||||
p = mntbuf;
|
||||
/* rule class single byte header */
|
||||
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
|
||||
goto fail;
|
||||
vec[1] = devbuf;
|
||||
if (!build_list_val_expr(typebuf, PATH_MAX+2, entry->dev_type))
|
||||
goto fail;
|
||||
vec[2] = typebuf;
|
||||
|
||||
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 (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;
|
||||
/* rule class single byte header */
|
||||
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
|
||||
entry->audit, 1, vec, dfaflags))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
if (entry->allow & AA_MAY_PIVOTROOT) {
|
||||
p = mntbuf;
|
||||
/* rule class single byte header */
|
||||
p += sprintf(p, "\\x%02x", AA_CLASS_MOUNT);
|
||||
if (!convert_entry(p, PATH_MAX +3, entry->mnt_point))
|
||||
goto fail;
|
||||
vec[0] = mntbuf;
|
||||
if (!convert_entry(devbuf, PATH_MAX +3, entry->device))
|
||||
goto fail;
|
||||
vec[1] = devbuf;
|
||||
if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow,
|
||||
entry->audit, 2, vec, dfaflags))
|
||||
goto fail;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
/* didn't actually encode anything */
|
||||
goto fail;
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
PERROR("Enocoding of mount rule failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
int post_process_policydb_ents(struct codomain *cod)
|
||||
{
|
||||
int ret = TRUE;
|
||||
int count = 0;
|
||||
|
||||
/* Add fns for rules that should be added to policydb here */
|
||||
if (cod->mnt_ents && kernel_supports_mount) {
|
||||
struct mnt_entry *entry;
|
||||
list_for_each(cod->mnt_ents, entry) {
|
||||
if (regex_type == AARE_DFA &&
|
||||
!process_mnt_entry(cod->policy_rules, entry))
|
||||
ret = FALSE;
|
||||
count++;
|
||||
}
|
||||
} else if (cod->mnt_ents && !kernel_supports_mount)
|
||||
pwarn("profile %s mount rules not enforced\n", cod->name);
|
||||
|
||||
cod->policy_rule_count = count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int process_policydb(struct codomain *cod)
|
||||
{
|
||||
int error = -1;
|
||||
|
||||
if (regex_type == AARE_DFA) {
|
||||
cod->policy_rules = aare_new_ruleset(0);
|
||||
if (!cod->policy_rules)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!post_process_policydb_ents(cod))
|
||||
goto out;
|
||||
|
||||
if (regex_type == AARE_DFA && cod->policy_rule_count > 0) {
|
||||
cod->policy_dfa = aare_create_dfa(cod->policy_rules,
|
||||
&cod->policy_dfa_size,
|
||||
dfaflags);
|
||||
aare_delete_ruleset(cod->policy_rules);
|
||||
cod->policy_rules = NULL;
|
||||
if (!cod->policy_dfa)
|
||||
goto out;
|
||||
}
|
||||
|
||||
aare_reset_matchflags();
|
||||
if (process_hat_policydb(cod) != 0)
|
||||
goto out;
|
||||
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
void reset_regex(void)
|
||||
{
|
||||
aare_reset_matchflags();
|
||||
|
@@ -28,6 +28,7 @@
|
||||
/* #define DEBUG */
|
||||
|
||||
#include "parser.h"
|
||||
#include "mount.h"
|
||||
|
||||
static inline char *get_var_end(char *var)
|
||||
{
|
||||
@@ -130,17 +131,19 @@ void free_var_string(struct var_string *var)
|
||||
free(var);
|
||||
}
|
||||
|
||||
static int expand_entry_variables(struct cod_entry *entry)
|
||||
/* doesn't handle variables in options atm */
|
||||
static int expand_entry_variables(char **name, void *entry,
|
||||
int (dup_and_chain)(void *))
|
||||
{
|
||||
struct set_value *valuelist;
|
||||
int ret = TRUE;
|
||||
char *value;
|
||||
struct var_string *split_var;
|
||||
|
||||
if (!entry) /* shouldn't happen */
|
||||
if (!entry) /* can happen when entry is optional */
|
||||
return ret;
|
||||
|
||||
while ((split_var = split_out_var(entry->name))) {
|
||||
while ((split_var = split_out_var(*name))) {
|
||||
valuelist = get_set_var(split_var->var);
|
||||
if (!valuelist) {
|
||||
int boolean = get_boolean_var(split_var->var);
|
||||
@@ -159,24 +162,22 @@ static int expand_entry_variables(struct cod_entry *entry)
|
||||
split_var->var);
|
||||
exit(1);
|
||||
}
|
||||
free(entry->name);
|
||||
if (asprintf(&(entry->name), "%s%s%s",
|
||||
free(*name);
|
||||
if (asprintf(name, "%s%s%s",
|
||||
split_var->prefix ? split_var->prefix : "",
|
||||
value,
|
||||
split_var->suffix ? split_var->suffix : "") == -1)
|
||||
return FALSE;
|
||||
|
||||
while ((value = get_next_set_value(&valuelist))) {
|
||||
struct cod_entry *dupe = copy_cod_entry(entry);
|
||||
if (!dupe) {
|
||||
PERROR("Memory allocaton error while handling set variable %s\n",
|
||||
if (!dup_and_chain(entry)) {
|
||||
PERROR("Memory allocation error while handling set variable %s\n",
|
||||
split_var->var);
|
||||
exit(1);
|
||||
}
|
||||
entry->next = dupe;
|
||||
|
||||
free(entry->name);
|
||||
if (asprintf(&(entry->name), "%s%s%s",
|
||||
free(*name);
|
||||
if (asprintf(name, "%s%s%s",
|
||||
split_var->prefix ? split_var->prefix : "", value,
|
||||
split_var->suffix ? split_var->suffix : "") == -1)
|
||||
return FALSE;
|
||||
@@ -187,15 +188,66 @@ static int expand_entry_variables(struct cod_entry *entry)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int clone_and_chain_cod(void *v)
|
||||
{
|
||||
struct cod_entry *entry = v;
|
||||
struct cod_entry *dup = copy_cod_entry(entry);
|
||||
if (!dup)
|
||||
return 0;
|
||||
|
||||
entry->next = dup;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int clone_and_chain_mnt(void *v)
|
||||
{
|
||||
struct mnt_entry *entry = v;
|
||||
|
||||
struct mnt_entry *dup = dup_mnt_entry(entry);
|
||||
if (!dup)
|
||||
return 0;
|
||||
|
||||
entry->next = dup;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int process_variables_in_entries(struct cod_entry *entry_list)
|
||||
{
|
||||
int ret = TRUE, rc;
|
||||
struct cod_entry *entry;
|
||||
|
||||
list_for_each(entry_list, entry) {
|
||||
rc = expand_entry_variables(entry);
|
||||
rc = expand_entry_variables(&entry->name, entry,
|
||||
clone_and_chain_cod);
|
||||
if (!rc)
|
||||
ret = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* does not currently support expansion of vars in options */
|
||||
static int process_variables_in_mnt_entries(struct mnt_entry *entry_list)
|
||||
{
|
||||
int ret = TRUE, rc;
|
||||
struct mnt_entry *entry;
|
||||
|
||||
list_for_each(entry_list, entry) {
|
||||
rc = expand_entry_variables(&entry->mnt_point, entry,
|
||||
clone_and_chain_mnt);
|
||||
if (!rc)
|
||||
return FALSE;
|
||||
rc = expand_entry_variables(&entry->device, entry,
|
||||
clone_and_chain_mnt);
|
||||
if (!rc)
|
||||
return FALSE;
|
||||
rc = expand_entry_variables(&entry->trans, entry,
|
||||
clone_and_chain_mnt);
|
||||
if (!rc)
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -209,6 +261,10 @@ int process_variables(struct codomain *cod)
|
||||
error = -1;
|
||||
}
|
||||
|
||||
if (!process_variables_in_mnt_entries(cod->mnt_ents)) {
|
||||
error = -1;
|
||||
}
|
||||
|
||||
if (process_hat_variables(cod) != 0) {
|
||||
error = -1;
|
||||
}
|
||||
|
@@ -2,8 +2,8 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
* NOVELL (All rights reserved)
|
||||
* Copyright (c) 2010
|
||||
* Canonical, Ltd.
|
||||
* Copyright (c) 2010-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
|
||||
@@ -32,6 +32,7 @@
|
||||
/* #define DEBUG */
|
||||
|
||||
#include "parser.h"
|
||||
#include "mount.h"
|
||||
#include "parser_include.h"
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -64,21 +65,23 @@
|
||||
|
||||
#define CAP_TO_MASK(x) (1ull << (x))
|
||||
|
||||
struct value_list {
|
||||
char *value;
|
||||
struct value_list *next;
|
||||
};
|
||||
int parser_token = 0;
|
||||
|
||||
void free_value_list(struct value_list *list);
|
||||
struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
|
||||
char *link_id, char *nt);
|
||||
struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src,
|
||||
struct cond_entry *dst_conds, char *dst,
|
||||
int mode);
|
||||
struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root,
|
||||
char *transition);
|
||||
|
||||
void add_local_entry(struct codomain *cod);
|
||||
|
||||
%}
|
||||
|
||||
%token TOK_ID
|
||||
%token TOK_SEP
|
||||
%token TOK_CONDID
|
||||
%token TOK_CARET
|
||||
%token TOK_OPEN
|
||||
%token TOK_CLOSE
|
||||
%token TOK_MODE
|
||||
@@ -110,6 +113,15 @@ void add_local_entry(struct codomain *cod);
|
||||
%token TOK_SET
|
||||
%token TOK_ALIAS
|
||||
%token TOK_PTRACE
|
||||
%token TOK_OPENPAREN
|
||||
%token TOK_CLOSEPAREN
|
||||
%token TOK_COMMA
|
||||
%token TOK_FILE
|
||||
%token TOK_MOUNT
|
||||
%token TOK_REMOUNT
|
||||
%token TOK_UMOUNT
|
||||
%token TOK_PIVOTROOT
|
||||
%token TOK_IN
|
||||
|
||||
/* rlimits */
|
||||
%token TOK_RLIMIT
|
||||
@@ -136,10 +148,6 @@ void add_local_entry(struct codomain *cod);
|
||||
|
||||
/* debug flag values */
|
||||
%token TOK_FLAGS
|
||||
%token TOK_FLAG_OPENPAREN
|
||||
%token TOK_FLAG_CLOSEPAREN
|
||||
%token TOK_FLAG_SEP
|
||||
%token TOK_FLAG_ID
|
||||
|
||||
%union {
|
||||
char *id;
|
||||
@@ -149,6 +157,8 @@ void add_local_entry(struct codomain *cod);
|
||||
struct codomain *cod;
|
||||
struct cod_net_entry *net_entry;
|
||||
struct cod_entry *user_entry;
|
||||
struct mnt_entry *mnt_entry;
|
||||
|
||||
struct flagval flags;
|
||||
int fmode;
|
||||
uint64_t cap;
|
||||
@@ -157,11 +167,13 @@ void add_local_entry(struct codomain *cod);
|
||||
char *bool_var;
|
||||
char *var_val;
|
||||
struct value_list *val_list;
|
||||
struct cond_entry *cond_entry;
|
||||
int boolean;
|
||||
struct named_transition transition;
|
||||
}
|
||||
|
||||
%type <id> TOK_ID
|
||||
%type <id> TOK_CONDID
|
||||
%type <mode> TOK_MODE
|
||||
%type <fmode> file_mode
|
||||
%type <cod> profile_base
|
||||
@@ -172,14 +184,19 @@ void add_local_entry(struct codomain *cod);
|
||||
%type <cod> cond_rule
|
||||
%type <network_entry> network_rule
|
||||
%type <user_entry> rule
|
||||
%type <user_entry> file_rule
|
||||
%type <user_entry> file_rule_tail
|
||||
%type <user_entry> link_rule
|
||||
%type <user_entry> ptrace_rule
|
||||
%type <user_entry> frule
|
||||
%type <mnt_entry> mnt_rule
|
||||
%type <cond_entry> opt_conds
|
||||
%type <cond_entry> cond
|
||||
%type <flags> flags
|
||||
%type <flags> flagvals
|
||||
%type <flags> flagval
|
||||
%type <flag_id> TOK_FLAG_ID
|
||||
%type <cap> caps
|
||||
%type <cap> capability
|
||||
%type <cap> set_caps
|
||||
%type <user_entry> change_profile
|
||||
%type <set_var> TOK_SET_VAR
|
||||
%type <bool_var> TOK_BOOL_VAR
|
||||
@@ -191,10 +208,12 @@ void add_local_entry(struct codomain *cod);
|
||||
%type <boolean> opt_audit_flag
|
||||
%type <boolean> opt_owner_flag
|
||||
%type <boolean> opt_profile_flag
|
||||
%type <boolean> opt_flags
|
||||
%type <id> opt_namespace
|
||||
%type <id> opt_id
|
||||
%type <transition> opt_named_transition
|
||||
%type <boolean> opt_unsafe
|
||||
%type <boolean> opt_file
|
||||
%%
|
||||
|
||||
|
||||
@@ -238,7 +257,8 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE
|
||||
if (force_complain)
|
||||
cod->flags.complain = 1;
|
||||
|
||||
post_process_nt_entries(cod);
|
||||
post_process_file_entries(cod);
|
||||
post_process_mnt_entries(cod);
|
||||
PDEBUG("%s: flags='%s%s'\n",
|
||||
$2,
|
||||
cod->flags.complain ? "complain, " : "",
|
||||
@@ -369,26 +389,23 @@ varassign: TOK_BOOL_VAR TOK_EQUALS TOK_VALUE
|
||||
|
||||
valuelist: TOK_VALUE
|
||||
{
|
||||
struct value_list *new = calloc(1, sizeof(struct value_list));
|
||||
if (!new)
|
||||
struct value_list *val = new_value_list($1);
|
||||
if (!val)
|
||||
yyerror(_("Memory allocation error."));
|
||||
PDEBUG("Matched: value (%s)\n", $1);
|
||||
|
||||
new->value = $1;
|
||||
new->next = NULL;
|
||||
$$ = new;
|
||||
$$ = val;
|
||||
}
|
||||
|
||||
valuelist: valuelist TOK_VALUE
|
||||
{
|
||||
struct value_list *new = calloc(1, sizeof(struct value_list));
|
||||
if (!new)
|
||||
struct value_list *val = new_value_list($2);
|
||||
if (!val)
|
||||
yyerror(_("Memory allocation error."));
|
||||
PDEBUG("Matched: value list\n");
|
||||
|
||||
new->value = $2;
|
||||
new->next = $1;
|
||||
$$ = new;
|
||||
list_append($1, val);
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
flags: { /* nothing */
|
||||
@@ -397,21 +414,24 @@ flags: { /* nothing */
|
||||
$$ = fv;
|
||||
};
|
||||
|
||||
flags: TOK_FLAGS TOK_EQUALS TOK_FLAG_OPENPAREN flagvals TOK_FLAG_CLOSEPAREN
|
||||
opt_flags: { /* nothing */ $$ = 0; }
|
||||
| TOK_CONDID TOK_EQUALS
|
||||
{
|
||||
$$ = $4;
|
||||
};
|
||||
|
||||
flags: TOK_FLAG_OPENPAREN flagvals TOK_FLAG_CLOSEPAREN
|
||||
{
|
||||
$$ = $2;
|
||||
if (strcmp($1, "flags") != 0)
|
||||
yyerror("expected flags= got %s=", $1);
|
||||
$$ = 1;
|
||||
}
|
||||
|
||||
flagvals: flagvals TOK_FLAG_SEP flagval
|
||||
flags: opt_flags TOK_OPENPAREN flagvals TOK_CLOSEPAREN
|
||||
{
|
||||
$1.complain = $1.complain || $3.complain;
|
||||
$1.audit = $1.audit || $3.audit;
|
||||
$1.path = $1.path | $3.path;
|
||||
$$ = $3;
|
||||
};
|
||||
|
||||
flagvals: flagvals flagval
|
||||
{
|
||||
$1.complain = $1.complain || $2.complain;
|
||||
$1.audit = $1.audit || $2.audit;
|
||||
$1.path = $1.path | $2.path;
|
||||
if (($1.path & (PATH_CHROOT_REL | PATH_NS_REL)) ==
|
||||
(PATH_CHROOT_REL | PATH_NS_REL))
|
||||
yyerror(_("Profile flag chroot_relative conflicts with namespace_relative"));
|
||||
@@ -434,7 +454,7 @@ flagvals: flagval
|
||||
$$ = $1;
|
||||
};
|
||||
|
||||
flagval: TOK_FLAG_ID
|
||||
flagval: TOK_VALUE
|
||||
{
|
||||
struct flagval fv = { 0, 0, 0, 0 };
|
||||
if (strcmp($1, "debug") == 0) {
|
||||
@@ -641,6 +661,25 @@ rules: rules opt_audit_flag network_rule
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
rules: rules opt_audit_flag TOK_DENY mnt_rule
|
||||
{
|
||||
$4->deny = $4->allow;
|
||||
if ($2)
|
||||
$4->audit = $4->allow;
|
||||
$4->next = $1->mnt_ents;
|
||||
$1->mnt_ents = $4;
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
rules: rules opt_audit_flag mnt_rule
|
||||
{
|
||||
if ($2)
|
||||
$3->audit = $3->allow;
|
||||
$3->next = $1->mnt_ents;
|
||||
$1->mnt_ents = $3;
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
rules: rules change_profile
|
||||
{
|
||||
PDEBUG("matched: rules change_profile\n");
|
||||
@@ -667,12 +706,6 @@ rules: rules opt_audit_flag capability
|
||||
$$ = $1;
|
||||
};
|
||||
|
||||
rules: rules set_caps
|
||||
{
|
||||
$1->set_caps |= $2;
|
||||
$$ = $1;
|
||||
};
|
||||
|
||||
rules: rules hat
|
||||
{
|
||||
PDEBUG("Matched: hat rule\n");
|
||||
@@ -892,24 +925,16 @@ opt_named_transition:
|
||||
$$.name = $5;
|
||||
};
|
||||
|
||||
rule: file_rule { $$ = $1; }
|
||||
| link_rule { $$ = $1; }
|
||||
| ptrace_rule {$$ = $1; }
|
||||
|
||||
opt_unsafe: { /* nothing */ $$ = 0; }
|
||||
| TOK_UNSAFE { $$ = 1; };
|
||||
| TOK_SAFE { $$ = 2; };
|
||||
|
||||
rule: opt_unsafe frule
|
||||
{
|
||||
if ($1) {
|
||||
if (!($2->mode & AA_EXEC_BITS))
|
||||
yyerror(_("unsafe rule missing exec permissions"));
|
||||
if ($1 == 1) {
|
||||
$2->mode |= (($2->mode & AA_EXEC_BITS) << 8) &
|
||||
ALL_AA_EXEC_UNSAFE;
|
||||
}
|
||||
else if ($1 == 2)
|
||||
$2->mode &= ~ALL_AA_EXEC_UNSAFE;
|
||||
}
|
||||
$$ = $2;
|
||||
};
|
||||
opt_file: { /* nothing */ $$ = 0; }
|
||||
| TOK_FILE { $$ = 1; }
|
||||
|
||||
frule: id_or_var file_mode opt_named_transition TOK_END_OF_RULE
|
||||
{
|
||||
@@ -933,16 +958,45 @@ frule: file_mode opt_subset_flag id_or_var opt_named_transition TOK_END_OF_RULE
|
||||
}
|
||||
};
|
||||
|
||||
rule: opt_unsafe id_or_var file_mode id_or_var
|
||||
file_rule: TOK_FILE TOK_END_OF_RULE
|
||||
{
|
||||
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, perms, NULL, NULL);
|
||||
}
|
||||
| opt_file file_rule_tail { $$ = $2; }
|
||||
|
||||
|
||||
file_rule_tail: opt_unsafe frule
|
||||
{
|
||||
if ($1) {
|
||||
if (!($2->mode & AA_EXEC_BITS))
|
||||
yyerror(_("unsafe rule missing exec permissions"));
|
||||
if ($1 == 1) {
|
||||
$2->mode |= (($2->mode & AA_EXEC_BITS) << 8) &
|
||||
ALL_AA_EXEC_UNSAFE;
|
||||
}
|
||||
else if ($1 == 2)
|
||||
$2->mode &= ~ALL_AA_EXEC_UNSAFE;
|
||||
}
|
||||
$$ = $2;
|
||||
};
|
||||
|
||||
file_rule_tail: opt_unsafe id_or_var file_mode id_or_var
|
||||
{
|
||||
/* Oopsie, we appear to be missing an EOL marker. If we
|
||||
* were *smart*, we could work around it. Since we're
|
||||
* obviously not smart, we'll just punt with a more
|
||||
* sensible error. */
|
||||
yyerror(_("missing an end of line character? (entry: %s)"), $1);
|
||||
yyerror(_("missing an end of line character? (entry: %s)"), $2);
|
||||
};
|
||||
|
||||
rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
|
||||
link_rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
PDEBUG("Matched: link tok_id (%s) -> (%s)\n", $3, $5);
|
||||
@@ -952,7 +1006,7 @@ rule: TOK_LINK opt_subset_flag TOK_ID TOK_ARROW TOK_ID TOK_END_OF_RULE
|
||||
$$ = entry;
|
||||
};
|
||||
|
||||
rule: TOK_PTRACE TOK_ID TOK_END_OF_RULE
|
||||
ptrace_rule: TOK_PTRACE TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
entry = new_entry(NULL, $2, AA_USER_PTRACE | AA_OTHER_PTRACE, NULL);
|
||||
@@ -961,7 +1015,7 @@ rule: TOK_PTRACE TOK_ID TOK_END_OF_RULE
|
||||
$$ = entry;
|
||||
};
|
||||
|
||||
rule: TOK_PTRACE TOK_COLON TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE
|
||||
ptrace_rule: TOK_PTRACE TOK_COLON TOK_ID TOK_COLON TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
struct cod_entry *entry;
|
||||
entry = new_entry($3, $5, AA_USER_PTRACE | AA_OTHER_PTRACE, NULL);
|
||||
@@ -1009,7 +1063,86 @@ network_rule: TOK_NETWORK TOK_ID TOK_ID TOK_END_OF_RULE
|
||||
$$ = entry;
|
||||
}
|
||||
|
||||
hat_start: TOK_SEP {}
|
||||
cond: TOK_CONDID TOK_EQUALS TOK_VALUE
|
||||
{
|
||||
struct cond_entry *ent;
|
||||
struct value_list *value = new_value_list($3);
|
||||
if (!value)
|
||||
yyerror(_("Memory allocation error."));
|
||||
ent = new_cond_entry($1, 1, value);
|
||||
if (!ent) {
|
||||
free_value_list(value);
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
$$ = ent;
|
||||
}
|
||||
|
||||
cond: TOK_CONDID TOK_EQUALS TOK_OPENPAREN valuelist TOK_CLOSEPAREN
|
||||
{
|
||||
struct cond_entry *ent = new_cond_entry($1, 1, $4);
|
||||
|
||||
if (!ent)
|
||||
yyerror(_("Memory allocation error."));
|
||||
$$ = ent;
|
||||
}
|
||||
|
||||
|
||||
cond: TOK_CONDID TOK_IN TOK_OPENPAREN valuelist TOK_CLOSEPAREN
|
||||
{
|
||||
struct cond_entry *ent = new_cond_entry($1, 0, $4);
|
||||
|
||||
if (!ent)
|
||||
yyerror(_("Memory allocation error."));
|
||||
$$ = ent;
|
||||
}
|
||||
|
||||
opt_conds: { /* nothing */ $$ = NULL; }
|
||||
| opt_conds cond
|
||||
{
|
||||
$2->next = $1;
|
||||
$$ = $2;
|
||||
}
|
||||
|
||||
mnt_rule: TOK_MOUNT opt_conds opt_id TOK_END_OF_RULE
|
||||
{
|
||||
$$ = do_mnt_rule($2, $3, NULL, NULL, AA_MAY_MOUNT);
|
||||
}
|
||||
|
||||
mnt_rule: TOK_MOUNT opt_conds opt_id TOK_ARROW opt_conds TOK_ID TOK_END_OF_RULE
|
||||
{
|
||||
$$ = do_mnt_rule($2, $3, $5, $6, AA_MAY_MOUNT);
|
||||
}
|
||||
|
||||
mnt_rule: TOK_REMOUNT opt_conds opt_id TOK_END_OF_RULE
|
||||
{
|
||||
$$ = do_mnt_rule($2, NULL, NULL, $3, AA_DUMMY_REMOUNT);
|
||||
}
|
||||
|
||||
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 opt_named_transition TOK_END_OF_RULE
|
||||
{
|
||||
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;
|
||||
|
||||
$$ = do_pivot_rule($2, $3, name);
|
||||
}
|
||||
|
||||
hat_start: TOK_CARET {}
|
||||
| TOK_HAT {}
|
||||
|
||||
file_mode: TOK_MODE
|
||||
@@ -1043,17 +1176,17 @@ change_profile: TOK_CHANGE_PROFILE TOK_ARROW TOK_COLON TOK_ID TOK_COLON TOK_ID T
|
||||
};
|
||||
|
||||
|
||||
set_caps: TOK_SET TOK_CAPABILITY caps TOK_END_OF_RULE
|
||||
{
|
||||
$$ = $3;
|
||||
};
|
||||
|
||||
capability: TOK_CAPABILITY caps TOK_END_OF_RULE
|
||||
{
|
||||
$$ = $2;
|
||||
if ($2 == 0) {
|
||||
/* bare capability keyword - set all caps */
|
||||
$$ = 0xffffffffffffffff;
|
||||
} else
|
||||
$$ = $2;
|
||||
};
|
||||
|
||||
caps: caps TOK_ID
|
||||
caps: { /* nothing */ $$ = 0; }
|
||||
| caps TOK_ID
|
||||
{
|
||||
int cap = name_to_capability($2);
|
||||
if (cap == -1)
|
||||
@@ -1062,26 +1195,14 @@ caps: caps TOK_ID
|
||||
$$ = $1 | CAP_TO_MASK(cap);
|
||||
}
|
||||
|
||||
caps: TOK_ID
|
||||
{
|
||||
int cap = name_to_capability($1);
|
||||
if (cap == -1)
|
||||
yyerror(_("Invalid capability %s."), $1);
|
||||
free($1);
|
||||
$$ = CAP_TO_MASK(cap);
|
||||
};
|
||||
|
||||
%%
|
||||
#define MAXBUFSIZE 4096
|
||||
|
||||
void yyerror(char *msg, ...)
|
||||
void vprintyyerror(const char *msg, va_list argptr)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[MAXBUFSIZE];
|
||||
|
||||
va_start(arg, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, arg);
|
||||
va_end(arg);
|
||||
vsnprintf(buf, sizeof(buf), msg, argptr);
|
||||
|
||||
if (profilename) {
|
||||
PERROR(_("AppArmor parser error for %s%s%s at line %d: %s\n"),
|
||||
@@ -1095,21 +1216,26 @@ void yyerror(char *msg, ...)
|
||||
current_filename ? current_filename : "",
|
||||
current_lineno, buf);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void free_value_list(struct value_list *list)
|
||||
void printyyerror(const char *msg, ...)
|
||||
{
|
||||
struct value_list *next;
|
||||
va_list arg;
|
||||
|
||||
while (list) {
|
||||
next = list->next;
|
||||
if (list->value)
|
||||
free(list->value);
|
||||
free(list);
|
||||
list = next;
|
||||
}
|
||||
va_start(arg, msg);
|
||||
vprintyyerror(msg, arg);
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
void yyerror(const char *msg, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, msg);
|
||||
vprintyyerror(msg, arg);
|
||||
va_end(arg);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct cod_entry *do_file_rule(char *namespace, char *id, int mode,
|
||||
@@ -1149,3 +1275,76 @@ void add_local_entry(struct codomain *cod)
|
||||
add_entry_to_policy(cod, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static char *mnt_cond_msg[] = {"",
|
||||
" not allowed as source conditional",
|
||||
" not allowed as target conditional",
|
||||
"",
|
||||
NULL};
|
||||
|
||||
int verify_mnt_conds(struct cond_entry *conds, int src)
|
||||
{
|
||||
struct cond_entry *entry;
|
||||
int error = 0;
|
||||
|
||||
if (!conds)
|
||||
return 0;
|
||||
|
||||
list_for_each(conds, entry) {
|
||||
int res = is_valid_mnt_cond(entry->name, src);
|
||||
if (res <= 0) {
|
||||
printyyerror(_("invalid mount conditional %s%s"),
|
||||
entry->name,
|
||||
res == -1 ? "" : mnt_cond_msg[src]);
|
||||
error++;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src,
|
||||
struct cond_entry *dst_conds, char *dst,
|
||||
int mode)
|
||||
{
|
||||
struct mnt_entry *ent;
|
||||
|
||||
if (verify_mnt_conds(src_conds, MNT_SRC_OPT) != 0)
|
||||
yyerror(_("bad mount rule"));
|
||||
|
||||
/* FIXME: atm conditions are not supported on dst
|
||||
if (verify_conds(dst_conds, DST_OPT) != 0)
|
||||
yyerror(_("bad mount rule"));
|
||||
*/
|
||||
if (dst_conds)
|
||||
yyerror(_("mount point conditions not currently supported"));
|
||||
|
||||
ent = new_mnt_entry(src_conds, src, dst_conds, dst, mode);
|
||||
if (!ent) {
|
||||
yyerror(_("Memory allocation error."));
|
||||
}
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
||||
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, device, NULL, root,
|
||||
AA_MAY_PIVOTROOT);
|
||||
ent->trans = transition;
|
||||
|
||||
return ent;
|
||||
}
|
||||
|
38
parser/policydb.h
Normal file
38
parser/policydb.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012 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 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AA_POLICYDB_H
|
||||
#define __AA_POLICYDB_H
|
||||
|
||||
/*
|
||||
* Class of mediation types in the AppArmor policy db
|
||||
*/
|
||||
#define AA_CLASS_COND 0
|
||||
#define AA_CLASS_UNKNOWN 1
|
||||
#define AA_CLASS_FILE 2
|
||||
#define AA_CLASS_CAP 3
|
||||
#define AA_CLASS_NET 4
|
||||
#define AA_CLASS_RLIMITS 5
|
||||
#define AA_CLASS_DOMAIN 6
|
||||
#define AA_CLASS_MOUNT 7
|
||||
#define AA_CLASS_NS_DOMAIN 8
|
||||
#define AA_CLASS_PTRACE 9
|
||||
|
||||
#define AA_CLASS_ENV 16
|
||||
|
||||
#define AA_CLASS_DBUS 32
|
||||
#define AA_CLASS_X 33
|
||||
|
||||
#endif /* __AA_POLICYDB_H */
|
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------
|
||||
# Copyright (c) 1999-2008 NOVELL (All rights reserved)
|
||||
# Copyright (c) 2009-2011 Canonical Ltd. (All rights reserved)
|
||||
# Copyright (c) 2009-2012 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
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# 2008, 2009
|
||||
# NOVELL (All rights reserved)
|
||||
#
|
||||
# Copyright (c) 2010
|
||||
# Copyright (c) 2010 - 2012
|
||||
# Canonical Ltd. (All rights reserved)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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
199
parser/tst/minimize.sh
Executable 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"
|
9
parser/tst/simple_tests/capability/bad_3.sd
Normal file
9
parser/tst/simple_tests/capability/bad_3.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION fail CAP_XXX syntax.
|
||||
#=EXRESULT FAIL
|
||||
# vim:syntax=subdomain
|
||||
# Last Modified: Sun Apr 17 19:44:44 2005
|
||||
#
|
||||
/does/not/exist {
|
||||
capability chown CAP_CHOWN,
|
||||
}
|
9
parser/tst/simple_tests/capability/bad_4.sd
Normal file
9
parser/tst/simple_tests/capability/bad_4.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION fail unknown keyword
|
||||
#=EXRESULT FAIL
|
||||
# vim:syntax=subdomain
|
||||
# Last Modified: Sun Apr 17 19:44:44 2005
|
||||
#
|
||||
/does/not/exist {
|
||||
capability chown foobar,
|
||||
}
|
9
parser/tst/simple_tests/capability/ok3.sd
Normal file
9
parser/tst/simple_tests/capability/ok3.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION validate some uses of capabilties.
|
||||
#=EXRESULT PASS
|
||||
# vim:syntax=subdomain
|
||||
# Last Modified: Sun Apr 17 19:44:44 2005
|
||||
#
|
||||
/does/not/exist {
|
||||
capability,
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#
|
||||
#=DESCRIPTION validate some uses of capabilties.
|
||||
#=EXRESULT PASS
|
||||
#=EXRESULT FAIL
|
||||
# vim:syntax=subdomain
|
||||
# Last Modified: Sun Apr 17 19:44:44 2005
|
||||
#
|
||||
|
7
parser/tst/simple_tests/file/file/bad_1.sd
Normal file
7
parser/tst/simple_tests/file/file/bad_1.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=Description basic file rule
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file rw,
|
||||
}
|
8
parser/tst/simple_tests/file/file/bad_append_1.sd
Normal file
8
parser/tst/simple_tests/file/file/bad_append_1.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION w and a conflict
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /a wa,
|
||||
}
|
||||
|
7
parser/tst/simple_tests/file/file/bad_comma_1.sd
Normal file
7
parser/tst/simple_tests/file/file/bad_comma_1.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
k#
|
||||
#=DESCRIPTION comma in pathname
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /foobar, r,
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
#=DESCRIPTION Simple test case for embedded spaces
|
||||
#=EXRESULT FAIL
|
||||
|
||||
/bin/foo {
|
||||
file /abc\ def r,
|
||||
}
|
8
parser/tst/simple_tests/file/file/bad_lock_1.sd
Normal file
8
parser/tst/simple_tests/file/file/bad_lock_1.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION k to be lower case
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /bin/ls K,
|
||||
}
|
||||
|
41
parser/tst/simple_tests/file/file/dos_line_endings.sd
Normal file
41
parser/tst/simple_tests/file/file/dos_line_endings.sd
Normal file
@@ -0,0 +1,41 @@
|
||||
# vim:syntax=subdomain
|
||||
# Last Modified: Wed Aug 31 11:14:09 2005
|
||||
#=DESCRIPTION dos line endings
|
||||
#=EXRESULT PASS
|
||||
/usr/lib/RealPlayer10/realplay {
|
||||
#include <includes/base>
|
||||
#include <includes/fonts>
|
||||
|
||||
file /bin/bash ix,
|
||||
file /bin/sed ixr,
|
||||
file /bin/true ixr,
|
||||
file /etc/opt/gnome/pango/pango.modules r,
|
||||
file /opt/gnome/lib/gtk-2.0/2.4.0/loaders/* r,
|
||||
file /opt/gnome/lib/lib*so* r,
|
||||
file /opt/gnome/lib/pango/1.4.0/modules/* r,
|
||||
file /opt/gnome/share/icons r,
|
||||
file /opt/gnome/share/icons/** r,
|
||||
file /opt/gnome/bin/nautilus rux,
|
||||
file /root r,
|
||||
file /root/.Xauthority r,
|
||||
file /root/.fonts.cache-1 r,
|
||||
file /root/.realplayerrc rw,
|
||||
file /home/*/ r,
|
||||
file /home/*/.Xauthority r,
|
||||
file /home/*/.fonts.cache-1 r,
|
||||
file /home/*/.realplayerrc rw,
|
||||
file /usr/X11R6/lib/Acrobat7/Resource/Font/* r,
|
||||
file /usr/X11R6/lib/Acrobat7/Resource/Font/PFM/* r,
|
||||
file /usr/lib/RealPlayer10/** r,
|
||||
file /usr/lib/RealPlayer10/realplay.bin ixr,
|
||||
file /usr/lib/jvm/java-1.4.2-sun-1.4.2.06/jre/lib/fonts/** r,
|
||||
file /usr/lib/ooo-2.0/share/fonts/** r,
|
||||
file /opt/MozillaFirefox/bin/firefox.sh pxr,
|
||||
file /opt/MozillaFirefox/lib/firefox-bin pxr,
|
||||
file /opt/MozillaFirefox/lib/init.d r,
|
||||
file /usr/bin/opera pxr,
|
||||
file /usr/share/icons r,
|
||||
file /usr/share/icons/** r,
|
||||
file /opt/gnome/share/pixmaps r,
|
||||
file /opt/gnome/share/pixmaps/** r,
|
||||
}
|
24
parser/tst/simple_tests/file/file/front_perms_ok_1.sd
Normal file
24
parser/tst/simple_tests/file/file/front_perms_ok_1.sd
Normal file
@@ -0,0 +1,24 @@
|
||||
#
|
||||
#=DESCRIPTION perms before pathname
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
|
||||
file r /foo1,
|
||||
file w /foo1,
|
||||
file a /foo1,
|
||||
file k /foo1,
|
||||
file m /foo1,
|
||||
file l /foo1,
|
||||
file px /foo1,
|
||||
file Px /foo2,
|
||||
file ux /foo3,
|
||||
file Ux /foo4,
|
||||
file ix /foo5,
|
||||
file unsafe px /foo6,
|
||||
file unsafe Px /foo7,
|
||||
file unsafe ux /foo8,
|
||||
file unsafe Ux /foo9,
|
||||
file unsafe ix /foo10,
|
||||
|
||||
}
|
7
parser/tst/simple_tests/file/file/ok_1.sd
Normal file
7
parser/tst/simple_tests/file/file/ok_1.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=Description basic file rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /usr/bin/foo r,
|
||||
}
|
7
parser/tst/simple_tests/file/file/ok_2.sd
Normal file
7
parser/tst/simple_tests/file/file/ok_2.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=Description basic file rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file,
|
||||
}
|
9
parser/tst/simple_tests/file/file/ok_3.sd
Normal file
9
parser/tst/simple_tests/file/file/ok_3.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION A simple successful profile
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /usr/bin/foo r,
|
||||
file /usr/bin/blah rix,
|
||||
}
|
||||
|
13
parser/tst/simple_tests/file/file/ok_append_1.sd
Normal file
13
parser/tst/simple_tests/file/file/ok_append_1.sd
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
#=DESCRIPTION test append
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /bin/cat a,
|
||||
file /bin/true ra,
|
||||
file /bin/false ma,
|
||||
file /lib/libc.so la,
|
||||
file /bin/less ixa,
|
||||
file /bin/more pxa,
|
||||
file /a uxa,
|
||||
}
|
7
parser/tst/simple_tests/file/file/ok_carat_1.sd
Normal file
7
parser/tst/simple_tests/file/file/ok_carat_1.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=DESCRIPTION carat in pathname
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /foo^bar r,
|
||||
}
|
7
parser/tst/simple_tests/file/file/ok_carat_2.sd
Normal file
7
parser/tst/simple_tests/file/file/ok_carat_2.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=DESCRIPTION trailing carat in pathname
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /foo/bar^ r,
|
||||
}
|
7
parser/tst/simple_tests/file/file/ok_comma_1.sd
Normal file
7
parser/tst/simple_tests/file/file/ok_comma_1.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=DESCRIPTION comma in pathname
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /foo,bar r,
|
||||
}
|
7
parser/tst/simple_tests/file/file/ok_comma_2.sd
Normal file
7
parser/tst/simple_tests/file/file/ok_comma_2.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=DESCRIPTION comma at end of pathname
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file "/foobar," r,
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
#=DESCRIPTION Simple test case for embedded spaces
|
||||
#=EXRESULT PASS
|
||||
|
||||
/bin/foo {
|
||||
file "/abc\ def" r,
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
#=DESCRIPTION Simple test case for embedded spaces
|
||||
#=EXRESULT PASS
|
||||
|
||||
/bin/foo {
|
||||
file "/abc def" r,
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
#=DESCRIPTION Simple test case for embedded spaces
|
||||
#=EXRESULT PASS
|
||||
|
||||
"/bin/fo o" {
|
||||
file "/abc def" r,
|
||||
}
|
7
parser/tst/simple_tests/file/file/ok_inv_char_class.sd
Normal file
7
parser/tst/simple_tests/file/file/ok_inv_char_class.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=DESCRIPTION carat in pathname
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /foo[^me]bar r,
|
||||
}
|
17
parser/tst/simple_tests/file/file/ok_lock_1.sd
Normal file
17
parser/tst/simple_tests/file/file/ok_lock_1.sd
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
#=DESCRIPTION k and other perms do not conflict
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /bin/a k,
|
||||
file /bin/b rk,
|
||||
file /bin/c wk,
|
||||
file /bin/d ak,
|
||||
file /bin/e lk,
|
||||
file /bin/e mk,
|
||||
file /bin/f pxk,
|
||||
file /bin/g Pxk,
|
||||
file /bin/h ixk,
|
||||
file /bin/i uxk,
|
||||
file /bin/j Uxk,
|
||||
}
|
12
parser/tst/simple_tests/file/file/ok_mmap_1.sd
Normal file
12
parser/tst/simple_tests/file/file/ok_mmap_1.sd
Normal file
@@ -0,0 +1,12 @@
|
||||
#
|
||||
#=DESCRIPTION m and [uUpPi]x do not conflict
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /bin/cat mix,
|
||||
file /bin/true mpx,
|
||||
file /bin/false mux,
|
||||
file /lib/libc.so rwlm,
|
||||
file /bin/less mUx,
|
||||
file /bin/more mPx,
|
||||
}
|
14
parser/tst/simple_tests/file/file/ok_mmap_2.sd
Normal file
14
parser/tst/simple_tests/file/file/ok_mmap_2.sd
Normal file
@@ -0,0 +1,14 @@
|
||||
#
|
||||
#=DESCRIPTION m and [upi]x do not conflict, seperate rules
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /bin/cat rm,
|
||||
file /bin/cat ix,
|
||||
file /bin/true px,
|
||||
file /bin/true m,
|
||||
file /bin/false m,
|
||||
file /bin/false ux,
|
||||
file /lib/libc.so rwl,
|
||||
file /lib/libc.so m,
|
||||
}
|
8
parser/tst/simple_tests/file/file/owner/bad_3.sd
Normal file
8
parser/tst/simple_tests/file/file/owner/bad_3.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION owner can not follow path name
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /foo owner rw,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/file/file/owner/bad_4.sd
Normal file
8
parser/tst/simple_tests/file/file/owner/bad_4.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION owner cannot follow permission
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file /foo rw owner,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/file/file/owner/bad_5.sd
Normal file
8
parser/tst/simple_tests/file/file/owner/bad_5.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION owner rules must have comma termination
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
owner file /foo rw
|
||||
file /bar rw,
|
||||
}
|
8
parser/tst/simple_tests/file/file/owner/bad_6.sd
Normal file
8
parser/tst/simple_tests/file/file/owner/bad_6.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION owner not allowed after forward perm
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file rw owner /foo,
|
||||
|
||||
}
|
8
parser/tst/simple_tests/file/file/owner/bad_7.sd
Normal file
8
parser/tst/simple_tests/file/file/owner/bad_7.sd
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
#=DESCRIPTION owner not allowed after pathname in forward rule
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
file rw /foo owner,
|
||||
|
||||
}
|
9
parser/tst/simple_tests/file/file/owner/bad_8.sd
Normal file
9
parser/tst/simple_tests/file/file/owner/bad_8.sd
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
#=DESCRIPTION owner block needs } termination
|
||||
#=EXRESULT FAIL
|
||||
#
|
||||
/usr/bin/foo {
|
||||
owner {
|
||||
file rw foo,
|
||||
|
||||
}
|
25
parser/tst/simple_tests/file/file/owner/ok_1.sd
Normal file
25
parser/tst/simple_tests/file/file/owner/ok_1.sd
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
#=DESCRIPTION test owner flag for file rules
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
|
||||
owner file /foo rw,
|
||||
owner file /foo/** rw,
|
||||
|
||||
owner file rw /bar,
|
||||
owner file rw /bar/**,
|
||||
|
||||
owner {
|
||||
file /one rw,
|
||||
file /one/** rw,
|
||||
|
||||
file rw /two,
|
||||
file rw /two/**,
|
||||
}
|
||||
|
||||
owner {
|
||||
|
||||
}
|
||||
|
||||
}
|
7
parser/tst/simple_tests/mount/in_1.sd
Normal file
7
parser/tst/simple_tests/mount/in_1.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=Description basic mount rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
mount options in (rw) -> /foo,
|
||||
}
|
7
parser/tst/simple_tests/mount/in_2.sd
Normal file
7
parser/tst/simple_tests/mount/in_2.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=Description basic mount rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
mount options in (rw, ro) -> /foo,
|
||||
}
|
7
parser/tst/simple_tests/mount/in_3.sd
Normal file
7
parser/tst/simple_tests/mount/in_3.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=Description basic mount rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
mount options in (rw ro) -> /foo,
|
||||
}
|
7
parser/tst/simple_tests/mount/in_4.sd
Normal file
7
parser/tst/simple_tests/mount/in_4.sd
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
#=Description basic mount rule
|
||||
#=EXRESULT PASS
|
||||
#
|
||||
/usr/bin/foo {
|
||||
mount options in (rw ro) fstype=procfs -> /foo,
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user