mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 18:17:09 +00:00
Merge ipc message queue rule support
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/858 Approved-by: John Johansen <john@jjmx.net> Merged-by: John Johansen <john@jjmx.net>
This commit is contained in:
commit
b5f558962f
5
.gitignore
vendored
5
.gitignore
vendored
@ -39,6 +39,7 @@ parser/libapparmor_re/hfa.o
|
|||||||
parser/libapparmor_re/libapparmor_re.a
|
parser/libapparmor_re/libapparmor_re.a
|
||||||
parser/libapparmor_re/parse.o
|
parser/libapparmor_re/parse.o
|
||||||
parser/mount.o
|
parser/mount.o
|
||||||
|
parser/mqueue.o
|
||||||
parser/network.o
|
parser/network.o
|
||||||
parser/parser_alias.o
|
parser/parser_alias.o
|
||||||
parser/parser_common.o
|
parser/parser_common.o
|
||||||
@ -265,6 +266,8 @@ tests/regression/apparmor/open
|
|||||||
tests/regression/apparmor/openat
|
tests/regression/apparmor/openat
|
||||||
tests/regression/apparmor/pipe
|
tests/regression/apparmor/pipe
|
||||||
tests/regression/apparmor/pivot_root
|
tests/regression/apparmor/pivot_root
|
||||||
|
tests/regression/apparmor/posix_mq_rcv
|
||||||
|
tests/regression/apparmor/posix_mq_snd
|
||||||
tests/regression/apparmor/ptrace
|
tests/regression/apparmor/ptrace
|
||||||
tests/regression/apparmor/ptrace_helper
|
tests/regression/apparmor/ptrace_helper
|
||||||
tests/regression/apparmor/pwrite
|
tests/regression/apparmor/pwrite
|
||||||
@ -288,6 +291,8 @@ tests/regression/apparmor/syscall_setpriority
|
|||||||
tests/regression/apparmor/syscall_setscheduler
|
tests/regression/apparmor/syscall_setscheduler
|
||||||
tests/regression/apparmor/syscall_sysctl
|
tests/regression/apparmor/syscall_sysctl
|
||||||
tests/regression/apparmor/sysctl_proc
|
tests/regression/apparmor/sysctl_proc
|
||||||
|
tests/regression/apparmor/sysv_mq_rcv
|
||||||
|
tests/regression/apparmor/sysv_mq_snd
|
||||||
tests/regression/apparmor/tcp
|
tests/regression/apparmor/tcp
|
||||||
tests/regression/apparmor/transition
|
tests/regression/apparmor/transition
|
||||||
tests/regression/apparmor/unix_fd_client
|
tests/regression/apparmor/unix_fd_client
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
Apr 05 19:36:19 ubuntu kernel: audit: type=1400 audit(1649187379.660:255): apparmor="DENIED" operation="create" profile="/root/apparmor/tests/regression/apparmor/posix_mq_rcv" name="/queuename" pid=791 comm="posix_mq_rcv" requested="create" denied="create" class="posix_mqueue" fsuid=0 ouid=0
|
@ -0,0 +1,16 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_01.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1649187379.660:255
|
||||||
|
Operation: create
|
||||||
|
Mask: create
|
||||||
|
Denied Mask: create
|
||||||
|
fsuid: 0
|
||||||
|
ouid: 0
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/posix_mq_rcv
|
||||||
|
Name: /queuename
|
||||||
|
Command: posix_mq_rcv
|
||||||
|
PID: 791
|
||||||
|
Class: posix_mqueue
|
||||||
|
Epoch: 1649187379
|
||||||
|
Audit subid: 255
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/posix_mq_rcv {
|
||||||
|
mqueue create type=posix /queuename,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
Apr 05 19:36:29 ubuntu kernel: audit: type=1400 audit(1649187389.828:262): apparmor="DENIED" operation="open" profile="/root/apparmor/tests/regression/apparmor/posix_mq_rcv" name="/queuename" pid=848 comm="posix_mq_rcv" requested="read create" denied="read" class="posix_mqueue" fsuid=0 ouid=0
|
||||||
|
|
@ -0,0 +1,16 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_02.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1649187389.828:262
|
||||||
|
Operation: open
|
||||||
|
Mask: read create
|
||||||
|
Denied Mask: read
|
||||||
|
fsuid: 0
|
||||||
|
ouid: 0
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/posix_mq_rcv
|
||||||
|
Name: /queuename
|
||||||
|
Command: posix_mq_rcv
|
||||||
|
PID: 848
|
||||||
|
Class: posix_mqueue
|
||||||
|
Epoch: 1649187389
|
||||||
|
Audit subid: 262
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/posix_mq_rcv {
|
||||||
|
mqueue read type=posix /queuename,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Apr 05 19:36:39 ubuntu kernel: audit: type=1400 audit(1649187399.973:265): apparmor="DENIED" operation="unlink" profile="/root/apparmor/tests/regression/apparmor/posix_mq_rcv" name="/queuename" pid=897 comm="posix_mq_rcv" requested="delete" denied="delete" class="posix_mqueue" fsuid=0 ouid=0
|
@ -0,0 +1,16 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_03.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1649187399.973:265
|
||||||
|
Operation: unlink
|
||||||
|
Mask: delete
|
||||||
|
Denied Mask: delete
|
||||||
|
fsuid: 0
|
||||||
|
ouid: 0
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/posix_mq_rcv
|
||||||
|
Name: /queuename
|
||||||
|
Command: posix_mq_rcv
|
||||||
|
PID: 897
|
||||||
|
Class: posix_mqueue
|
||||||
|
Epoch: 1649187399
|
||||||
|
Audit subid: 265
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/posix_mq_rcv {
|
||||||
|
mqueue delete type=posix /queuename,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Jun 02 16:58:20 ubuntu kernel: audit: type=1400 audit(1654189100.680:1011): apparmor="DENIED" operation="sysv_mqueue" profile="/root/apparmor/tests/regression/apparmor/sysv_mq_rcv" name="123" pid=13574 comm="sysv_mq_rcv" requested="create" denied="create" class="sysv_mqueue" fsuid=0 ouid=0
|
@ -0,0 +1,16 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_04.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1654189100.680:1011
|
||||||
|
Operation: sysv_mqueue
|
||||||
|
Mask: create
|
||||||
|
Denied Mask: create
|
||||||
|
fsuid: 0
|
||||||
|
ouid: 0
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/sysv_mq_rcv
|
||||||
|
Name: 123
|
||||||
|
Command: sysv_mq_rcv
|
||||||
|
PID: 13574
|
||||||
|
Class: sysv_mqueue
|
||||||
|
Epoch: 1654189100
|
||||||
|
Audit subid: 1011
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/sysv_mq_rcv {
|
||||||
|
mqueue create type=sysv 123,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Jun 02 17:15:45 ubuntu kernel: audit: type=1400 audit(1654190145.439:1135): apparmor="DENIED" operation="sysv_mqueue" profile="/root/apparmor/tests/regression/apparmor/sysv_mq_snd" name="123" pid=15849 comm="sysv_mq_snd" requested="open" denied="open" class="sysv_mqueue"
|
@ -0,0 +1,14 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_05.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1654190145.439:1135
|
||||||
|
Operation: sysv_mqueue
|
||||||
|
Mask: open
|
||||||
|
Denied Mask: open
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/sysv_mq_snd
|
||||||
|
Name: 123
|
||||||
|
Command: sysv_mq_snd
|
||||||
|
PID: 15849
|
||||||
|
Class: sysv_mqueue
|
||||||
|
Epoch: 1654190145
|
||||||
|
Audit subid: 1135
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/sysv_mq_snd {
|
||||||
|
mqueue open type=sysv 123,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Jun 02 17:15:37 ubuntu kernel: audit: type=1400 audit(1654190137.559:1122): apparmor="DENIED" operation="sysv_mqueue" profile="/root/apparmor/tests/regression/apparmor/sysv_mq_rcv" name="123" pid=15632 comm="sysv_mq_rcv" requested="read" denied="read" class="sysv_mqueue" fsuid=0 ouid=0
|
@ -0,0 +1,16 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_06.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1654190137.559:1122
|
||||||
|
Operation: sysv_mqueue
|
||||||
|
Mask: read
|
||||||
|
Denied Mask: read
|
||||||
|
fsuid: 0
|
||||||
|
ouid: 0
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/sysv_mq_rcv
|
||||||
|
Name: 123
|
||||||
|
Command: sysv_mq_rcv
|
||||||
|
PID: 15632
|
||||||
|
Class: sysv_mqueue
|
||||||
|
Epoch: 1654190137
|
||||||
|
Audit subid: 1122
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/sysv_mq_rcv {
|
||||||
|
mqueue read type=sysv 123,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Jun 02 17:15:51 ubuntu kernel: audit: type=1400 audit(1654190151.003:1145): apparmor="DENIED" operation="sysv_mqueue" profile="/root/apparmor/tests/regression/apparmor/sysv_mq_rcv" name="123" pid=15973 comm="sysv_mq_rcv" requested="delete" denied="delete" class="sysv_mqueue" fsuid=1001 ouid=1001
|
@ -0,0 +1,16 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_07.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1654190151.003:1145
|
||||||
|
Operation: sysv_mqueue
|
||||||
|
Mask: delete
|
||||||
|
Denied Mask: delete
|
||||||
|
fsuid: 1001
|
||||||
|
ouid: 1001
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/sysv_mq_rcv
|
||||||
|
Name: 123
|
||||||
|
Command: sysv_mq_rcv
|
||||||
|
PID: 15973
|
||||||
|
Class: sysv_mqueue
|
||||||
|
Epoch: 1654190151
|
||||||
|
Audit subid: 1145
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/sysv_mq_rcv {
|
||||||
|
mqueue delete type=sysv 123,
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Jun 02 17:15:55 ubuntu kernel: audit: type=1400 audit(1654190155.699:1155): apparmor="DENIED" operation="sysv_mqueue" profile="/root/apparmor/tests/regression/apparmor/sysv_mq_snd" name="123" pid=16148 comm="sysv_mq_snd" requested="write" denied="write" class="sysv_mqueue" fsuid=1001 ouid=1001
|
@ -0,0 +1,16 @@
|
|||||||
|
START
|
||||||
|
File: testcase_mqueue_08.in
|
||||||
|
Event type: AA_RECORD_DENIED
|
||||||
|
Audit ID: 1654190155.699:1155
|
||||||
|
Operation: sysv_mqueue
|
||||||
|
Mask: write
|
||||||
|
Denied Mask: write
|
||||||
|
fsuid: 1001
|
||||||
|
ouid: 1001
|
||||||
|
Profile: /root/apparmor/tests/regression/apparmor/sysv_mq_snd
|
||||||
|
Name: 123
|
||||||
|
Command: sysv_mq_snd
|
||||||
|
PID: 16148
|
||||||
|
Class: sysv_mqueue
|
||||||
|
Epoch: 1654190155
|
||||||
|
Audit subid: 1155
|
@ -0,0 +1,4 @@
|
|||||||
|
/root/apparmor/tests/regression/apparmor/sysv_mq_snd {
|
||||||
|
mqueue write type=sysv 123,
|
||||||
|
|
||||||
|
}
|
@ -101,10 +101,11 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
|
|||||||
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
|
parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
|
||||||
parser_alias.c common_optarg.c lib.c network.c \
|
parser_alias.c common_optarg.c lib.c network.c \
|
||||||
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
|
mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
|
||||||
af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc
|
af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc \
|
||||||
|
mqueue.cc
|
||||||
HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \
|
HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h \
|
||||||
rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h \
|
rule.h common_optarg.h signal.h ptrace.h network.h af_rule.h af_unix.h \
|
||||||
policy_cache.h file_cache.h userns.h
|
policy_cache.h file_cache.h userns.h mqueue.h
|
||||||
TOOLS = apparmor_parser
|
TOOLS = apparmor_parser
|
||||||
|
|
||||||
OBJECTS = $(patsubst %.cc, %.o, $(SRCS:.c=.o))
|
OBJECTS = $(patsubst %.cc, %.o, $(SRCS:.c=.o))
|
||||||
@ -304,6 +305,9 @@ rule.o: rule.cc rule.h policydb.h
|
|||||||
userns.o: userns.cc userns.h parser.h parser_yacc.h rule.h $(APPARMOR_H)
|
userns.o: userns.cc userns.h parser.h parser_yacc.h rule.h $(APPARMOR_H)
|
||||||
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
mqueue.o: mqueue.cc mqueue.h parser.h immunix.h profile.h parser_yacc.h rule.h $(APPARMOR_H)
|
||||||
|
$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
parser_version.h: Makefile
|
parser_version.h: Makefile
|
||||||
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
|
@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
|
||||||
@mv -f .ver $@
|
@mv -f .ver $@
|
||||||
|
@ -123,7 +123,7 @@ B<RULES> = [ ( I<LINE RULES> | I<COMMA RULES> ',' | I<BLOCK RULES> )
|
|||||||
|
|
||||||
B<LINE RULES> = ( I<COMMENT> | I<INCLUDE> ) [ '\r' ] '\n'
|
B<LINE RULES> = ( I<COMMENT> | I<INCLUDE> ) [ '\r' ] '\n'
|
||||||
|
|
||||||
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<DBUS RULE> )
|
B<COMMA RULES> = ( I<CAPABILITY RULE> | I<NETWORK RULE> | I<MOUNT RULE> | I<PIVOT ROOT RULE> | I<UNIX RULE> | I<FILE RULE> | I<LINK RULE> | I<CHANGE_PROFILE RULE> | I<RLIMIT RULE> | I<DBUS RULE> | I<MQUEUE RULE> )
|
||||||
|
|
||||||
B<BLOCK RULES> = ( I<SUBPROFILE> | I<HAT> | I<QUALIFIER BLOCK> )
|
B<BLOCK RULES> = ( I<SUBPROFILE> | I<HAT> | I<QUALIFIER BLOCK> )
|
||||||
|
|
||||||
@ -176,6 +176,20 @@ B<MOUNT FLAGS> = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | 'noexec'
|
|||||||
|
|
||||||
B<MOUNT EXPRESSION> = ( I<ALPHANUMERIC> | I<AARE> ) ...
|
B<MOUNT EXPRESSION> = ( I<ALPHANUMERIC> | I<AARE> ) ...
|
||||||
|
|
||||||
|
B<MQUEUE_RULE> = [ I<QUALIFIERS> ] 'mqueue' [ I<MQUEUE ACCESS PERMISSIONS> ] [ I<MQUEUE TYPE> ] [ I<MQUEUE LABEL> ] [ I<MQUEUE NAME> ]
|
||||||
|
|
||||||
|
B<MQUEUE ACCESS PERMISSIONS> = I<MQUEUE ACCESS> | I<MQUEUE ACCESS LIST>
|
||||||
|
|
||||||
|
B<MQUEUE ACCESS LIST> = '(' Comma or space separated list of I<MQUEUE ACCESS> ')'
|
||||||
|
|
||||||
|
B<MQUEUE ACCESS> = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'create' | 'open' | 'delete' | 'getattr' | 'setattr' )
|
||||||
|
|
||||||
|
B<MQUEUE TYPE> = 'type' '=' ( 'posix' | 'sysv' )
|
||||||
|
|
||||||
|
B<MQUEUE LABEL> = 'label' '=' '(' '"' I<AARE> '"' | I<AARE> ')'
|
||||||
|
|
||||||
|
B<MQUEUE NAME> = I<AARE>
|
||||||
|
|
||||||
B<PIVOT ROOT RULE> = [ I<QUALIFIERS> ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ '-E<gt>' I<PROFILE NAME> ]
|
B<PIVOT ROOT RULE> = [ I<QUALIFIERS> ] pivot_root [ oldroot=I<OLD PUT FILEGLOB> ] [ I<NEW ROOT FILEGLOB> ] [ '-E<gt>' I<PROFILE NAME> ]
|
||||||
|
|
||||||
B<SOURCE FILEGLOB> = I<FILEGLOB>
|
B<SOURCE FILEGLOB> = I<FILEGLOB>
|
||||||
@ -1057,6 +1071,51 @@ Matches only:
|
|||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
=head2 Message Queue rules
|
||||||
|
|
||||||
|
AppArmor supports mediation of POSIX and SYSV message queues.
|
||||||
|
|
||||||
|
AppArmor Message Queue permissions are implied when a rule does not explicitly
|
||||||
|
state an access list. By default, all Message Queue permissions are implied.
|
||||||
|
|
||||||
|
AppArmor Message Queue permissions become more restricted as further information
|
||||||
|
is specified. Policy can be specified by determining its access mode, type,
|
||||||
|
label, and message queue name.
|
||||||
|
|
||||||
|
Regarding access modes, 'r' and 'read' are used to read messages from the queue.
|
||||||
|
'w' and 'write' are used to write to the message queue. 'create' is used to create
|
||||||
|
the message queue, and 'open' is used to get the message queue identifier when the
|
||||||
|
queue is already created. 'delete' is used to remove the message queue. The access
|
||||||
|
modes to get and set attributes of the message queue are 'setattr' and 'getattr'.
|
||||||
|
|
||||||
|
The type of the policy can be either 'posix' or 'sysv'. This information is
|
||||||
|
relevant when the message queue name is not specified, and when specified can be
|
||||||
|
inferred by the queue name, since message queues' name for posix must start with '/',
|
||||||
|
and message queues' key for SYSV must be a positive integer.
|
||||||
|
|
||||||
|
The policy label is the label assigned to the message queue when it is created.
|
||||||
|
|
||||||
|
The message queue name can be either a string starting with '/' if the type
|
||||||
|
is POSIX, or a positive integer if the type is SYSV. If the type is not
|
||||||
|
specified, then it will be inferred by the queue name.
|
||||||
|
|
||||||
|
Example AppArmor Message Queue rules:
|
||||||
|
|
||||||
|
# Allow all Message Queue access
|
||||||
|
mqueue,
|
||||||
|
|
||||||
|
# Explicitly allow all Message Queue access,
|
||||||
|
mqueue (create, open, delete, read, write, getattr, setattr),
|
||||||
|
|
||||||
|
# Explicitly deny use of Message Queue
|
||||||
|
deny mqueue,
|
||||||
|
|
||||||
|
# Allow all access for POSIX queue of name /bar
|
||||||
|
mqueue type=posix /bar,
|
||||||
|
|
||||||
|
# Allow create permission for a SYSV queue of label foo
|
||||||
|
mqueue create label=foo 123,
|
||||||
|
|
||||||
=head2 Pivot Root Rules
|
=head2 Pivot Root Rules
|
||||||
|
|
||||||
AppArmor mediates changing of the root filesystem through the pivot_root(2)
|
AppArmor mediates changing of the root filesystem through the pivot_root(2)
|
||||||
|
278
parser/mqueue.cc
Normal file
278
parser/mqueue.cc
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
#include "profile.h"
|
||||||
|
#include "mqueue.h"
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
int parse_mqueue_mode(const char *str_mode, int *mode, int fail)
|
||||||
|
{
|
||||||
|
return parse_X_mode("mqueue", AA_VALID_MQUEUE_PERMS, str_mode, mode, fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_all_digits(char *str)
|
||||||
|
{
|
||||||
|
const char *s = str;
|
||||||
|
while (*str && isdigit(*str))
|
||||||
|
str++;
|
||||||
|
return str != s && *str == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqueue_rule::validate_qname(void)
|
||||||
|
{
|
||||||
|
if (qname[0] == '/') {
|
||||||
|
// TODO full syntax check of name
|
||||||
|
if (qtype == mqueue_sysv)
|
||||||
|
yyerror("mqueue type=sysv invalid name '%s', sysv "
|
||||||
|
"message queues must be identified by a "
|
||||||
|
"positive integer.\n", qname);
|
||||||
|
qtype = mqueue_posix; // implied by name
|
||||||
|
} else if (is_all_digits(qname)) {
|
||||||
|
if (qtype == mqueue_posix)
|
||||||
|
yyerror("mqueue type=posix invalid name '%s', posix "
|
||||||
|
"message queues names must begin with a /\n",
|
||||||
|
qname);
|
||||||
|
qtype = mqueue_sysv; // implied
|
||||||
|
} else {
|
||||||
|
yyerror("mqueue invalid name '%s', message queue names must begin with a / or be a positive integer.\n", qname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqueue_rule::move_conditionals(struct cond_entry *conds)
|
||||||
|
{
|
||||||
|
struct cond_entry *cond_ent;
|
||||||
|
|
||||||
|
list_for_each(conds, cond_ent) {
|
||||||
|
/* for now disallow keyword 'in' (list) */
|
||||||
|
if (!cond_ent->eq)
|
||||||
|
yyerror("keyword \"in\" is not allowed in mqueue rules\n");
|
||||||
|
|
||||||
|
if (strcmp(cond_ent->name, "label") == 0) {
|
||||||
|
move_conditional_value("mqueue", &label, cond_ent);
|
||||||
|
} else if (strcmp(cond_ent->name, "type") == 0) {
|
||||||
|
char *tmp = NULL;
|
||||||
|
move_conditional_value("mqueue", &tmp, cond_ent);
|
||||||
|
if (strcmp(tmp, "posix") == 0)
|
||||||
|
qtype = mqueue_posix;
|
||||||
|
else if (strcmp(tmp, "sysv") == 0)
|
||||||
|
qtype = mqueue_sysv;
|
||||||
|
else
|
||||||
|
yyerror("mqueue invalid type='%s'\n", tmp);
|
||||||
|
free(tmp);
|
||||||
|
} else {
|
||||||
|
yyerror("invalid mqueue rule conditional \"%s\"\n",
|
||||||
|
cond_ent->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mqueue_rule::mqueue_rule(int mode_p, struct cond_entry *conds, char *qname_p):
|
||||||
|
qtype(mqueue_unspecified), qname(qname_p), label(NULL), audit(0), deny(0)
|
||||||
|
{
|
||||||
|
move_conditionals(conds);
|
||||||
|
free_cond_list(conds);
|
||||||
|
|
||||||
|
if (qname)
|
||||||
|
validate_qname();
|
||||||
|
if (mode_p) {
|
||||||
|
// do we want to allow perms to imply type like we do for
|
||||||
|
// qname?
|
||||||
|
if (qtype == mqueue_posix && (mode_p & ~AA_VALID_POSIX_MQ_PERMS)) {
|
||||||
|
yyerror("mode contains invalid permissions for mqueue type=posix\n");
|
||||||
|
} else if (qtype == mqueue_sysv && (mode_p & ~AA_VALID_SYSV_MQ_PERMS)) {
|
||||||
|
yyerror("mode contains invalid permissions for mqueue type=sysv\n");
|
||||||
|
} else if (mode_p & ~AA_VALID_MQUEUE_PERMS) {
|
||||||
|
yyerror("mode contains invalid permissions for mqueue\n");
|
||||||
|
}
|
||||||
|
mode = mode_p;
|
||||||
|
} else {
|
||||||
|
// default to all perms
|
||||||
|
mode = AA_VALID_MQUEUE_PERMS;
|
||||||
|
}
|
||||||
|
qname = qname_p;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ostream &mqueue_rule::dump(ostream &os)
|
||||||
|
{
|
||||||
|
if (audit)
|
||||||
|
os << "audit ";
|
||||||
|
if (deny)
|
||||||
|
os << "deny ";
|
||||||
|
|
||||||
|
os << "mqueue ";
|
||||||
|
|
||||||
|
// do we want to always put type out or leave it implied if there
|
||||||
|
// is a qname
|
||||||
|
if (qtype == mqueue_posix)
|
||||||
|
os << "type=posix";
|
||||||
|
else if (qtype == mqueue_sysv)
|
||||||
|
os << "type=sysv";
|
||||||
|
|
||||||
|
if (mode != AA_VALID_MQUEUE_PERMS) {
|
||||||
|
os << "(";
|
||||||
|
|
||||||
|
if (mode & AA_MQUEUE_WRITE)
|
||||||
|
os << "write ";
|
||||||
|
if (mode & AA_MQUEUE_READ)
|
||||||
|
os << "read ";
|
||||||
|
if (mode & AA_MQUEUE_OPEN)
|
||||||
|
os << "open ";
|
||||||
|
if (mode & AA_MQUEUE_CREATE)
|
||||||
|
os << "create ";
|
||||||
|
if (mode & AA_MQUEUE_DELETE)
|
||||||
|
os << "delete ";
|
||||||
|
if (mode & AA_MQUEUE_SETATTR)
|
||||||
|
os << "setattr ";
|
||||||
|
if (mode & AA_MQUEUE_GETATTR)
|
||||||
|
os << "getattr ";
|
||||||
|
|
||||||
|
os << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qname)
|
||||||
|
os << " " << qname;
|
||||||
|
|
||||||
|
os << ",\n";
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mqueue_rule::expand_variables(void)
|
||||||
|
{
|
||||||
|
int error = expand_entry_variables(&qname);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
error = expand_entry_variables(&label);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: this is not right, need separate warning for each type */
|
||||||
|
void mqueue_rule::warn_once(const char *name)
|
||||||
|
{
|
||||||
|
if (qtype == mqueue_unspecified)
|
||||||
|
rule_t::warn_once(name, "mqueue rules not enforced");
|
||||||
|
else if (qtype == mqueue_posix)
|
||||||
|
rule_t::warn_once(name, "mqueue type=posix rules not enforced");
|
||||||
|
else if (qtype == mqueue_sysv)
|
||||||
|
rule_t::warn_once(name, "mqueue type=sysv rules not enforced");
|
||||||
|
}
|
||||||
|
|
||||||
|
int mqueue_rule::gen_policy_re(Profile &prof)
|
||||||
|
{
|
||||||
|
std::string labelbuf;
|
||||||
|
std::string buf;
|
||||||
|
const int size = 2;
|
||||||
|
const char *vec[size];
|
||||||
|
|
||||||
|
|
||||||
|
if (qtype == mqueue_posix && !features_supports_posix_mqueue) {
|
||||||
|
warn_once(prof.name);
|
||||||
|
return RULE_NOT_SUPPORTED;
|
||||||
|
} else if (qtype == mqueue_sysv && !features_supports_sysv_mqueue) {
|
||||||
|
warn_once(prof.name);
|
||||||
|
// return RULE_NOT_SUPPORTED;
|
||||||
|
} else if (qtype == mqueue_unspecified &&
|
||||||
|
!(features_supports_posix_mqueue ||
|
||||||
|
features_supports_sysv_mqueue)) {
|
||||||
|
warn_once(prof.name);
|
||||||
|
// should split into warning where posix and sysv can
|
||||||
|
// be separated from nothing being enforced
|
||||||
|
// return RULE_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
prof.flags.flags |= FLAG_DEBUG1;
|
||||||
|
/* always generate a label and mqueue entry */
|
||||||
|
|
||||||
|
//buffer << "(" << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_LABEL << "|)"; //is this required?
|
||||||
|
|
||||||
|
// posix and generic
|
||||||
|
if (qtype != mqueue_sysv) {
|
||||||
|
std::ostringstream buffer;
|
||||||
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_POSIX_MQUEUE;
|
||||||
|
buf.assign(buffer.str());
|
||||||
|
if (qname) {
|
||||||
|
if (!convert_entry(buf, qname))
|
||||||
|
goto fail;
|
||||||
|
} else {
|
||||||
|
buf += default_match_pattern;
|
||||||
|
}
|
||||||
|
vec[0] = buf.c_str();
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
if (!convert_entry(labelbuf, label))
|
||||||
|
goto fail;
|
||||||
|
vec[1] = labelbuf.c_str();
|
||||||
|
} else {
|
||||||
|
vec[1] = anyone_match_pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode & AA_VALID_POSIX_MQ_PERMS) {
|
||||||
|
/* store perms at name match so label doesn't need
|
||||||
|
* to be checked
|
||||||
|
*/
|
||||||
|
if (!label && !prof.policy.rules->add_rule_vec(deny, mode, audit, 1, vec, dfaflags, false))
|
||||||
|
goto fail;
|
||||||
|
/* also provide label match with perm */
|
||||||
|
if (!prof.policy.rules->add_rule_vec(deny, mode, audit, size, vec, dfaflags, false))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sysv and generic
|
||||||
|
if (qtype != mqueue_posix) {
|
||||||
|
std::ostringstream buffer;
|
||||||
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_SYSV_MQUEUE;
|
||||||
|
buf.assign(buffer.str());
|
||||||
|
if (qname) {
|
||||||
|
if (!convert_entry(buf, qname))
|
||||||
|
goto fail;
|
||||||
|
} else {
|
||||||
|
buf += default_match_pattern;
|
||||||
|
}
|
||||||
|
vec[0] = buf.c_str();
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
if (!convert_entry(labelbuf, label))
|
||||||
|
goto fail;
|
||||||
|
vec[1] = labelbuf.c_str();
|
||||||
|
} else {
|
||||||
|
vec[1] = anyone_match_pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode & AA_VALID_SYSV_MQ_PERMS) {
|
||||||
|
if (!label && !prof.policy.rules->add_rule_vec(deny, mode, audit, 1, vec, dfaflags, false))
|
||||||
|
goto fail;
|
||||||
|
/* also provide label match with perm */
|
||||||
|
if (!prof.policy.rules->add_rule_vec(deny, mode, audit, size, vec, dfaflags, false))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RULE_OK;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
return RULE_ERROR;
|
||||||
|
}
|
111
parser/mqueue.h
Normal file
111
parser/mqueue.h
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* sysv and posix mqueue mediation. */
|
||||||
|
|
||||||
|
#ifndef __AA_MQUEUE_H
|
||||||
|
#define __AA_MQUEUE_H
|
||||||
|
|
||||||
|
#include "immunix.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#define AA_MQUEUE_WRITE AA_MAY_WRITE
|
||||||
|
#define AA_MQUEUE_READ AA_MAY_READ
|
||||||
|
|
||||||
|
#define AA_MQUEUE_CREATE 0x0010 /* create */
|
||||||
|
#define AA_MQUEUE_DELETE 0x0020 /* destroy, unlink */
|
||||||
|
#define AA_MQUEUE_OPEN 0x0040 /* associate */
|
||||||
|
#define AA_MQUEUE_RENAME 0x0080 /* ?? pair */
|
||||||
|
|
||||||
|
#define AA_MQUEUE_SETATTR 0x0100 /* setattr */
|
||||||
|
#define AA_MQUEUE_GETATTR 0x0200 /* getattr */
|
||||||
|
|
||||||
|
#define AA_MQUEUE_CHMOD 0x1000 /* pair */
|
||||||
|
#define AA_MQUEUE_CHOWN 0x2000 /* pair */
|
||||||
|
#define AA_MQUEUE_CHGRP 0x4000 /* pair */
|
||||||
|
#define AA_MQUEUE_LOCK 0x8000 /* LINK_SUBSET overlaid */
|
||||||
|
|
||||||
|
/* sysv and posix mqueues use different terminology, allow mapping
|
||||||
|
* between. To be as common as possible.
|
||||||
|
*
|
||||||
|
* sysv and posix mqueues have different levels of mediation possible
|
||||||
|
* in the kernel. Only the most basic mqueue rules can be shared
|
||||||
|
* eg.
|
||||||
|
* mqueue rw,
|
||||||
|
* mqueue rw label=foo,
|
||||||
|
*
|
||||||
|
* kernel doesn't allow for us to control
|
||||||
|
* - posix
|
||||||
|
* - notify
|
||||||
|
* - getattr/setattr
|
||||||
|
* - labels at anything other than mqueue label, via mqueue inode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define AA_VALID_POSIX_MQ_PERMS (AA_MQUEUE_WRITE | AA_MQUEUE_READ | \
|
||||||
|
AA_MQUEUE_CREATE | AA_MQUEUE_DELETE | \
|
||||||
|
AA_MQUEUE_OPEN)
|
||||||
|
|
||||||
|
/* TBD - for now make it wider than posix */
|
||||||
|
#define AA_VALID_SYSV_MQ_PERMS (AA_MQUEUE_WRITE | AA_MQUEUE_READ | \
|
||||||
|
AA_MQUEUE_CREATE | AA_MQUEUE_DELETE | \
|
||||||
|
AA_MQUEUE_OPEN | \
|
||||||
|
AA_MQUEUE_SETATTR | AA_MQUEUE_GETATTR)
|
||||||
|
|
||||||
|
#define AA_VALID_MQUEUE_PERMS (AA_VALID_POSIX_MQ_PERMS | \
|
||||||
|
AA_VALID_SYSV_MQ_PERMS)
|
||||||
|
|
||||||
|
// warning getting into overlap area
|
||||||
|
|
||||||
|
/* Type of mqueue - can be explicit or implied by rule id/path */
|
||||||
|
typedef enum mqueue_type {
|
||||||
|
mqueue_unspecified,
|
||||||
|
mqueue_posix,
|
||||||
|
mqueue_sysv
|
||||||
|
} mqueue_type;
|
||||||
|
|
||||||
|
|
||||||
|
int parse_mqueue_mode(const char *str_mode, int *mode, int fail);
|
||||||
|
|
||||||
|
class mqueue_rule: public rule_t {
|
||||||
|
void move_conditionals(struct cond_entry *conds);
|
||||||
|
public:
|
||||||
|
mqueue_type qtype;
|
||||||
|
char *qname;
|
||||||
|
char *label;
|
||||||
|
int mode;
|
||||||
|
int audit;
|
||||||
|
int deny;
|
||||||
|
|
||||||
|
mqueue_rule(int mode, struct cond_entry *conds, char *qname = NULL);
|
||||||
|
virtual ~mqueue_rule()
|
||||||
|
{
|
||||||
|
free(qname);
|
||||||
|
free(label);
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ostream &dump(ostream &os);
|
||||||
|
virtual int expand_variables(void);
|
||||||
|
virtual int gen_policy_re(Profile &prof);
|
||||||
|
virtual void post_process(Profile &prof unused) { };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void warn_once(const char *name) override;
|
||||||
|
void validate_qname(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __AA_MQUEUE_H */
|
@ -345,6 +345,8 @@ extern int features_supports_unix;
|
|||||||
extern int features_supports_stacking;
|
extern int features_supports_stacking;
|
||||||
extern int features_supports_domain_xattr;
|
extern int features_supports_domain_xattr;
|
||||||
extern int features_supports_userns;
|
extern int features_supports_userns;
|
||||||
|
extern int features_supports_posix_mqueue;
|
||||||
|
extern int features_supports_sysv_mqueue;
|
||||||
extern int kernel_supports_oob;
|
extern int kernel_supports_oob;
|
||||||
extern int conf_verbose;
|
extern int conf_verbose;
|
||||||
extern int conf_quiet;
|
extern int conf_quiet;
|
||||||
|
@ -79,6 +79,8 @@ int features_supports_ptrace = 0; /* kernel supports ptrace rules */
|
|||||||
int features_supports_stacking = 0; /* kernel supports stacking */
|
int features_supports_stacking = 0; /* kernel supports stacking */
|
||||||
int features_supports_domain_xattr = 0; /* x attachment cond */
|
int features_supports_domain_xattr = 0; /* x attachment cond */
|
||||||
int features_supports_userns = 0; /* kernel supports user namespace */
|
int features_supports_userns = 0; /* kernel supports user namespace */
|
||||||
|
int features_supports_posix_mqueue = 0; /* kernel supports mqueue rules */
|
||||||
|
int features_supports_sysv_mqueue = 0; /* kernel supports mqueue rules */
|
||||||
int kernel_supports_oob = 0; /* out of band transitions */
|
int kernel_supports_oob = 0; /* out of band transitions */
|
||||||
int conf_verbose = 0;
|
int conf_verbose = 0;
|
||||||
int conf_quiet = 0;
|
int conf_quiet = 0;
|
||||||
|
@ -420,7 +420,7 @@ void sd_serialize_profile(std::ostringstream &buf, Profile *profile,
|
|||||||
|
|
||||||
sd_write_struct(buf, "flags");
|
sd_write_struct(buf, "flags");
|
||||||
/* used to be flags.debug, but that's no longer supported */
|
/* used to be flags.debug, but that's no longer supported */
|
||||||
sd_write_uint32(buf, profile->flags.hat);
|
sd_write_uint32(buf, profile->flags.flags);
|
||||||
sd_write_uint32(buf, profile_mode_packed(profile->flags.mode));
|
sd_write_uint32(buf, profile_mode_packed(profile->flags.mode));
|
||||||
sd_write_uint32(buf, profile->flags.audit);
|
sd_write_uint32(buf, profile->flags.audit);
|
||||||
sd_write_structend(buf);
|
sd_write_structend(buf);
|
||||||
|
@ -328,6 +328,7 @@ GT >
|
|||||||
%x INCLUDE_EXISTS
|
%x INCLUDE_EXISTS
|
||||||
%x ABI_MODE
|
%x ABI_MODE
|
||||||
%x USERNS_MODE
|
%x USERNS_MODE
|
||||||
|
%x MQUEUE_MODE
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
@ -340,7 +341,7 @@ GT >
|
|||||||
}
|
}
|
||||||
%}
|
%}
|
||||||
|
|
||||||
<INITIAL,SUB_ID_WS,INCLUDE,INCLUDE_EXISTS,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE>{
|
<INITIAL,SUB_ID_WS,INCLUDE,INCLUDE_EXISTS,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE,MQUEUE_MODE>{
|
||||||
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
|
{WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +377,7 @@ GT >
|
|||||||
yyterminate();
|
yyterminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
|
<INITIAL,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE>{
|
||||||
(peer|xattrs)/{WS}*={WS}*\( {
|
(peer|xattrs)/{WS}*={WS}*\( {
|
||||||
/* we match to the = in the lexer so that we can switch scanner
|
/* we match to the = in the lexer so that we can switch scanner
|
||||||
* state. By the time the parser see the = it may be too late
|
* state. By the time the parser see the = it may be too late
|
||||||
@ -560,17 +561,25 @@ GT >
|
|||||||
listen { RETURN_TOKEN(TOK_LISTEN); }
|
listen { RETURN_TOKEN(TOK_LISTEN); }
|
||||||
accept { RETURN_TOKEN(TOK_ACCEPT); }
|
accept { RETURN_TOKEN(TOK_ACCEPT); }
|
||||||
connect { RETURN_TOKEN(TOK_CONNECT); }
|
connect { RETURN_TOKEN(TOK_CONNECT); }
|
||||||
getattr { RETURN_TOKEN(TOK_GETATTR); }
|
|
||||||
setattr { RETURN_TOKEN(TOK_SETATTR); }
|
|
||||||
getopt { RETURN_TOKEN(TOK_GETOPT); }
|
getopt { RETURN_TOKEN(TOK_GETOPT); }
|
||||||
setopt { RETURN_TOKEN(TOK_SETOPT); }
|
setopt { RETURN_TOKEN(TOK_SETOPT); }
|
||||||
shutdown { RETURN_TOKEN(TOK_SHUTDOWN); }
|
shutdown { RETURN_TOKEN(TOK_SHUTDOWN); }
|
||||||
}
|
}
|
||||||
|
|
||||||
<UNIX_MODE,USERNS_MODE>{
|
<UNIX_MODE,USERNS_MODE,MQUEUE_MODE>{
|
||||||
create { RETURN_TOKEN(TOK_CREATE); }
|
create { RETURN_TOKEN(TOK_CREATE); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<MQUEUE_MODE>{
|
||||||
|
open { RETURN_TOKEN(TOK_OPEN); }
|
||||||
|
delete { RETURN_TOKEN(TOK_DELETE); }
|
||||||
|
}
|
||||||
|
|
||||||
|
<UNIX_MODE,MQUEUE_MODE>{
|
||||||
|
getattr { RETURN_TOKEN(TOK_GETATTR); }
|
||||||
|
setattr { RETURN_TOKEN(TOK_SETATTR); }
|
||||||
|
}
|
||||||
|
|
||||||
<DBUS_MODE,UNIX_MODE>{
|
<DBUS_MODE,UNIX_MODE>{
|
||||||
bind { RETURN_TOKEN(TOK_BIND); }
|
bind { RETURN_TOKEN(TOK_BIND); }
|
||||||
}
|
}
|
||||||
@ -590,7 +599,7 @@ GT >
|
|||||||
tracedby { RETURN_TOKEN(TOK_TRACEDBY); }
|
tracedby { RETURN_TOKEN(TOK_TRACEDBY); }
|
||||||
}
|
}
|
||||||
|
|
||||||
<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
|
<DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE>{
|
||||||
read { RETURN_TOKEN(TOK_READ); }
|
read { RETURN_TOKEN(TOK_READ); }
|
||||||
write { RETURN_TOKEN(TOK_WRITE); }
|
write { RETURN_TOKEN(TOK_WRITE); }
|
||||||
{OPEN_PAREN} {
|
{OPEN_PAREN} {
|
||||||
@ -606,7 +615,7 @@ GT >
|
|||||||
{ARROW} { RETURN_TOKEN(TOK_ARROW); }
|
{ARROW} { RETURN_TOKEN(TOK_ARROW); }
|
||||||
}
|
}
|
||||||
|
|
||||||
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE>{
|
<MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,MQUEUE_MODE>{
|
||||||
({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) {
|
({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) {
|
||||||
yylval.id = processid(yytext, yyleng);
|
yylval.id = processid(yytext, yyleng);
|
||||||
RETURN_TOKEN(TOK_ID);
|
RETURN_TOKEN(TOK_ID);
|
||||||
@ -731,13 +740,16 @@ include/{WS} {
|
|||||||
case TOK_USERNS:
|
case TOK_USERNS:
|
||||||
state = USERNS_MODE;
|
state = USERNS_MODE;
|
||||||
break;
|
break;
|
||||||
|
case TOK_MQUEUE:
|
||||||
|
state = MQUEUE_MODE;
|
||||||
|
break;
|
||||||
default: /* nothing */
|
default: /* nothing */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PUSH_AND_RETURN(state, token);
|
PUSH_AND_RETURN(state, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE>{
|
<INITIAL,NETWORK_MODE,RLIMIT_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE,MQUEUE_MODE>{
|
||||||
{END_OF_RULE} {
|
{END_OF_RULE} {
|
||||||
if (YY_START != INITIAL)
|
if (YY_START != INITIAL)
|
||||||
POP_NODUMP();
|
POP_NODUMP();
|
||||||
@ -745,14 +757,14 @@ include/{WS} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<INITIAL,SUB_ID_WS,INCLUDE,INCLUDE_EXISTS,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE>{
|
<INITIAL,SUB_ID_WS,INCLUDE,INCLUDE_EXISTS,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,RLIMIT_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,ABI_MODE,USERNS_MODE,MQUEUE_MODE>{
|
||||||
\r?\n {
|
\r?\n {
|
||||||
DUMP_PREPROCESS;
|
DUMP_PREPROCESS;
|
||||||
current_lineno++;
|
current_lineno++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<INITIAL,SUB_ID,SUB_ID_WS,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,RLIMIT_MODE,INCLUDE,INCLUDE_EXISTS,ABI_MODE,USERNS_MODE>{
|
<INITIAL,SUB_ID,SUB_ID_WS,SUB_VALUE,LIST_VAL_MODE,EXTCOND_MODE,LIST_COND_VAL,LIST_COND_PAREN_VAL,LIST_COND_MODE,EXTCONDLIST_MODE,ASSIGN_MODE,NETWORK_MODE,CHANGE_PROFILE_MODE,MOUNT_MODE,DBUS_MODE,SIGNAL_MODE,PTRACE_MODE,UNIX_MODE,RLIMIT_MODE,INCLUDE,INCLUDE_EXISTS,ABI_MODE,USERNS_MODE,MQUEUE_MODE>{
|
||||||
(.|\n) {
|
(.|\n) {
|
||||||
DUMP_PREPROCESS;
|
DUMP_PREPROCESS;
|
||||||
/* Something we didn't expect */
|
/* Something we didn't expect */
|
||||||
@ -788,4 +800,5 @@ unordered_map<int, string> state_names = {
|
|||||||
STATE_TABLE_ENT(INCLUDE_EXISTS),
|
STATE_TABLE_ENT(INCLUDE_EXISTS),
|
||||||
STATE_TABLE_ENT(ABI_MODE),
|
STATE_TABLE_ENT(ABI_MODE),
|
||||||
STATE_TABLE_ENT(USERNS_MODE),
|
STATE_TABLE_ENT(USERNS_MODE),
|
||||||
|
STATE_TABLE_ENT(MQUEUE_MODE),
|
||||||
};
|
};
|
||||||
|
@ -944,6 +944,12 @@ void set_supported_features()
|
|||||||
features_supports_userns = features_intersect(kernel_features,
|
features_supports_userns = features_intersect(kernel_features,
|
||||||
policy_features,
|
policy_features,
|
||||||
"namespaces/mask/userns_create");
|
"namespaces/mask/userns_create");
|
||||||
|
features_supports_posix_mqueue = features_intersect(kernel_features,
|
||||||
|
policy_features,
|
||||||
|
"ipc/posix_mqueue");
|
||||||
|
features_supports_sysv_mqueue = features_intersect(kernel_features,
|
||||||
|
policy_features,
|
||||||
|
"ipc/sysv_mqueue");
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool do_print_cache_dir(aa_features *features, int dirfd, const char *path)
|
static bool do_print_cache_dir(aa_features *features, int dirfd, const char *path)
|
||||||
|
@ -121,6 +121,9 @@ static struct keyword_table keyword_table[] = {
|
|||||||
{"readby", TOK_READBY},
|
{"readby", TOK_READBY},
|
||||||
{"abi", TOK_ABI},
|
{"abi", TOK_ABI},
|
||||||
{"userns", TOK_USERNS},
|
{"userns", TOK_USERNS},
|
||||||
|
{"mqueue", TOK_MQUEUE},
|
||||||
|
{"delete", TOK_DELETE},
|
||||||
|
{"open", TOK_OPEN},
|
||||||
|
|
||||||
/* terminate */
|
/* terminate */
|
||||||
{NULL, 0}
|
{NULL, 0}
|
||||||
|
@ -243,7 +243,7 @@ void post_process_rule_entries(Profile *prof)
|
|||||||
static int profile_add_hat_rules(Profile *prof)
|
static int profile_add_hat_rules(Profile *prof)
|
||||||
{
|
{
|
||||||
/* don't add hat rules if not hat or profile doesn't have hats */
|
/* don't add hat rules if not hat or profile doesn't have hats */
|
||||||
if (!prof->flags.hat && prof->hat_table.empty())
|
if (!(prof->flags.flags & FLAG_HAT) && prof->hat_table.empty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!add_proc_access(prof, CHANGEHAT_PATH))
|
if (!add_proc_access(prof, CHANGEHAT_PATH))
|
||||||
|
@ -936,6 +936,8 @@ static const char *mediates_extended_net = CLASS_STR(AA_CLASS_NET);
|
|||||||
static const char *mediates_netv8 = CLASS_STR(AA_CLASS_NETV8);
|
static const char *mediates_netv8 = CLASS_STR(AA_CLASS_NETV8);
|
||||||
static const char *mediates_net_unix = CLASS_SUB_STR(AA_CLASS_NET, AF_UNIX);
|
static const char *mediates_net_unix = CLASS_SUB_STR(AA_CLASS_NET, AF_UNIX);
|
||||||
static const char *mediates_ns = CLASS_STR(AA_CLASS_NS);
|
static const char *mediates_ns = CLASS_STR(AA_CLASS_NS);
|
||||||
|
static const char *mediates_posix_mqueue = CLASS_STR(AA_CLASS_POSIX_MQUEUE);
|
||||||
|
static const char *mediates_sysv_mqueue = CLASS_STR(AA_CLASS_SYSV_MQUEUE);
|
||||||
|
|
||||||
int process_profile_policydb(Profile *prof)
|
int process_profile_policydb(Profile *prof)
|
||||||
{
|
{
|
||||||
@ -981,6 +983,12 @@ int process_profile_policydb(Profile *prof)
|
|||||||
if (features_supports_userns &&
|
if (features_supports_userns &&
|
||||||
!prof->policy.rules->add_rule(mediates_ns, 0, AA_MAY_READ, 0, dfaflags))
|
!prof->policy.rules->add_rule(mediates_ns, 0, AA_MAY_READ, 0, dfaflags))
|
||||||
goto out;
|
goto out;
|
||||||
|
if (features_supports_posix_mqueue &&
|
||||||
|
!prof->policy.rules->add_rule(mediates_posix_mqueue, 0, AA_MAY_READ, 0, dfaflags))
|
||||||
|
goto out;
|
||||||
|
if (features_supports_sysv_mqueue &&
|
||||||
|
!prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, AA_MAY_READ, 0, dfaflags))
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (prof->policy.rules->rule_count > 0) {
|
if (prof->policy.rules->rule_count > 0) {
|
||||||
int xmatch_len = 0;
|
int xmatch_len = 0;
|
||||||
|
@ -143,6 +143,8 @@ void add_local_entry(Profile *prof);
|
|||||||
%token TOK_READBY
|
%token TOK_READBY
|
||||||
%token TOK_ABI
|
%token TOK_ABI
|
||||||
%token TOK_USERNS
|
%token TOK_USERNS
|
||||||
|
%token TOK_MQUEUE
|
||||||
|
%token TOK_DELETE
|
||||||
|
|
||||||
/* rlimits */
|
/* rlimits */
|
||||||
%token TOK_RLIMIT
|
%token TOK_RLIMIT
|
||||||
@ -179,6 +181,7 @@ void add_local_entry(Profile *prof);
|
|||||||
#include "ptrace.h"
|
#include "ptrace.h"
|
||||||
#include "af_unix.h"
|
#include "af_unix.h"
|
||||||
#include "userns.h"
|
#include "userns.h"
|
||||||
|
#include "mqueue.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
@ -196,6 +199,7 @@ void add_local_entry(Profile *prof);
|
|||||||
ptrace_rule *ptrace_entry;
|
ptrace_rule *ptrace_entry;
|
||||||
unix_rule *unix_entry;
|
unix_rule *unix_entry;
|
||||||
userns_rule *userns_entry;
|
userns_rule *userns_entry;
|
||||||
|
mqueue_rule *mqueue_entry;
|
||||||
|
|
||||||
flagvals flags;
|
flagvals flags;
|
||||||
int fmode;
|
int fmode;
|
||||||
@ -279,6 +283,10 @@ void add_local_entry(Profile *prof);
|
|||||||
%type <fmode> userns_perms
|
%type <fmode> userns_perms
|
||||||
%type <fmode> opt_userns_perm
|
%type <fmode> opt_userns_perm
|
||||||
%type <userns_entry> userns_rule
|
%type <userns_entry> userns_rule
|
||||||
|
%type <fmode> mqueue_perm
|
||||||
|
%type <fmode> mqueue_perms
|
||||||
|
%type <fmode> opt_mqueue_perm
|
||||||
|
%type <mqueue_entry> mqueue_rule
|
||||||
%%
|
%%
|
||||||
|
|
||||||
|
|
||||||
@ -413,7 +421,7 @@ profile: opt_profile_flag profile_base
|
|||||||
yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
|
yyerror(_("Profile names must begin with a '/', namespace or keyword 'profile' or 'hat'."));
|
||||||
|
|
||||||
if ($1 == 2)
|
if ($1 == 2)
|
||||||
prof->flags.hat = 1;
|
prof->flags.flags |= FLAG_HAT;
|
||||||
$$ = prof;
|
$$ = prof;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -440,7 +448,7 @@ hat: hat_start profile_base
|
|||||||
if ($2->xattrs.list)
|
if ($2->xattrs.list)
|
||||||
yyerror("hat profiles can't use xattrs matches");
|
yyerror("hat profiles can't use xattrs matches");
|
||||||
|
|
||||||
prof->flags.hat = 1;
|
prof->flags.flags |= FLAG_HAT;
|
||||||
$$ = prof;
|
$$ = prof;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -920,6 +928,22 @@ rules: rules opt_prefix capability
|
|||||||
$$ = $1;
|
$$ = $1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rules: rules opt_prefix mqueue_rule
|
||||||
|
{
|
||||||
|
if ($2.owner)
|
||||||
|
yyerror(_("owner prefix not allowed on mqueue rules")); //is this true?
|
||||||
|
if ($2.deny && $2.audit) {
|
||||||
|
$3->deny = 1;
|
||||||
|
} else if ($2.deny) {
|
||||||
|
$3->deny = 1;
|
||||||
|
$3->audit = $3->mode;
|
||||||
|
} else if ($2.audit) {
|
||||||
|
$3->audit = $3->mode;
|
||||||
|
}
|
||||||
|
$1->rule_ents.push_back($3);
|
||||||
|
$$ = $1;
|
||||||
|
};
|
||||||
|
|
||||||
rules: rules hat
|
rules: rules hat
|
||||||
{
|
{
|
||||||
PDEBUG("Matched: hat rule\n");
|
PDEBUG("Matched: hat rule\n");
|
||||||
@ -1591,6 +1615,62 @@ userns_rule: TOK_USERNS opt_userns_perm opt_conds TOK_END_OF_RULE
|
|||||||
$$ = ent;
|
$$ = ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mqueue_perm: TOK_VALUE
|
||||||
|
{
|
||||||
|
if (strcmp($1, "create") == 0)
|
||||||
|
$$ = AA_MQUEUE_CREATE;
|
||||||
|
else if (strcmp($1, "open") == 0)
|
||||||
|
$$ = AA_MQUEUE_OPEN;
|
||||||
|
else if (strcmp($1, "delete") == 0)
|
||||||
|
$$ = AA_MQUEUE_DELETE;
|
||||||
|
else if (strcmp($1, "getattr") == 0)
|
||||||
|
$$ = AA_MQUEUE_GETATTR;
|
||||||
|
else if (strcmp($1, "setattr") == 0)
|
||||||
|
$$ = AA_MQUEUE_SETATTR;
|
||||||
|
else if (strcmp($1, "write") == 0)
|
||||||
|
$$ = AA_MQUEUE_WRITE;
|
||||||
|
else if (strcmp($1, "read") == 0)
|
||||||
|
$$ = AA_MQUEUE_READ;
|
||||||
|
else if ($1) {
|
||||||
|
parse_mqueue_mode($1, &$$, 1);
|
||||||
|
} else
|
||||||
|
$$ = 0;
|
||||||
|
|
||||||
|
if ($1)
|
||||||
|
free($1);
|
||||||
|
}
|
||||||
|
| TOK_CREATE { $$ = AA_MQUEUE_CREATE; }
|
||||||
|
| TOK_OPEN { $$ = AA_MQUEUE_OPEN; }
|
||||||
|
| TOK_DELETE { $$ = AA_MQUEUE_DELETE; }
|
||||||
|
| TOK_GETATTR { $$ = AA_MQUEUE_GETATTR; }
|
||||||
|
| TOK_SETATTR { $$ = AA_MQUEUE_SETATTR; }
|
||||||
|
| TOK_WRITE { $$ = AA_MQUEUE_WRITE; }
|
||||||
|
| TOK_READ { $$ = AA_MQUEUE_READ; }
|
||||||
|
| TOK_MODE
|
||||||
|
{
|
||||||
|
parse_mqueue_mode($1, &$$, 1);
|
||||||
|
free($1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mqueue_perms: { /* nothing */ $$ = 0; }
|
||||||
|
| mqueue_perms mqueue_perm { $$ = $1 | $2; }
|
||||||
|
| mqueue_perms TOK_COMMA mqueue_perm { $$ = $1 | $3; }
|
||||||
|
|
||||||
|
opt_mqueue_perm: { /* nothing */ $$ = 0; }
|
||||||
|
| mqueue_perm { $$ = $1; }
|
||||||
|
| TOK_OPENPAREN mqueue_perms TOK_CLOSEPAREN { $$ = $2; }
|
||||||
|
|
||||||
|
mqueue_rule: TOK_MQUEUE opt_mqueue_perm opt_conds TOK_END_OF_RULE
|
||||||
|
{
|
||||||
|
mqueue_rule *ent = new mqueue_rule($2, $3);
|
||||||
|
$$ = ent;
|
||||||
|
}
|
||||||
|
| TOK_MQUEUE opt_mqueue_perm opt_conds TOK_ID TOK_END_OF_RULE
|
||||||
|
{
|
||||||
|
mqueue_rule *ent = new mqueue_rule($2, $3, $4);
|
||||||
|
$$ = ent;
|
||||||
|
}
|
||||||
|
|
||||||
hat_start: TOK_CARET {}
|
hat_start: TOK_CARET {}
|
||||||
| TOK_HAT {}
|
| TOK_HAT {}
|
||||||
|
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
#define AA_CLASS_SIGNAL 10
|
#define AA_CLASS_SIGNAL 10
|
||||||
#define AA_CLASS_NETV8 14
|
#define AA_CLASS_NETV8 14
|
||||||
#define AA_CLASS_LABEL 16
|
#define AA_CLASS_LABEL 16
|
||||||
|
#define AA_CLASS_POSIX_MQUEUE 17
|
||||||
|
#define AA_CLASS_SYSV_MQUEUE 18
|
||||||
#define AA_CLASS_NS 21
|
#define AA_CLASS_NS 21
|
||||||
|
|
||||||
/* defined in libapparmor's apparmor.h #define AA_CLASS_DBUS 32 */
|
/* defined in libapparmor's apparmor.h #define AA_CLASS_DBUS 32 */
|
||||||
|
@ -110,9 +110,13 @@ static inline enum profile_mode str_to_mode(const char *str)
|
|||||||
return MODE_UNSPECIFIED;
|
return MODE_UNSPECIFIED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define FLAG_HAT 1
|
||||||
|
#define FLAG_DEBUG1 2
|
||||||
|
#define FLAG_DEBUG2 4
|
||||||
|
|
||||||
class flagvals {
|
class flagvals {
|
||||||
public:
|
public:
|
||||||
int hat;
|
int flags;
|
||||||
enum profile_mode mode;
|
enum profile_mode mode;
|
||||||
int audit;
|
int audit;
|
||||||
int path;
|
int path;
|
||||||
@ -124,7 +128,7 @@ public:
|
|||||||
if (audit)
|
if (audit)
|
||||||
os << ", Audit";
|
os << ", Audit";
|
||||||
|
|
||||||
if (hat)
|
if (flags & FLAG_HAT)
|
||||||
os << ", Hat";
|
os << ", Hat";
|
||||||
|
|
||||||
os << "\n";
|
os << "\n";
|
||||||
|
8
parser/tst/simple_tests/mqueue/bad_01.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_01.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid label
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue label=,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_02.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_02.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid type
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_03.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_03.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid queuename for type sysv
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=sysv /queuename,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_04.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_04.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid queuename for type posix
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=posix 1234,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_05.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_05.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid access name
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue invalidaccess /queuename,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_06.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_06.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid type option - posix
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=posixfoo,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_07.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_07.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid type option - sysv
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=sysvfoo,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_08.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_08.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid type option
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=foo,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_09.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_09.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid queuename - does not start with /
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue foo,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/bad_10.sd
Normal file
8
parser/tst/simple_tests/mqueue/bad_10.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue invalid queuename - not only numbers
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue 1234foo,
|
||||||
|
}
|
6
parser/tst/simple_tests/mqueue/bad_11.sd
Normal file
6
parser/tst/simple_tests/mqueue/bad_11.sd
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue rule outside of a profile
|
||||||
|
#=EXRESULT FAIL
|
||||||
|
#
|
||||||
|
|
||||||
|
mqueue,
|
7
parser/tst/simple_tests/mqueue/ok_01.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_01.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue generic rule
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/ok_02.sd
Normal file
8
parser/tst/simple_tests/mqueue/ok_02.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue type option
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=posix,
|
||||||
|
mqueue type=sysv,
|
||||||
|
}
|
7
parser/tst/simple_tests/mqueue/ok_03.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_03.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue label option
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue label=bar,
|
||||||
|
}
|
7
parser/tst/simple_tests/mqueue/ok_04.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_04.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue valid sysv queue name
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue 1234,
|
||||||
|
}
|
7
parser/tst/simple_tests/mqueue/ok_05.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_05.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue valid posix queue name
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue /bar,
|
||||||
|
}
|
7
parser/tst/simple_tests/mqueue/ok_06.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_06.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue valid sysv queue name with type
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=sysv 1234,
|
||||||
|
}
|
7
parser/tst/simple_tests/mqueue/ok_07.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_07.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue valid posix queue name with type
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=posix /bar,
|
||||||
|
}
|
7
parser/tst/simple_tests/mqueue/ok_08.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_08.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue type and label defined
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=posix label=bar,
|
||||||
|
}
|
7
parser/tst/simple_tests/mqueue/ok_09.sd
Normal file
7
parser/tst/simple_tests/mqueue/ok_09.sd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue type, label and queue name defined
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue type=posix label=bar /baz,
|
||||||
|
}
|
14
parser/tst/simple_tests/mqueue/ok_10.sd
Normal file
14
parser/tst/simple_tests/mqueue/ok_10.sd
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue valid access mode
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue create,
|
||||||
|
mqueue (create, getattr, setattr),
|
||||||
|
mqueue (open delete),
|
||||||
|
mqueue (read write),
|
||||||
|
mqueue r,
|
||||||
|
mqueue w,
|
||||||
|
mqueue rw,
|
||||||
|
mqueue wr,
|
||||||
|
}
|
8
parser/tst/simple_tests/mqueue/ok_11.sd
Normal file
8
parser/tst/simple_tests/mqueue/ok_11.sd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue full valid rule
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
mqueue (create, write) type=posix label=baz /bar,
|
||||||
|
mqueue (open, delete) type=sysv label=baz 1234,
|
||||||
|
}
|
10
parser/tst/simple_tests/mqueue/ok_12.sd
Normal file
10
parser/tst/simple_tests/mqueue/ok_12.sd
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#
|
||||||
|
#=DESCRIPTION mqueue misc rules
|
||||||
|
#=EXRESULT PASS
|
||||||
|
#
|
||||||
|
/usr/bin/foo {
|
||||||
|
deny mqueue,
|
||||||
|
audit allow mqueue,
|
||||||
|
audit deny mqueue,
|
||||||
|
allow mqueue,
|
||||||
|
}
|
@ -115,6 +115,8 @@ SRC=access.c \
|
|||||||
openat.c \
|
openat.c \
|
||||||
pipe.c \
|
pipe.c \
|
||||||
pivot_root.c \
|
pivot_root.c \
|
||||||
|
posix_mq_rcv.c \
|
||||||
|
posix_mq_snd.c \
|
||||||
ptrace.c \
|
ptrace.c \
|
||||||
ptrace_helper.c \
|
ptrace_helper.c \
|
||||||
pwrite.c \
|
pwrite.c \
|
||||||
@ -135,6 +137,8 @@ SRC=access.c \
|
|||||||
syscall_setdomainname.c \
|
syscall_setdomainname.c \
|
||||||
syscall_setscheduler.c \
|
syscall_setscheduler.c \
|
||||||
sysctl_proc.c \
|
sysctl_proc.c \
|
||||||
|
sysv_mq_rcv.c \
|
||||||
|
sysv_mq_snd.c \
|
||||||
tcp.c \
|
tcp.c \
|
||||||
transition.c \
|
transition.c \
|
||||||
unix_fd_client.c \
|
unix_fd_client.c \
|
||||||
@ -237,6 +241,7 @@ TESTS=aa_exec \
|
|||||||
openat \
|
openat \
|
||||||
pipe \
|
pipe \
|
||||||
pivot_root \
|
pivot_root \
|
||||||
|
posix_ipc \
|
||||||
ptrace \
|
ptrace \
|
||||||
pwrite \
|
pwrite \
|
||||||
query_label \
|
query_label \
|
||||||
@ -250,6 +255,7 @@ TESTS=aa_exec \
|
|||||||
setattr \
|
setattr \
|
||||||
symlink \
|
symlink \
|
||||||
syscall \
|
syscall \
|
||||||
|
sysv_ipc \
|
||||||
tcp \
|
tcp \
|
||||||
unix_fd_server \
|
unix_fd_server \
|
||||||
unix_socket_pathname \
|
unix_socket_pathname \
|
||||||
@ -312,6 +318,12 @@ dbus_service: dbus_message dbus_service.c dbus_common.o
|
|||||||
dbus_unrequested_reply: dbus_service dbus_unrequested_reply.c dbus_common.o
|
dbus_unrequested_reply: dbus_service dbus_unrequested_reply.c dbus_common.o
|
||||||
${CC} ${CFLAGS} ${LDFLAGS} $(filter-out dbus_service, $^) -o $@ ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
|
${CC} ${CFLAGS} ${LDFLAGS} $(filter-out dbus_service, $^) -o $@ ${LDLIBS} $(shell pkg-config --cflags --libs dbus-1)
|
||||||
|
|
||||||
|
posix_mq_rcv: posix_mq_rcv.c
|
||||||
|
${CC} ${CFLAGS} ${LDFLAGS} $< -o $@ ${LDLIBS} -lrt
|
||||||
|
|
||||||
|
posix_mq_snd: posix_mq_snd.c
|
||||||
|
${CC} ${CFLAGS} ${LDFLAGS} $< -o $@ ${LDLIBS} -lrt
|
||||||
|
|
||||||
transition: transition.c
|
transition: transition.c
|
||||||
${CC} ${CFLAGS} ${TRANSITION_CFLAGS} ${LDFLAGS} $< -o $@ ${LDLIBS}
|
${CC} ${CFLAGS} ${TRANSITION_CFLAGS} ${LDFLAGS} $< -o $@ ${LDLIBS}
|
||||||
|
|
||||||
|
@ -423,6 +423,26 @@ sub gen_path($) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub gen_mqueue($) {
|
||||||
|
my $rule = shift;
|
||||||
|
my @rules = split (/:/, $rule);
|
||||||
|
if (@rules == 2) {
|
||||||
|
if ($rules[1] =~ /^ALL$/) {
|
||||||
|
push (@{$output_rules{$hat}}, " mqueue,\n");
|
||||||
|
} else {
|
||||||
|
push (@{$output_rules{$hat}}, " mqueue $rules[1],\n");
|
||||||
|
}
|
||||||
|
} elsif (@rules == 3) {
|
||||||
|
push (@{$output_rules{$hat}}, " mqueue $rules[1] $rules[2],\n");
|
||||||
|
} elsif (@rules == 4) {
|
||||||
|
push (@{$output_rules{$hat}}, " mqueue $rules[1] $rules[2] $rules[3],\n");
|
||||||
|
} elsif (@rules == 5) {
|
||||||
|
push (@{$output_rules{$hat}}, " mqueue $rules[1] $rules[2] $rules[3] $rules[4],\n");
|
||||||
|
} else {
|
||||||
|
(!$nowarn) && print STDERR "Warning: invalid mqueue description '$rule', ignored\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub emit_flags($) {
|
sub emit_flags($) {
|
||||||
my $hat = shift;
|
my $hat = shift;
|
||||||
|
|
||||||
@ -492,6 +512,8 @@ sub gen_from_args() {
|
|||||||
gen_xattr($rule);
|
gen_xattr($rule);
|
||||||
} elsif ($rule =~ /^path:/) {
|
} elsif ($rule =~ /^path:/) {
|
||||||
gen_path($rule);
|
gen_path($rule);
|
||||||
|
} elsif ($rule =~ /^mqueue:/) {
|
||||||
|
gen_mqueue($rule);
|
||||||
} else {
|
} else {
|
||||||
gen_file($rule, $qualifier);
|
gen_file($rule, $qualifier);
|
||||||
}
|
}
|
||||||
|
12
tests/regression/apparmor/posix_ipc.sh
Normal file
12
tests/regression/apparmor/posix_ipc.sh
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
# Test semaphores first, as they could be used for synchronization
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
# posix_sem.sh
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
# posix_shm.sh
|
||||||
|
|
||||||
|
./posix_mq.sh
|
||||||
|
|
||||||
|
|
31
tests/regression/apparmor/posix_mq.h
Normal file
31
tests/regression/apparmor/posix_mq.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef POSIX_MQ_H_
|
||||||
|
#define POSIX_MQ_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
|
||||||
|
#define QNAME "/testmq"
|
||||||
|
#define SHM_PATH "/unnamedsemtest"
|
||||||
|
#define SEM_PATH "/namedsemtest"
|
||||||
|
#define OBJ_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
|
||||||
|
|
||||||
|
#define BUF_SIZE 1024
|
||||||
|
struct shmbuf { // Buffer in shared memory
|
||||||
|
sem_t sem;
|
||||||
|
int cnt; // Number of bytes used in 'buf'
|
||||||
|
char buf[BUF_SIZE]; // Data being transferred
|
||||||
|
};
|
||||||
|
|
||||||
|
struct msgbuf {
|
||||||
|
long mtype;
|
||||||
|
char mtext[BUF_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
char *msg = "hello world";
|
||||||
|
|
||||||
|
#endif /* #ifndef POSIX_MQ_H_ */
|
179
tests/regression/apparmor/posix_mq.sh
Executable file
179
tests/regression/apparmor/posix_mq.sh
Executable file
@ -0,0 +1,179 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
#Copyright (C) 2022 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.
|
||||||
|
|
||||||
|
#=NAME posix_mq
|
||||||
|
#=DESCRIPTION
|
||||||
|
# This test verifies if mediation of posix message queues is working
|
||||||
|
#=END
|
||||||
|
|
||||||
|
pwd=`dirname $0`
|
||||||
|
pwd=`cd $pwd ; /bin/pwd`
|
||||||
|
|
||||||
|
bin=$pwd
|
||||||
|
|
||||||
|
. $bin/prologue.inc
|
||||||
|
|
||||||
|
requires_kernel_features ipc/posix_mqueue
|
||||||
|
requires_parser_support "mqueue,"
|
||||||
|
|
||||||
|
settest posix_mq_rcv
|
||||||
|
|
||||||
|
sender="$bin/posix_mq_snd"
|
||||||
|
receiver="$bin/posix_mq_rcv"
|
||||||
|
queuename="/queuename"
|
||||||
|
queuename2="/queuename2"
|
||||||
|
|
||||||
|
user="foo"
|
||||||
|
adduser --gecos "First Last,RoomNumber,WorkPhone,HomePhone" --no-create-home --disabled-password $user >/dev/null
|
||||||
|
echo "$user:password" | sudo chpasswd
|
||||||
|
userid=$(id -u $user)
|
||||||
|
|
||||||
|
# workaround to not have to set o+x
|
||||||
|
chmod 6755 $receiver
|
||||||
|
setcap cap_dac_read_search+pie $receiver
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
rm -f /dev/mqueue/$queuename
|
||||||
|
rm -f /dev/mqueue/$queuename2
|
||||||
|
deluser foo >/dev/null
|
||||||
|
}
|
||||||
|
do_onexit="cleanup"
|
||||||
|
|
||||||
|
do_test()
|
||||||
|
{
|
||||||
|
local desc="POSIX MQUEUE ($1)"
|
||||||
|
shift
|
||||||
|
runchecktest "$desc" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
do_tests()
|
||||||
|
{
|
||||||
|
prefix=$1
|
||||||
|
expect_send=$2
|
||||||
|
expect_recv=$3
|
||||||
|
expect_open=$4
|
||||||
|
|
||||||
|
all_args=("$@")
|
||||||
|
rest_args=("${all_args[@]:5}")
|
||||||
|
|
||||||
|
do_test "$prefix" "$expect_send" $sender "$expect_recv" -c $sender -k $queuename "${rest_args[@]}"
|
||||||
|
|
||||||
|
# notify requires netlink permissions
|
||||||
|
do_test "$prefix : mq_notify" "$expect_send" $sender "$expect_recv" -c $sender -k $queuename -n mq_notify "${rest_args[@]}"
|
||||||
|
|
||||||
|
do_test "$prefix : select" "$expect_open" -c $sender -k $queuename -n select "${rest_args[@]}"
|
||||||
|
|
||||||
|
do_test "$prefix : poll" "$expect_open" -c $sender -k $queuename -n poll "${rest_args[@]}"
|
||||||
|
|
||||||
|
do_test "$prefix : epoll" "$expect_open" -c $sender -k $queuename -n epoll "${rest_args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for username in "root" "$userid" ; do
|
||||||
|
if [ $username == "root" ] ; then
|
||||||
|
usercmd=""
|
||||||
|
else
|
||||||
|
usercmd="-u $userid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
do_tests "unconfined $username" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
# No mqueue perms
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "$sender:px" -- image=$sender
|
||||||
|
do_tests "confined $username - no perms" fail fail fail fail $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "deny:mqueue" "$sender:px" -- image=$sender "deny mqueue"
|
||||||
|
do_tests "confined $username - deny perms" fail fail fail fail $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# generic mqueue
|
||||||
|
# 2 Potential failures caused by missing other x permission in path
|
||||||
|
# to tests. Usually on the user home dir as it is now default to
|
||||||
|
# create a user without that
|
||||||
|
# * if you seen a capability dac_read_search denied failure from
|
||||||
|
# apparmor when doing "root" username tests
|
||||||
|
# * if doing the $userid set of tests and you see
|
||||||
|
# Permission denied in the test output
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue" "$sender:px" -- image=$sender "mqueue"
|
||||||
|
do_tests "confined $username - mqueue" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:type=posix" "$sender:px" -- image=$sender "mqueue:type=posix"
|
||||||
|
do_tests "confined $username - mqueue type=posix" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
# queue name
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:$queuename" "$sender:px" -- image=$sender "mqueue:$queuename"
|
||||||
|
do_tests "confined $username - mqueue /name 1" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue" "$sender:px" -- image=$sender "mqueue:$queuename"
|
||||||
|
do_tests "confined $username - mqueue /name 2" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:$queuename" "$sender:px" -- image=$sender "mqueue"
|
||||||
|
do_tests "confined $username - mqueue /name 3" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:$queuename" "$sender:px" -- image=$sender "mqueue:$queuename2"
|
||||||
|
do_tests "confined $username - mqueue /name 4" fail fail fail fail $usercmd -t 1
|
||||||
|
|
||||||
|
|
||||||
|
# specific permissions
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(create,read,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:write"
|
||||||
|
do_tests "confined $username - specific 1" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(read,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:write"
|
||||||
|
do_tests "confined $username - specific 2" fail fail fail fail $usercmd -t 1
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(create,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:write"
|
||||||
|
do_tests "confined $username - specific 3" fail fail fail fail $usercmd -t 1
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(create,read,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:write"
|
||||||
|
do_tests "confined $username - specific 4" fail fail fail fail $usercmd -t 1
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(create,read,delete,setattr)" "$sender:px" -- image=$sender "mqueue:write"
|
||||||
|
do_tests "confined $username - specific 5" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(create,read,delete,getattr)" "$sender:px" -- image=$sender "mqueue:write"
|
||||||
|
do_tests "confined $username - specific 6" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(create,read,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:read"
|
||||||
|
do_tests "confined $username - specific 7" fail fail fail fail $usercmd -t 1
|
||||||
|
|
||||||
|
# unconfined receiver
|
||||||
|
genprofile image=$sender "mqueue"
|
||||||
|
do_tests "confined sender $username - unconfined receiver" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# unconfined sender
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue" "$sender:ux"
|
||||||
|
do_tests "confined receiver $username - unconfined sender" pass pass pass pass $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# queue label
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:label=$receiver" "$sender:px" -- image=$sender "mqueue:label=$receiver"
|
||||||
|
do_tests "confined $username - mqueue label 1" xpass xpass xpass xpass $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# queue name and label
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "network:netlink" "mqueue:(create,read,delete):type=posix:label=$receiver:$queuename" "$sender:px" -- image=$sender "mqueue:(open,write):type=posix:label=$receiver:$queuename"
|
||||||
|
do_tests "confined $username - mqueue label 2" xpass xpass xpass xpass $usercmd
|
||||||
|
|
||||||
|
# ensure we are cleaned up for next pass
|
||||||
|
removeprofile
|
||||||
|
rm -f /dev/mqueue/$queuename
|
||||||
|
rm -f /dev/mqueue/$queuename2
|
||||||
|
done
|
||||||
|
|
||||||
|
# cross user tests
|
||||||
|
|
||||||
|
|
||||||
|
# confined root with cap ??override
|
||||||
|
|
||||||
|
|
||||||
|
# confined root without cap ??override
|
||||||
|
|
306
tests/regression/apparmor/posix_mq_rcv.c
Normal file
306
tests/regression/apparmor/posix_mq_rcv.c
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
#include <mqueue.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "posix_mq.h"
|
||||||
|
|
||||||
|
int timeout = 5; //seconds
|
||||||
|
char *queuename = QNAME;
|
||||||
|
|
||||||
|
enum notify_options {
|
||||||
|
DO_NOT_NOTIFY,
|
||||||
|
MQ_NOTIFY,
|
||||||
|
SELECT,
|
||||||
|
POLL,
|
||||||
|
EPOLL
|
||||||
|
};
|
||||||
|
|
||||||
|
int receive_message(mqd_t mqd, char needs_timeout) {
|
||||||
|
ssize_t nbytes;
|
||||||
|
struct mq_attr attr;
|
||||||
|
char *buf = NULL;
|
||||||
|
|
||||||
|
if (mq_getattr(mqd, &attr) == -1) {
|
||||||
|
perror("FAIL - could not mq_getattr");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc(attr.mq_msgsize);
|
||||||
|
if (buf == NULL) {
|
||||||
|
perror("FAIL - could not malloc");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needs_timeout) { /* do we need this or should we just use mq_timedreceive always? */
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
ts.tv_sec += timeout;
|
||||||
|
nbytes = mq_timedreceive(mqd, buf, attr.mq_msgsize,
|
||||||
|
NULL, &ts);
|
||||||
|
} else {
|
||||||
|
attr.mq_flags |= O_NONBLOCK;
|
||||||
|
if (mq_setattr(mqd, &attr, NULL) == -1){
|
||||||
|
perror("FAIL - could not mq_setattr");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
nbytes = mq_receive(mqd, buf, attr.mq_msgsize, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbytes < 0) {
|
||||||
|
perror("FAIL - could not receive msg");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[nbytes] = 0;
|
||||||
|
|
||||||
|
if (strncmp(buf, msg, BUF_SIZE) != 0) {
|
||||||
|
fprintf(stderr, "FAIL - msg received does not match: %s - %s\n", buf, msg);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
if (mq_close(mqd) == (mqd_t) -1) {
|
||||||
|
perror("FAIL - could not close mq");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (mq_unlink(queuename) == (mqd_t) -1) {
|
||||||
|
perror("FAIL - could unlink mq");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_signal(union sigval sv) {
|
||||||
|
mqd_t mqd = *((mqd_t *) sv.sival_ptr);
|
||||||
|
receive_message(mqd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(char *prog_name, char *msg)
|
||||||
|
{
|
||||||
|
if (msg != NULL)
|
||||||
|
fprintf(stderr, "%s\n", msg);
|
||||||
|
|
||||||
|
fprintf(stderr, "Usage: %s [options]\n", prog_name);
|
||||||
|
fprintf(stderr, "Options are:\n");
|
||||||
|
fprintf(stderr, "-n get notified if there's an item in the queue\n");
|
||||||
|
fprintf(stderr, " available options are: mq_notify, select, poll and epoll\n");
|
||||||
|
fprintf(stderr, "-k message queue name (default is %s)\n", QNAME);
|
||||||
|
fprintf(stderr, "-c path of the client binary\n");
|
||||||
|
fprintf(stderr, "-u run test as specified UID\n");
|
||||||
|
fprintf(stderr, "-t timeout in seconds\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_mq_notify(mqd_t mqd)
|
||||||
|
{
|
||||||
|
struct sigevent sev;
|
||||||
|
sev.sigev_notify = SIGEV_THREAD;
|
||||||
|
sev.sigev_notify_function = handle_signal;
|
||||||
|
sev.sigev_notify_attributes = NULL;
|
||||||
|
sev.sigev_value.sival_ptr = &mqd;
|
||||||
|
|
||||||
|
if (mq_notify(mqd, &sev) == -1) {
|
||||||
|
perror(" FAIL - could not mq_notify");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
sleep(timeout);
|
||||||
|
fprintf(stderr, "FAIL - could not mq_notify: Connection timed out\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_select(mqd_t mqd)
|
||||||
|
{
|
||||||
|
fd_set read_fds;
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = timeout;
|
||||||
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
FD_SET(mqd, &read_fds);
|
||||||
|
|
||||||
|
if (select(mqd + 1, &read_fds, NULL, NULL, &tv) == -1) {
|
||||||
|
perror("FAIL - could not select");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
if (FD_ISSET(mqd, &read_fds))
|
||||||
|
receive_message(mqd, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_poll(mqd_t mqd)
|
||||||
|
{
|
||||||
|
struct pollfd fds[1];
|
||||||
|
fds[0].fd = mqd;
|
||||||
|
fds[0].events = POLLIN;
|
||||||
|
|
||||||
|
if (poll(fds, 1, timeout * 1000) == -1) {
|
||||||
|
perror("FAIL - could not poll");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
if (fds[0].revents & POLLIN)
|
||||||
|
receive_message(mqd, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_epoll(mqd_t mqd)
|
||||||
|
{
|
||||||
|
int epfd = epoll_create(1);
|
||||||
|
if (epfd == -1) {
|
||||||
|
perror("FAIL - could not create epoll");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct epoll_event ev, rev[1];
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.fd = mqd;
|
||||||
|
if (epoll_ctl(epfd, EPOLL_CTL_ADD, mqd, &ev) == -1) {
|
||||||
|
perror("FAIL - could not add mqd to epoll");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (epoll_wait(epfd, rev, 1, timeout * 1000) == -1) {
|
||||||
|
perror("FAIL - could not epoll_wait");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
if (rev[0].data.fd == mqd && rev[0].events & EPOLLIN)
|
||||||
|
receive_message(mqd, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive(enum notify_options notify, mqd_t mqd)
|
||||||
|
{
|
||||||
|
switch(notify) {
|
||||||
|
case DO_NOT_NOTIFY:
|
||||||
|
receive_message(mqd, 1);
|
||||||
|
return;
|
||||||
|
case MQ_NOTIFY:
|
||||||
|
receive_mq_notify(mqd);
|
||||||
|
break;
|
||||||
|
case SELECT:
|
||||||
|
receive_select(mqd);
|
||||||
|
break;
|
||||||
|
case POLL:
|
||||||
|
receive_poll(mqd);
|
||||||
|
break;
|
||||||
|
case EPOLL:
|
||||||
|
receive_epoll(mqd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char opt = 0;
|
||||||
|
enum notify_options notify = DO_NOT_NOTIFY;
|
||||||
|
mqd_t mqd;
|
||||||
|
char *client = NULL;
|
||||||
|
int uid;
|
||||||
|
struct mq_attr attr;
|
||||||
|
attr.mq_flags = 0;
|
||||||
|
attr.mq_maxmsg = 10;
|
||||||
|
attr.mq_msgsize = BUF_SIZE;
|
||||||
|
attr.mq_curmsgs = 0;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "n:k:c:u:t:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'n':
|
||||||
|
if (strcmp(optarg, "mq_notify") == 0)
|
||||||
|
notify = MQ_NOTIFY;
|
||||||
|
else if (strcmp(optarg, "select") == 0)
|
||||||
|
notify = SELECT;
|
||||||
|
else if (strcmp(optarg, "poll") == 0)
|
||||||
|
notify = POLL;
|
||||||
|
else if (strcmp(optarg, "epoll") == 0)
|
||||||
|
notify = EPOLL;
|
||||||
|
else
|
||||||
|
usage(argv[0], "invalid option for -n");
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
queuename = optarg;
|
||||||
|
if (queuename == NULL)
|
||||||
|
usage(argv[0], "-k option must specify the queue name\n");
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
client = optarg;
|
||||||
|
if (client == NULL)
|
||||||
|
usage(argv[0], "-c option must specify the client binary\n");
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
/* change file mode on output before setuid drops
|
||||||
|
* privs. This is required to make sure we can
|
||||||
|
* write to the output file and in some cases
|
||||||
|
* even exec with our inherited output file
|
||||||
|
*
|
||||||
|
* This assume test infrastructure creates the
|
||||||
|
* file as root and dups stderr to stdout
|
||||||
|
*/
|
||||||
|
if (fchmod(fileno(stdout), 0666) == -1) {
|
||||||
|
perror("FAIL - could not set output file mode");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (fchmod(fileno(stderr), 0666) == -1) {
|
||||||
|
perror("FAIL - could not set output file mode");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
uid = atoi(optarg);
|
||||||
|
if (setuid(uid) < 0) {
|
||||||
|
perror("FAIL - could not setuid");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
timeout = atoi(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0], "Unrecognized option\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mqd = mq_open(queuename, O_CREAT | O_RDONLY, OBJ_PERMS, &attr);
|
||||||
|
if (mqd == (mqd_t) -1) {
|
||||||
|
perror("FAIL - could not open mq");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exec the client */
|
||||||
|
int pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("FAIL - could not fork");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else if (!pid) {
|
||||||
|
if (client == NULL) {
|
||||||
|
usage(argv[0], "client not specified");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
/* execution of the main thread continues
|
||||||
|
* in case the client will be manually executed
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
execl(client, client, queuename, NULL);
|
||||||
|
printf("FAIL %d - execlp %s %s- %m\n", getuid(), client, queuename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
receive(notify, mqd);
|
||||||
|
|
||||||
|
/* when the notification fails because of timeout, it ends up here
|
||||||
|
* so, clean up the mqueue
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (mq_close(mqd) == (mqd_t) -1) {
|
||||||
|
perror("FAIL - could not close mq");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (mq_unlink(queuename) == (mqd_t) -1) {
|
||||||
|
perror("FAIL - could unlink mq");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
32
tests/regression/apparmor/posix_mq_snd.c
Normal file
32
tests/regression/apparmor/posix_mq_snd.c
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <mqueue.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "posix_mq.h"
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
mqd_t mqd;
|
||||||
|
char *queuename = QNAME;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
queuename = argv[1];
|
||||||
|
}
|
||||||
|
mqd = mq_open(queuename, O_WRONLY);
|
||||||
|
if (mqd == (mqd_t) -1) {
|
||||||
|
perror("FAIL sender - could not open mq");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mq_send(mqd, msg, strnlen(msg, BUF_SIZE), 0) == -1) {
|
||||||
|
perror("FAIL sender - could not send");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mq_close(mqd) == (mqd_t) -1) {
|
||||||
|
perror("FAIL sender - could not close mq");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("PASS client\n");
|
||||||
|
return 0;
|
||||||
|
}
|
9
tests/regression/apparmor/sysv_ipc.sh
Executable file
9
tests/regression/apparmor/sysv_ipc.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
# Test semaphores first, as they could be used for synchronization
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
# sysv_sem.sh
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
# sysv_shm.sh
|
||||||
|
|
||||||
|
./sysv_mq.sh
|
35
tests/regression/apparmor/sysv_mq.h
Normal file
35
tests/regression/apparmor/sysv_mq.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef SYSV_MQ_H_
|
||||||
|
#define SYSV_MQ_H_
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/sem.h>
|
||||||
|
#include <sys/msg.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#define MQ_KEY (123)
|
||||||
|
#define MQ_TYPE (0)
|
||||||
|
#define SHM_KEY (456)
|
||||||
|
#define SEM_KEY (789)
|
||||||
|
#define OBJ_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
|
||||||
|
|
||||||
|
#define BUF_SIZE 1024
|
||||||
|
struct msg_buf {
|
||||||
|
long mtype;
|
||||||
|
char mtext[BUF_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
union semun {
|
||||||
|
int val;
|
||||||
|
struct semid_ds *buf;
|
||||||
|
unsigned short *array;
|
||||||
|
struct seminfo *__buf;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
char *msg = "hello world";
|
||||||
|
|
||||||
|
#endif /* #ifndef SYSV_MQ_H_ */
|
171
tests/regression/apparmor/sysv_mq.sh
Executable file
171
tests/regression/apparmor/sysv_mq.sh
Executable file
@ -0,0 +1,171 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
#Copyright (C) 2022 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.
|
||||||
|
|
||||||
|
#=NAME sysv_mq
|
||||||
|
#=DESCRIPTION
|
||||||
|
# This test verifies if mediation of sysv message queues is working
|
||||||
|
#=END
|
||||||
|
|
||||||
|
pwd=`dirname $0`
|
||||||
|
pwd=`cd $pwd ; /bin/pwd`
|
||||||
|
|
||||||
|
bin=$pwd
|
||||||
|
|
||||||
|
. $bin/prologue.inc
|
||||||
|
|
||||||
|
requires_kernel_features ipc/sysv_mqueue
|
||||||
|
requires_parser_support "mqueue,"
|
||||||
|
|
||||||
|
settest sysv_mq_rcv
|
||||||
|
|
||||||
|
sender="$bin/sysv_mq_snd"
|
||||||
|
receiver="$bin/sysv_mq_rcv"
|
||||||
|
qkey=123
|
||||||
|
qkey2=124
|
||||||
|
semaphore=456
|
||||||
|
|
||||||
|
user="foo"
|
||||||
|
adduser --gecos "First Last,RoomNumber,WorkPhone,HomePhone" --no-create-home --disabled-password $user >/dev/null
|
||||||
|
echo "$user:password" | sudo chpasswd
|
||||||
|
userid=$(id -u $user)
|
||||||
|
|
||||||
|
# workaround to not have to set o+x
|
||||||
|
chmod 6755 $receiver
|
||||||
|
setcap cap_dac_read_search+pie $receiver
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
{
|
||||||
|
ipcrm --queue-key $qkey >/dev/null 2>&1
|
||||||
|
ipcrm --queue-key $qkey2 >/dev/null 2>&1
|
||||||
|
ipcrm --semaphore-key $semaphore >/dev/null 2>&1
|
||||||
|
deluser foo >/dev/null
|
||||||
|
}
|
||||||
|
do_onexit="cleanup"
|
||||||
|
|
||||||
|
do_test()
|
||||||
|
{
|
||||||
|
local desc="SYSV MQUEUE ($1)"
|
||||||
|
shift
|
||||||
|
runchecktest "$desc" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_tests()
|
||||||
|
{
|
||||||
|
prefix=$1
|
||||||
|
expect_send=$2
|
||||||
|
|
||||||
|
all_args=("$@")
|
||||||
|
rest_args=("${all_args[@]:2}")
|
||||||
|
|
||||||
|
do_test "$prefix" "$expect_send" -c $sender -k $qkey -s $semaphore "${rest_args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
for username in "root" "$userid" ; do
|
||||||
|
if [ $username == "root" ] ; then
|
||||||
|
usercmd=""
|
||||||
|
else
|
||||||
|
usercmd="-u $userid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
do_tests "unconfined $username" pass $usercmd
|
||||||
|
|
||||||
|
# No mqueue perms
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "$sender:px" -- image=$sender
|
||||||
|
do_tests "confined $username - no perms" fail $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "deny:mqueue" "$sender:px" -- image=$sender "deny mqueue"
|
||||||
|
do_tests "confined $username - deny perms" fail $usercmd
|
||||||
|
|
||||||
|
# generic mqueue
|
||||||
|
# 2 Potential failures caused by missing other x permission in path
|
||||||
|
# to tests. Usually on the user home dir as it is now default to
|
||||||
|
# create a user without that
|
||||||
|
# * if you seen a capability dac_read_search denied failure from
|
||||||
|
# apparmor when doing "root" username tests
|
||||||
|
# * if doing the $userid set of tests and you see
|
||||||
|
# Permission denied in the test output
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue" "$sender:px" -- image=$sender "mqueue"
|
||||||
|
do_tests "confined $username - mqueue" pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:type=sysv" "$sender:px" -- image=$sender "mqueue:type=sysv"
|
||||||
|
do_tests "confined $username - mqueue type=sysv" pass $usercmd
|
||||||
|
|
||||||
|
# queue name
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:$qkey" "$sender:px" -- image=$sender "mqueue:$qkey"
|
||||||
|
do_tests "confined $username - mqueue /name 1" pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue" "$sender:px" -- image=$sender "mqueue:$qkey"
|
||||||
|
do_tests "confined $username - mqueue /name 2" pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:$qkey" "$sender:px" -- image=$sender "mqueue"
|
||||||
|
do_tests "confined $username - mqueue /name 3" pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:$qkey" "$sender:px" -- image=$sender "mqueue:$qkey2"
|
||||||
|
do_tests "confined $username - mqueue /name 4" fail $usercmd -t 1
|
||||||
|
|
||||||
|
|
||||||
|
# specific permissions
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,read,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:(open,write)"
|
||||||
|
do_tests "confined $username - specific 1" pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(read,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:(open,write)"
|
||||||
|
do_tests "confined $username - specific 2" fail $usercmd -t 1
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:(open,write)"
|
||||||
|
do_tests "confined $username - specific 3" fail $usercmd -t 1
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,read,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:(open,write)"
|
||||||
|
do_tests "confined $username - specific 4" fail $usercmd -t 1
|
||||||
|
# we need to remove queue since the previous test didn't
|
||||||
|
ipcrm --queue-key $qkey >/dev/null 2>&1
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,read,delete,setattr)" "$sender:px" -- image=$sender "mqueue:(open,write)"
|
||||||
|
do_tests "confined $username - specific 5" pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,read,delete,getattr)" "$sender:px" -- image=$sender "mqueue:(open,write)"
|
||||||
|
do_tests "confined $username - specific 6" pass $usercmd
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,read,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:(open,read)"
|
||||||
|
do_tests "confined $username - specific 7" fail $usercmd -t 1
|
||||||
|
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,read,delete,getattr,setattr)" "$sender:px" -- image=$sender "mqueue:write"
|
||||||
|
do_tests "confined $username - specific 7" fail $usercmd -t 1
|
||||||
|
|
||||||
|
|
||||||
|
# unconfined receiver
|
||||||
|
genprofile image=$sender "mqueue"
|
||||||
|
do_tests "confined sender $username - unconfined receiver" pass $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# unconfined sender
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue" "$sender:ux"
|
||||||
|
do_tests "confined receiver $username - unconfined sender" pass $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# queue label
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:label=$receiver" "$sender:px" -- image=$sender "mqueue:label=$receiver"
|
||||||
|
do_tests "confined $username - mqueue label 1" xpass $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# queue name and label
|
||||||
|
genprofile "cap:sys_resource:deny" "cap:setuid" "cap:fowner" "mqueue:(create,read,delete):type=sysv:label=$receiver:$qkey" "$sender:px" -- image=$sender "mqueue:(open,write):type=sysv:label=$receiver:$qkey"
|
||||||
|
do_tests "confined $username - mqueue label 2" xpass $usercmd
|
||||||
|
|
||||||
|
|
||||||
|
# ensure we are cleaned up for next pass
|
||||||
|
removeprofile
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# confined root with cap ??override
|
||||||
|
|
||||||
|
|
||||||
|
# confined root without cap ??override
|
||||||
|
|
||||||
|
|
||||||
|
# deliver message by mtype (posix lacks this)
|
187
tests/regression/apparmor/sysv_mq_rcv.c
Normal file
187
tests/regression/apparmor/sysv_mq_rcv.c
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "sysv_mq.h"
|
||||||
|
|
||||||
|
int timeout = 5; //seconds
|
||||||
|
key_t mqkey = MQ_KEY;
|
||||||
|
long mqtype = MQ_TYPE;
|
||||||
|
key_t semkey = SEM_KEY;
|
||||||
|
|
||||||
|
// missing getattr and setattr
|
||||||
|
int receive_message(int qid, long qtype)
|
||||||
|
{
|
||||||
|
struct msg_buf mb;
|
||||||
|
ssize_t nbytes;
|
||||||
|
|
||||||
|
nbytes = msgrcv(qid, &mb, sizeof(mb.mtext), qtype,
|
||||||
|
MSG_NOERROR | IPC_NOWAIT);
|
||||||
|
if (nbytes < 0) {
|
||||||
|
perror("FAIL - could not receive msg");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mb.mtext[nbytes] = 0;
|
||||||
|
|
||||||
|
if (strncmp(mb.mtext, msg, BUF_SIZE) != 0) {
|
||||||
|
fprintf(stderr, "FAIL - msg received does not match: %s - %s\n", mb.mtext, msg);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("PASS\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int receive(int qid, long qtype, int semid)
|
||||||
|
{
|
||||||
|
struct sembuf sop;
|
||||||
|
sop.sem_num = 0;
|
||||||
|
sop.sem_op = 0;
|
||||||
|
sop.sem_flg = 0;
|
||||||
|
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = timeout;
|
||||||
|
ts.tv_nsec = 0;
|
||||||
|
|
||||||
|
if (semtimedop(semid, &sop, 1, &ts) < 0) {
|
||||||
|
perror("FAIL - could not wait for semaphore");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return receive_message(qid, qtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(char *prog_name, char *msg)
|
||||||
|
{
|
||||||
|
if (msg != NULL)
|
||||||
|
fprintf(stderr, "%s\n", msg);
|
||||||
|
|
||||||
|
fprintf(stderr, "Usage: %s [options]\n", prog_name);
|
||||||
|
fprintf(stderr, "Options are:\n");
|
||||||
|
fprintf(stderr, "-k message queue key (default is %d)\n", MQ_KEY);
|
||||||
|
fprintf(stderr, "-e message queue type (default is %d)\n", MQ_TYPE);
|
||||||
|
fprintf(stderr, "-c path of the client binary\n");
|
||||||
|
fprintf(stderr, "-u run test as specified UID\n");
|
||||||
|
fprintf(stderr, "-t timeout in seconds\n");
|
||||||
|
fprintf(stderr, "-s semaphore key (default is %d)\n", SEM_KEY);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char opt = 0;
|
||||||
|
char *client = NULL;
|
||||||
|
int uid;
|
||||||
|
int qid;
|
||||||
|
int semid;
|
||||||
|
int rc = EXIT_SUCCESS;
|
||||||
|
const int stringsize = 50;
|
||||||
|
char smqkey[stringsize];
|
||||||
|
char ssemkey[stringsize];
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "k:c:u:t:e:s:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'k':
|
||||||
|
mqkey = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
client = optarg;
|
||||||
|
if (client == NULL)
|
||||||
|
usage(argv[0], "-c option must specify the client binary\n");
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
/* change file mode on output before setuid drops
|
||||||
|
* privs. This is required to make sure we can
|
||||||
|
* write to the output file and in some cases
|
||||||
|
* even exec with our inherited output file
|
||||||
|
*
|
||||||
|
* This assume test infrastructure creates the
|
||||||
|
* file as root and dups stderr to stdout
|
||||||
|
*/
|
||||||
|
if (fchmod(fileno(stdout), 0666) == -1) {
|
||||||
|
perror("FAIL - could not set output file mode");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (fchmod(fileno(stderr), 0666) == -1) {
|
||||||
|
perror("FAIL - could not set output file mode");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
uid = atoi(optarg);
|
||||||
|
if (setuid(uid) < 0) {
|
||||||
|
perror("FAIL - could not setuid");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
timeout = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
mqtype = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
semkey = atoi(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage(argv[0], "Unrecognized option\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
qid = msgget(mqkey, IPC_CREAT | OBJ_PERMS);
|
||||||
|
if (qid == -1) {
|
||||||
|
perror("FAIL - could not msgget");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
semid = semget(semkey, 1, IPC_CREAT | OBJ_PERMS);
|
||||||
|
if (semid == -1) {
|
||||||
|
perror("FAIL - could not get semaphore");
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
goto out_mq;
|
||||||
|
}
|
||||||
|
|
||||||
|
union semun arg;
|
||||||
|
arg.val = 1;
|
||||||
|
if (semctl(semid, 0, SETVAL, arg) == -1) {
|
||||||
|
perror("FAIL - could not get semaphore");
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* exec the client */
|
||||||
|
int pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("FAIL - could not fork");
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
goto out;
|
||||||
|
} else if (!pid) {
|
||||||
|
if (client == NULL) {
|
||||||
|
usage(argv[0], "client not specified");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
/* execution of the main thread continues
|
||||||
|
* in case the client will be manually executed
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
snprintf(smqkey, stringsize - 1, "%d", mqkey);
|
||||||
|
snprintf(ssemkey, stringsize - 1, "%d", semkey);
|
||||||
|
execl(client, client, smqkey, ssemkey, NULL);
|
||||||
|
printf("FAIL %d - execl %s %d - %m\n", getuid(), client, mqkey);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = receive(qid, mqtype, semid);
|
||||||
|
out:
|
||||||
|
if (semctl(semid, 0, IPC_RMID) == -1) {
|
||||||
|
perror("FAIL - could not remove semaphore");
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
out_mq:
|
||||||
|
if (msgctl(qid, IPC_RMID, NULL) < 0) {
|
||||||
|
perror("FAIL - could not remove msg queue");
|
||||||
|
rc = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
55
tests/regression/apparmor/sysv_mq_snd.c
Normal file
55
tests/regression/apparmor/sysv_mq_snd.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include "sysv_mq.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
key_t mqkey = MQ_KEY;
|
||||||
|
key_t semkey = SEM_KEY;
|
||||||
|
long qtype = 1;
|
||||||
|
int qid, semid;
|
||||||
|
struct msg_buf mb;
|
||||||
|
|
||||||
|
if (argc != 1 && argc != 3) {
|
||||||
|
fprintf(stderr, "FAIL sender - specify values for message queue"
|
||||||
|
" key and semaphore key, respectively \n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (argc > 1) {
|
||||||
|
mqkey = atoi(argv[1]);
|
||||||
|
semkey = atoi(argv[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
qid = msgget(mqkey, IPC_CREAT | OBJ_PERMS);
|
||||||
|
if (qid == -1) {
|
||||||
|
perror("FAIL sender - could not msgget");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
semid = semget(semkey, 1, IPC_CREAT | OBJ_PERMS);
|
||||||
|
if (semid == -1) {
|
||||||
|
perror("FAIL sender - could not get semaphore");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(mb.mtext, sizeof(mb.mtext), "%s", msg);
|
||||||
|
mb.mtype = qtype;
|
||||||
|
|
||||||
|
if (msgsnd(qid, &mb, sizeof(struct msg_buf),
|
||||||
|
IPC_NOWAIT) == -1) {
|
||||||
|
perror("FAIL sender - could not msgsnd");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* notify using semaphore */
|
||||||
|
|
||||||
|
struct sembuf sop;
|
||||||
|
sop.sem_num = 0;
|
||||||
|
sop.sem_op = -1;
|
||||||
|
sop.sem_flg = 0;
|
||||||
|
|
||||||
|
if (semop(semid, &sop, 1) == -1) {
|
||||||
|
perror("FAIL sender - could not notify using semaphore");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
@ -51,6 +51,7 @@ from apparmor.rule.network import NetworkRule
|
|||||||
from apparmor.rule.ptrace import PtraceRule
|
from apparmor.rule.ptrace import PtraceRule
|
||||||
from apparmor.rule.signal import SignalRule
|
from apparmor.rule.signal import SignalRule
|
||||||
from apparmor.rule.userns import UserNamespaceRule
|
from apparmor.rule.userns import UserNamespaceRule
|
||||||
|
from apparmor.rule.mqueue import MessageQueueRule
|
||||||
from apparmor.translations import init_translation
|
from apparmor.translations import init_translation
|
||||||
|
|
||||||
_ = init_translation()
|
_ = init_translation()
|
||||||
@ -1728,6 +1729,14 @@ def collapse_log(hashlog, ignore_null_profiles=True):
|
|||||||
if not hat_exists or not is_known_rule(aa[profile][hat], 'userns', userns_event):
|
if not hat_exists or not is_known_rule(aa[profile][hat], 'userns', userns_event):
|
||||||
log_dict[aamode][full_profile]['userns'].add(userns_event)
|
log_dict[aamode][full_profile]['userns'].add(userns_event)
|
||||||
|
|
||||||
|
mqueue = hashlog[aamode][full_profile]['mqueue']
|
||||||
|
for access in mqueue.keys():
|
||||||
|
for mqueue_type in mqueue[access]:
|
||||||
|
for mqueue_name in mqueue[access][mqueue_type]:
|
||||||
|
mqueue_event = MessageQueueRule(access, mqueue_type, MessageQueueRule.ALL, mqueue_name, log_event=True)
|
||||||
|
if not hat_exists or not is_known_rule(aa[profile][hat], 'mqueue', mqueue_event):
|
||||||
|
log_dict[aamode][full_profile]['mqueue'].add(mqueue_event)
|
||||||
|
|
||||||
return log_dict
|
return log_dict
|
||||||
|
|
||||||
|
|
||||||
@ -2104,6 +2113,7 @@ def match_line_against_rule_classes(line, profile, file, lineno, in_preamble):
|
|||||||
'rlimit',
|
'rlimit',
|
||||||
'signal',
|
'signal',
|
||||||
'userns',
|
'userns',
|
||||||
|
'mqueue',
|
||||||
):
|
):
|
||||||
|
|
||||||
if rule_name in ruletypes:
|
if rule_name in ruletypes:
|
||||||
|
@ -58,6 +58,7 @@ class ReadLog:
|
|||||||
'ptrace': hasher(),
|
'ptrace': hasher(),
|
||||||
'signal': hasher(),
|
'signal': hasher(),
|
||||||
'userns': hasher(),
|
'userns': hasher(),
|
||||||
|
'mqueue': hasher(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def prefetch_next_log_entry(self):
|
def prefetch_next_log_entry(self):
|
||||||
@ -188,7 +189,12 @@ class ReadLog:
|
|||||||
elif e['class'] and e['class'] == 'namespace':
|
elif e['class'] and e['class'] == 'namespace':
|
||||||
if e['denied_mask'].startswith('userns'):
|
if e['denied_mask'].startswith('userns'):
|
||||||
self.hashlog[aamode][full_profile]['userns'][e['denied_mask'].removeprefix('userns_')] = True
|
self.hashlog[aamode][full_profile]['userns'][e['denied_mask'].removeprefix('userns_')] = True
|
||||||
return None
|
return
|
||||||
|
|
||||||
|
elif e['class'] and e['class'].endswith('mqueue'):
|
||||||
|
mqueue_type = e['class'].partition('_')[0]
|
||||||
|
self.hashlog[aamode][full_profile]['mqueue'][e['denied_mask']][mqueue_type][e['name']] = True
|
||||||
|
return
|
||||||
|
|
||||||
elif self.op_type(e) == 'file':
|
elif self.op_type(e) == 'file':
|
||||||
# Map c (create) and d (delete) to w (logging is more detailed than the profile language)
|
# Map c (create) and d (delete) to w (logging is more detailed than the profile language)
|
||||||
|
@ -28,6 +28,7 @@ from apparmor.rule.ptrace import PtraceRule, PtraceRuleset
|
|||||||
from apparmor.rule.rlimit import RlimitRule, RlimitRuleset
|
from apparmor.rule.rlimit import RlimitRule, RlimitRuleset
|
||||||
from apparmor.rule.signal import SignalRule, SignalRuleset
|
from apparmor.rule.signal import SignalRule, SignalRuleset
|
||||||
from apparmor.rule.userns import UserNamespaceRule, UserNamespaceRuleset
|
from apparmor.rule.userns import UserNamespaceRule, UserNamespaceRuleset
|
||||||
|
from apparmor.rule.mqueue import MessageQueueRule, MessageQueueRuleset
|
||||||
from apparmor.translations import init_translation
|
from apparmor.translations import init_translation
|
||||||
|
|
||||||
_ = init_translation()
|
_ = init_translation()
|
||||||
@ -44,6 +45,7 @@ ruletypes = {
|
|||||||
'rlimit': {'rule': RlimitRule, 'ruleset': RlimitRuleset},
|
'rlimit': {'rule': RlimitRule, 'ruleset': RlimitRuleset},
|
||||||
'signal': {'rule': SignalRule, 'ruleset': SignalRuleset},
|
'signal': {'rule': SignalRule, 'ruleset': SignalRuleset},
|
||||||
'userns': {'rule': UserNamespaceRule, 'ruleset': UserNamespaceRuleset},
|
'userns': {'rule': UserNamespaceRule, 'ruleset': UserNamespaceRuleset},
|
||||||
|
'mqueue': {'rule': MessageQueueRule, 'ruleset': MessageQueueRuleset},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -188,6 +190,7 @@ class ProfileStorage:
|
|||||||
'network',
|
'network',
|
||||||
'dbus',
|
'dbus',
|
||||||
'mount',
|
'mount',
|
||||||
|
'mqueue',
|
||||||
'signal',
|
'signal',
|
||||||
'ptrace',
|
'ptrace',
|
||||||
'pivot_root',
|
'pivot_root',
|
||||||
|
@ -52,6 +52,7 @@ RE_PROFILE_PTRACE = re.compile(RE_AUDIT_DENY + r'(ptrace\s*,|ptrace(?P<details>\
|
|||||||
RE_PROFILE_PIVOT_ROOT = re.compile(RE_AUDIT_DENY + r'(pivot_root\s*,|pivot_root\s+[^#]*\s*,)' + RE_EOL)
|
RE_PROFILE_PIVOT_ROOT = re.compile(RE_AUDIT_DENY + r'(pivot_root\s*,|pivot_root\s+[^#]*\s*,)' + RE_EOL)
|
||||||
RE_PROFILE_UNIX = re.compile(RE_AUDIT_DENY + r'(unix\s*,|unix\s+[^#]*\s*,)' + RE_EOL)
|
RE_PROFILE_UNIX = re.compile(RE_AUDIT_DENY + r'(unix\s*,|unix\s+[^#]*\s*,)' + RE_EOL)
|
||||||
RE_PROFILE_USERNS = re.compile(RE_AUDIT_DENY + r'(userns\s*,|userns(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
|
RE_PROFILE_USERNS = re.compile(RE_AUDIT_DENY + r'(userns\s*,|userns(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
|
||||||
|
RE_PROFILE_MQUEUE = re.compile(RE_AUDIT_DENY + r'(mqueue\s*,|mqueue(?P<details>\s+[^#]*)\s*,)' + RE_EOL)
|
||||||
|
|
||||||
# match anything that's not " or #, or matching quotes with anything except quotes inside
|
# match anything that's not " or #, or matching quotes with anything except quotes inside
|
||||||
__re_no_or_quoted_hash = '([^#"]|"[^"]*")*'
|
__re_no_or_quoted_hash = '([^#"]|"[^"]*")*'
|
||||||
|
230
utils/apparmor/rule/mqueue.py
Normal file
230
utils/apparmor/rule/mqueue.py
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Copyright (C) 2022 Canonical, Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of version 2 of the GNU General Public
|
||||||
|
# License as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from apparmor.regex import RE_PROFILE_MQUEUE, RE_PROFILE_NAME
|
||||||
|
from apparmor.common import AppArmorBug, AppArmorException
|
||||||
|
from apparmor.rule import BaseRule, BaseRuleset, check_and_split_list, logprof_value_or_all, parse_modifiers, quote_if_needed
|
||||||
|
|
||||||
|
# setup module translations
|
||||||
|
from apparmor.translations import init_translation
|
||||||
|
_ = init_translation()
|
||||||
|
|
||||||
|
|
||||||
|
access_keywords_read = ['r', 'read']
|
||||||
|
access_keywords_write = ['w', 'write']
|
||||||
|
access_keywords_rw = ['rw', 'wr']
|
||||||
|
access_keywords_other = ['create', 'open', 'delete', 'getattr', 'setattr']
|
||||||
|
access_keywords = access_keywords_read + access_keywords_write + access_keywords_rw + access_keywords_other
|
||||||
|
|
||||||
|
joint_access_keyword = r'\s*(' + '|'.join(access_keywords) + r')\s*'
|
||||||
|
RE_ACCESS_KEYWORDS = (joint_access_keyword + # one of the access_keyword or
|
||||||
|
'|' + # or
|
||||||
|
r'\(' + joint_access_keyword + '(' + r'(\s|,)+' + joint_access_keyword + ')*' + r'\)' # one or more access_keyword in (...)
|
||||||
|
)
|
||||||
|
|
||||||
|
RE_MQUEUE_NAME = r'(?P<%s>(/\S+|\d*))' # / + string for posix, or digits for sys
|
||||||
|
RE_MQUEUE_TYPE = r'(?P<%s>(sysv|posix))' # type can be sysv or posix
|
||||||
|
|
||||||
|
RE_MQUEUE_DETAILS = re.compile(
|
||||||
|
'^' +
|
||||||
|
r'(\s+(?P<access>' + RE_ACCESS_KEYWORDS + '))?' + # optional access keyword(s)
|
||||||
|
r'(\s+(type=' + RE_MQUEUE_TYPE % 'mqueue_type' + '))?' + # optional type
|
||||||
|
r'(\s+(label=' + RE_PROFILE_NAME % 'label' + '))?' + # optional label
|
||||||
|
r'(\s+(' + RE_MQUEUE_NAME % 'mqueue_name' + '))?' + # optional mqueue name
|
||||||
|
r'\s*$')
|
||||||
|
|
||||||
|
|
||||||
|
class MessageQueueRule(BaseRule):
|
||||||
|
'''Class to handle and store a single mqueue rule'''
|
||||||
|
|
||||||
|
# Nothing external should reference this class, all external users
|
||||||
|
# should reference the class field MessageQueueRule.ALL
|
||||||
|
class __MessageQueueAll(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
ALL = __MessageQueueAll
|
||||||
|
|
||||||
|
rule_name = 'mqueue'
|
||||||
|
_match_re = RE_PROFILE_MQUEUE
|
||||||
|
|
||||||
|
def __init__(self, access, mqueue_type, label, mqueue_name,
|
||||||
|
audit=False, deny=False, allow_keyword=False,
|
||||||
|
comment='', log_event=None):
|
||||||
|
|
||||||
|
super().__init__(audit=audit, deny=deny,
|
||||||
|
allow_keyword=allow_keyword,
|
||||||
|
comment=comment,
|
||||||
|
log_event=log_event)
|
||||||
|
|
||||||
|
self.access, self.all_access, unknown_items = check_and_split_list(access, access_keywords, self.ALL, type(self).__name__, 'access')
|
||||||
|
if unknown_items:
|
||||||
|
raise AppArmorException(_('Passed unknown access keyword to %s: %s') % (type(self).__name__, ' '.join(unknown_items)))
|
||||||
|
|
||||||
|
self.label, self.all_labels = self._aare_or_all(label, 'label', is_path=False, log_event=log_event)
|
||||||
|
self.mqueue_type, self.all_mqueue_types = self._aare_or_all(mqueue_type, 'type', is_path=False, log_event=log_event)
|
||||||
|
self.mqueue_name, self.all_mqueue_names = self._aare_or_all(mqueue_name, 'mqueue_name', is_path=False, log_event=log_event)
|
||||||
|
self.validate_mqueue_name()
|
||||||
|
|
||||||
|
def validate_mqueue_name(self):
|
||||||
|
# The regex checks if it starts with / or if it's numeric
|
||||||
|
if self.all_mqueue_types or self.all_mqueue_names:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.mqueue_type.regex == 'sysv' and not self.mqueue_name.regex.isnumeric():
|
||||||
|
raise AppArmorException(_('Queue name for SYSV must be a positive integer'))
|
||||||
|
elif self.mqueue_type.regex == 'posix' and not self.mqueue_name.regex.startswith('/'):
|
||||||
|
raise AppArmorException(_('Queue name for POSIX must begin with /'))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _create_instance(cls, raw_rule, matches):
|
||||||
|
'''parse raw_rule and return instance of this class'''
|
||||||
|
|
||||||
|
audit, deny, allow_keyword, comment = parse_modifiers(matches)
|
||||||
|
|
||||||
|
rule_details = ''
|
||||||
|
if matches.group('details'):
|
||||||
|
rule_details = matches.group('details')
|
||||||
|
|
||||||
|
if rule_details:
|
||||||
|
details = RE_MQUEUE_DETAILS.search(rule_details)
|
||||||
|
if not details:
|
||||||
|
raise AppArmorException(_("Invalid or unknown keywords in 'mqueue %s" % rule_details))
|
||||||
|
|
||||||
|
if details.group('access'):
|
||||||
|
access = details.group('access')
|
||||||
|
if access.startswith('(') and access.endswith(')'):
|
||||||
|
access = access[1:-1]
|
||||||
|
access = access.replace(',', ' ').split() # split by ',' or whitespace
|
||||||
|
else:
|
||||||
|
access = cls.ALL
|
||||||
|
|
||||||
|
if details.group('mqueue_type'):
|
||||||
|
mqueue_type = details.group('mqueue_type')
|
||||||
|
else:
|
||||||
|
mqueue_type = cls.ALL
|
||||||
|
|
||||||
|
if details.group('label'):
|
||||||
|
label = details.group('label')
|
||||||
|
else:
|
||||||
|
label = cls.ALL
|
||||||
|
|
||||||
|
if details.group('mqueue_name'):
|
||||||
|
mqueue_name = details.group('mqueue_name')
|
||||||
|
else:
|
||||||
|
mqueue_name = cls.ALL
|
||||||
|
else:
|
||||||
|
access = cls.ALL
|
||||||
|
mqueue_type = cls.ALL
|
||||||
|
label = cls.ALL
|
||||||
|
mqueue_name = cls.ALL
|
||||||
|
|
||||||
|
return cls(access, mqueue_type, label, mqueue_name,
|
||||||
|
audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment)
|
||||||
|
|
||||||
|
def get_clean(self, depth=0):
|
||||||
|
'''return rule (in clean/default formatting)'''
|
||||||
|
|
||||||
|
space = ' ' * depth
|
||||||
|
|
||||||
|
if self.all_access:
|
||||||
|
access = ''
|
||||||
|
elif len(self.access) == 1:
|
||||||
|
access = ' %s' % ' '.join(self.access)
|
||||||
|
elif self.access:
|
||||||
|
access = ' (%s)' % ' '.join(sorted(self.access))
|
||||||
|
else:
|
||||||
|
raise AppArmorBug('Empty access in mqueue rule')
|
||||||
|
|
||||||
|
if self.all_mqueue_types:
|
||||||
|
mqueue_type = ''
|
||||||
|
elif self.mqueue_type:
|
||||||
|
mqueue_type = ' type=%s' % self.mqueue_type.regex
|
||||||
|
else:
|
||||||
|
raise AppArmorBug('Empty type in mqueue rule')
|
||||||
|
|
||||||
|
if self.all_labels:
|
||||||
|
label = ''
|
||||||
|
elif self.label:
|
||||||
|
label = ' label=%s' % quote_if_needed(self.label.regex)
|
||||||
|
else:
|
||||||
|
raise AppArmorBug('Empty label in mqueue rule')
|
||||||
|
|
||||||
|
if self.all_mqueue_names:
|
||||||
|
mqueue_name = ''
|
||||||
|
elif self.mqueue_name:
|
||||||
|
mqueue_name = ' %s' % self.mqueue_name.regex
|
||||||
|
else:
|
||||||
|
raise AppArmorBug('Empty mqueue_name in mqueue rule')
|
||||||
|
|
||||||
|
return('%s%smqueue%s%s%s%s,%s' % (space, self.modifiers_str(), access, mqueue_type, label, mqueue_name, self.comment))
|
||||||
|
|
||||||
|
def _is_covered_localvars(self, other_rule):
|
||||||
|
'''check if other_rule is covered by this rule object'''
|
||||||
|
|
||||||
|
if not self._is_covered_list(self.access, self.all_access, other_rule.access, other_rule.all_access, 'access'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_covered_aare(self.mqueue_type, self.all_mqueue_types, other_rule.mqueue_type, other_rule.all_mqueue_types, 'mqueue_type'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_covered_aare(self.label, self.all_labels, other_rule.label, other_rule.all_labels, 'label'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_covered_aare(self.mqueue_name, self.all_mqueue_names, other_rule.mqueue_name, other_rule.all_mqueue_names, 'mqueue_name'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# still here? -> then it is covered
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _is_equal_localvars(self, rule_obj, strict):
|
||||||
|
'''compare if rule-specific variables are equal'''
|
||||||
|
|
||||||
|
if (self.access != rule_obj.access or
|
||||||
|
self.all_access != rule_obj.all_access):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_equal_aare(self.mqueue_type, self.all_mqueue_types, rule_obj.mqueue_type, rule_obj.all_mqueue_types, 'mqueue_type'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_equal_aare(self.label, self.all_labels, rule_obj.label, rule_obj.all_labels, 'label'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self._is_equal_aare(self.mqueue_name, self.all_mqueue_names, rule_obj.mqueue_name, rule_obj.all_mqueue_names, 'mqueue_name'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _logprof_header_localvars(self):
|
||||||
|
access = logprof_value_or_all(self.access, self.all_access)
|
||||||
|
mqueue_type = logprof_value_or_all(self.mqueue_type, self.all_mqueue_types)
|
||||||
|
label = logprof_value_or_all(self.label, self.all_labels)
|
||||||
|
mqueue_name = logprof_value_or_all(self.mqueue_name, self.all_mqueue_names)
|
||||||
|
|
||||||
|
return (
|
||||||
|
_('Access mode'), access,
|
||||||
|
_('Type'), mqueue_type,
|
||||||
|
_('Label'), label,
|
||||||
|
_('Message queue name'), mqueue_name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageQueueRuleset(BaseRuleset):
|
||||||
|
'''Class to handle and store a collection of mqueue rules'''
|
||||||
|
|
||||||
|
def get_glob(self, path_or_rule):
|
||||||
|
'''Return the next possible glob. For mqueue rules, that means removing access, label or mqueue_name'''
|
||||||
|
# XXX only remove one part, not all
|
||||||
|
return 'mqueue,'
|
258
utils/test/test-mqueue.py
Normal file
258
utils/test/test-mqueue.py
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Copyright (C) 2022 Canonical, Ltd.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of version 2 of the GNU General Public
|
||||||
|
# License as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from collections import namedtuple
|
||||||
|
from common_test import AATest, setup_all_loops
|
||||||
|
|
||||||
|
from apparmor.rule.mqueue import MessageQueueRule, MessageQueueRuleset
|
||||||
|
from apparmor.common import AppArmorException, AppArmorBug
|
||||||
|
from apparmor.translations import init_translation
|
||||||
|
_ = init_translation()
|
||||||
|
|
||||||
|
|
||||||
|
class MessageQueueTestParse(AATest):
|
||||||
|
tests = (
|
||||||
|
# access type label mqueue_name audit deny allow comment
|
||||||
|
('mqueue,' , MessageQueueRule(MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue create,' , MessageQueueRule(('create'), MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue (create,open,delete),' , MessageQueueRule(('create', 'open', 'delete'), MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue (getattr,setattr),' , MessageQueueRule(('getattr', 'setattr'), MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue (write,read),' , MessageQueueRule(('write', 'read'), MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue (open,delete),' , MessageQueueRule(('open', 'delete'), MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue write label=foo,' , MessageQueueRule(('write'), MessageQueueRule.ALL, 'foo', MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue read label=foo /queue,' , MessageQueueRule(('read'), MessageQueueRule.ALL, 'foo', '/queue', False, False, False, '')),
|
||||||
|
('audit mqueue read label=foo /queue,' , MessageQueueRule(('read'), MessageQueueRule.ALL, 'foo', '/queue', True, False, False, '')),
|
||||||
|
('deny mqueue rw label=foo /queue,' , MessageQueueRule(('rw'), MessageQueueRule.ALL, 'foo', '/queue', False, True, False, '')),
|
||||||
|
('audit allow mqueue r label=foo /queue,', MessageQueueRule(('r'), MessageQueueRule.ALL, 'foo', '/queue', True, False, True, '')),
|
||||||
|
('mqueue w label=foo 1234, # cmt' , MessageQueueRule(('w'), MessageQueueRule.ALL, 'foo', '1234', False, False, False, ' # cmt')),
|
||||||
|
('mqueue wr 1234,' , MessageQueueRule(('wr'), MessageQueueRule.ALL, MessageQueueRule.ALL, '1234', False, False, False, '')),
|
||||||
|
('mqueue 1234,' , MessageQueueRule(MessageQueueRule.ALL, MessageQueueRule.ALL, MessageQueueRule.ALL, '1234', False, False, False, '')),
|
||||||
|
('mqueue type=sysv,' , MessageQueueRule(MessageQueueRule.ALL, 'sysv', MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue type=posix,' , MessageQueueRule(MessageQueueRule.ALL, 'posix', MessageQueueRule.ALL, MessageQueueRule.ALL, False, False, False, '')),
|
||||||
|
('mqueue type=sysv 1234,' , MessageQueueRule(MessageQueueRule.ALL, 'sysv', MessageQueueRule.ALL, '1234', False, False, False, '')),
|
||||||
|
('mqueue type=posix /queue,' , MessageQueueRule(MessageQueueRule.ALL, 'posix', MessageQueueRule.ALL, '/queue', False, False, False, '')),
|
||||||
|
('mqueue open type=sysv label=foo 1234,' , MessageQueueRule(('open'), 'sysv', 'foo', '1234', False, False, False, '')),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, rawrule, expected):
|
||||||
|
self.assertTrue(MessageQueueRule.match(rawrule))
|
||||||
|
obj = MessageQueueRule.create_instance(rawrule)
|
||||||
|
expected.raw_rule = rawrule.strip()
|
||||||
|
self.assertTrue(obj.is_equal(expected, True))
|
||||||
|
|
||||||
|
|
||||||
|
class MessageQueueTestParseInvalid(AATest):
|
||||||
|
tests = (
|
||||||
|
('mqueue label=,' , AppArmorException),
|
||||||
|
('mqueue invalidaccess /queuename,', AppArmorException),
|
||||||
|
('mqueue invalidqueuename,' , AppArmorException),
|
||||||
|
('mqueue invalidqueuename1234,' , AppArmorException),
|
||||||
|
('mqueue foo label foo bar,' , AppArmorException),
|
||||||
|
('mqueue type=,' , AppArmorException),
|
||||||
|
('mqueue type=sysv /foo,' , AppArmorException),
|
||||||
|
('mqueue type=posix 1234,' , AppArmorException),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, rawrule, expected):
|
||||||
|
self.assertTrue(MessageQueueRule.match(rawrule)) # the above invalid rules still match the main regex!
|
||||||
|
with self.assertRaises(expected):
|
||||||
|
MessageQueueRule.create_instance(rawrule)
|
||||||
|
|
||||||
|
def test_parse_fail(self):
|
||||||
|
with self.assertRaises(AppArmorException):
|
||||||
|
MessageQueueRule.create_instance('foo,')
|
||||||
|
|
||||||
|
def test_diff_non_mqueuerule(self):
|
||||||
|
exp = namedtuple('exp', ('audit', 'deny'))
|
||||||
|
obj = MessageQueueRule(('open'), 'posix', 'bar', '/foo')
|
||||||
|
with self.assertRaises(AppArmorBug):
|
||||||
|
obj.is_equal(exp(False, False), False)
|
||||||
|
|
||||||
|
def test_diff_access(self):
|
||||||
|
obj1 = MessageQueueRule(('open'), 'posix', 'bar', '/foo')
|
||||||
|
obj2 = MessageQueueRule(('create'), 'posix', 'bar', '/foo')
|
||||||
|
self.assertFalse(obj1.is_equal(obj2, False))
|
||||||
|
|
||||||
|
def test_diff_type(self):
|
||||||
|
obj1 = MessageQueueRule(('open'), 'sysv', 'bar', MessageQueueRule.ALL)
|
||||||
|
obj2 = MessageQueueRule(('open'), 'posix', 'inv', MessageQueueRule.ALL)
|
||||||
|
self.assertFalse(obj1.is_equal(obj2, False))
|
||||||
|
|
||||||
|
def test_diff_label(self):
|
||||||
|
obj1 = MessageQueueRule(('open'), 'posix', 'bar', '/foo')
|
||||||
|
obj2 = MessageQueueRule(('open'), 'posix', 'inv', '/foo')
|
||||||
|
self.assertFalse(obj1.is_equal(obj2, False))
|
||||||
|
|
||||||
|
def test_diff_mqueue_name(self):
|
||||||
|
obj1 = MessageQueueRule(('open'), MessageQueueRule.ALL, 'bar', '/foo')
|
||||||
|
obj2 = MessageQueueRule(('open'), MessageQueueRule.ALL, 'bar', '123')
|
||||||
|
self.assertFalse(obj1.is_equal(obj2, False))
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidMessageQueueInit(AATest):
|
||||||
|
tests = (
|
||||||
|
# init params expected exception
|
||||||
|
(('write', 'sysv', '', '/foo'), AppArmorBug), # empty label
|
||||||
|
(('write', '', 'bar', '/foo'), AppArmorBug), # empty type
|
||||||
|
(('', 'sysv', 'bar', '/foo'), AppArmorBug), # empty access
|
||||||
|
(('write', 'sysv', 'bar', ''), AppArmorBug), # empty mqueue_name
|
||||||
|
((' ', 'sysv', 'bar', '/foo'), AppArmorBug), # whitespace access
|
||||||
|
(('write', ' ', 'bar', '/foo'), AppArmorBug), # whitespace type
|
||||||
|
(('write', 'sysv', ' ', '/foo'), AppArmorBug), # whitespace label
|
||||||
|
(('write', 'sysv', 'bar', ' '), AppArmorBug), # whitespace mqueue_name
|
||||||
|
(('xyxy', 'sysv', 'bar', '/foo'), AppArmorException), # invalid access
|
||||||
|
((dict(), '', 'bar', '/foo'), AppArmorBug), # wrong type for access
|
||||||
|
((None, '', 'bar', '/foo'), AppArmorBug), # wrong type for access
|
||||||
|
(('write', dict(), 'bar', '/foo'), AppArmorBug), # wrong type for type
|
||||||
|
(('write', None, 'bar', '/foo'), AppArmorBug), # wrong type for type
|
||||||
|
(('write', '', dict(), '/foo'), AppArmorBug), # wrong type for label
|
||||||
|
(('write', '', None, '/foo'), AppArmorBug), # wrong type for label
|
||||||
|
(('write', '', 'bar', dict()), AppArmorBug), # wrong type for mqueue_name
|
||||||
|
(('write', '', 'bar', None), AppArmorBug), # wrong type for mqueue_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, params, expected):
|
||||||
|
with self.assertRaises(expected):
|
||||||
|
MessageQueueRule(*params)
|
||||||
|
|
||||||
|
def test_missing_params_1(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
MessageQueueRule()
|
||||||
|
|
||||||
|
def test_missing_params_2(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
MessageQueueRule('r')
|
||||||
|
|
||||||
|
def test_missing_params_3(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
MessageQueueRule('r', 'sysv')
|
||||||
|
|
||||||
|
def test_missing_params_4(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
MessageQueueRule('r', 'sysv', 'foo')
|
||||||
|
|
||||||
|
|
||||||
|
class WriteMessageQueueTestAATest(AATest):
|
||||||
|
tests = (
|
||||||
|
# raw rule clean rule
|
||||||
|
(' mqueue , # foo ' , 'mqueue, # foo'),
|
||||||
|
(' audit mqueue create,' , 'audit mqueue create,'),
|
||||||
|
(' audit mqueue (open ),' , 'audit mqueue open,'),
|
||||||
|
(' audit mqueue (delete , read ),' , 'audit mqueue (delete read),'),
|
||||||
|
(' deny mqueue write label=bar,# foo bar', 'deny mqueue write label=bar, # foo bar'),
|
||||||
|
(' deny mqueue open ,# foo bar' , 'deny mqueue open, # foo bar'),
|
||||||
|
(' allow mqueue label=tst ,# foo bar' , 'allow mqueue label=tst, # foo bar'),
|
||||||
|
('mqueue,' , 'mqueue,'),
|
||||||
|
('mqueue (read),' , 'mqueue read,'),
|
||||||
|
('mqueue (create),' , 'mqueue create,'),
|
||||||
|
('mqueue (write read),' , 'mqueue (read write),'),
|
||||||
|
('mqueue (open,create,open,delete,write,read),' , 'mqueue (create delete open read write),'),
|
||||||
|
('mqueue r,' , 'mqueue r,'),
|
||||||
|
('mqueue w,' , 'mqueue w,'),
|
||||||
|
('mqueue rw,' , 'mqueue rw,'),
|
||||||
|
('mqueue delete label="tst",' , 'mqueue delete label="tst",'),
|
||||||
|
('mqueue (getattr) label=bar,' , 'mqueue getattr label=bar,'),
|
||||||
|
('mqueue getattr /foo,' , 'mqueue getattr /foo,'),
|
||||||
|
('mqueue (setattr getattr) 1234,' , 'mqueue (getattr setattr) 1234,'),
|
||||||
|
('mqueue wr label=tst 1234,' , 'mqueue wr label=tst 1234,'),
|
||||||
|
('mqueue wr type=sysv label=tst 1234,' , 'mqueue wr type=sysv label=tst 1234,'),
|
||||||
|
('mqueue wr type=posix label=tst /foo,' , 'mqueue wr type=posix label=tst /foo,'),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, rawrule, expected):
|
||||||
|
self.assertTrue(MessageQueueRule.match(rawrule))
|
||||||
|
obj = MessageQueueRule.create_instance(rawrule)
|
||||||
|
clean = obj.get_clean()
|
||||||
|
raw = obj.get_raw()
|
||||||
|
|
||||||
|
self.assertEqual(expected.strip(), clean, 'unexpected clean rule')
|
||||||
|
self.assertEqual(rawrule.strip(), raw, 'unexpected raw rule')
|
||||||
|
|
||||||
|
def test_write_manually(self):
|
||||||
|
obj = MessageQueueRule('setattr', 'posix', 'bar', '/foo', allow_keyword=True)
|
||||||
|
|
||||||
|
expected = ' allow mqueue setattr type=posix label=bar /foo,'
|
||||||
|
|
||||||
|
self.assertEqual(expected, obj.get_clean(2), 'unexpected clean rule')
|
||||||
|
self.assertEqual(expected, obj.get_raw(2), 'unexpected raw rule')
|
||||||
|
|
||||||
|
def test_write_invalid_access(self):
|
||||||
|
obj = MessageQueueRule('setattr', 'posix', 'bar', '/foo')
|
||||||
|
obj.access = ''
|
||||||
|
with self.assertRaises(AppArmorBug):
|
||||||
|
obj.get_clean()
|
||||||
|
|
||||||
|
def test_write_invalid_type(self):
|
||||||
|
obj = MessageQueueRule('setattr', 'posix', 'bar', '/foo')
|
||||||
|
obj.mqueue_type = ''
|
||||||
|
with self.assertRaises(AppArmorBug):
|
||||||
|
obj.get_clean()
|
||||||
|
|
||||||
|
def test_write_invalid_label(self):
|
||||||
|
obj = MessageQueueRule('setattr', 'posix', 'bar', '/foo')
|
||||||
|
obj.label = ''
|
||||||
|
with self.assertRaises(AppArmorBug):
|
||||||
|
obj.get_clean()
|
||||||
|
|
||||||
|
def test_write_invalid_mqueue_name(self):
|
||||||
|
obj = MessageQueueRule('setattr', 'posix', 'bar', '/foo')
|
||||||
|
obj.mqueue_name = ''
|
||||||
|
with self.assertRaises(AppArmorBug):
|
||||||
|
obj.get_clean()
|
||||||
|
|
||||||
|
|
||||||
|
class MessageQueueIsCoveredTest(AATest):
|
||||||
|
def test_is_covered(self):
|
||||||
|
obj = MessageQueueRule(('create'), MessageQueueRule.ALL, 'f*', MessageQueueRule.ALL)
|
||||||
|
self.assertTrue(obj.is_covered(MessageQueueRule(('create'), 'sysv', 'f*', '1234')))
|
||||||
|
self.assertTrue(obj.is_covered(MessageQueueRule(('create'), 'posix', 'f*', MessageQueueRule.ALL)))
|
||||||
|
self.assertTrue(obj.is_covered(MessageQueueRule(('create'), 'sysv', 'foo', MessageQueueRule.ALL)))
|
||||||
|
self.assertTrue(obj.is_covered(MessageQueueRule(('create'), 'sysv', 'foo', '1234')))
|
||||||
|
|
||||||
|
def test_is_not_covered(self):
|
||||||
|
obj = MessageQueueRule(('getattr'), 'sysv', 'f*', '1234')
|
||||||
|
self.assertFalse(obj.is_covered(MessageQueueRule(('create'), 'sysv', 'foo', MessageQueueRule.ALL)))
|
||||||
|
self.assertFalse(obj.is_covered(MessageQueueRule(('getattr'), 'posix', 'foo', MessageQueueRule.ALL)))
|
||||||
|
self.assertFalse(obj.is_covered(MessageQueueRule(('getattr'), 'sysv', 'bar', MessageQueueRule.ALL)))
|
||||||
|
self.assertFalse(obj.is_covered(MessageQueueRule(('getattr'), 'sysv', 'foo', '123')))
|
||||||
|
|
||||||
|
|
||||||
|
class MessageQueueLogprofHeaderTest(AATest):
|
||||||
|
tests = (
|
||||||
|
('mqueue,', [ _('Access mode'), _('ALL'), _('Type'), _('ALL'), _('Label'), _('ALL'), _('Message queue name'), _('ALL'), ]),
|
||||||
|
('mqueue (create,getattr) 12,', [ _('Access mode'), 'create getattr', _('Type'), _('ALL'), _('Label'), _('ALL'), _('Message queue name'), '12', ]),
|
||||||
|
('mqueue write label=bar,', [ _('Access mode'), 'write', _('Type'), _('ALL'), _('Label'), 'bar', _('Message queue name'), _('ALL'), ]),
|
||||||
|
('mqueue write type=sysv,', [ _('Access mode'), 'write', _('Type'), 'sysv', _('Label'), _('ALL'), _('Message queue name'), _('ALL'), ]),
|
||||||
|
('mqueue read type=posix,', [ _('Access mode'), 'read', _('Type'), 'posix', _('Label'), _('ALL'), _('Message queue name'), _('ALL'), ]),
|
||||||
|
('deny mqueue read /foo,', [_('Qualifier'), 'deny', _('Access mode'), 'read', _('Type'), _('ALL'), _('Label'), _('ALL'), _('Message queue name'), '/foo', ]),
|
||||||
|
('allow mqueue setattr,', [_('Qualifier'), 'allow', _('Access mode'), 'setattr', _('Type'), _('ALL'), _('Label'), _('ALL'), _('Message queue name'), _('ALL'), ]),
|
||||||
|
('audit mqueue r label=ba 12,', [_('Qualifier'), 'audit', _('Access mode'), 'r', _('Type'), _('ALL'), _('Label'), 'ba', _('Message queue name'), '12', ]),
|
||||||
|
('audit deny mqueue rw,', [_('Qualifier'), 'audit deny', _('Access mode'), 'rw', _('Type'), _('ALL'), _('Label'), _('ALL'), _('Message queue name'), _('ALL'), ]),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _run_test(self, params, expected):
|
||||||
|
obj = MessageQueueRule.create_instance(params)
|
||||||
|
self.assertEqual(obj.logprof_header(), expected)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageQueueGlobTestAATest(AATest):
|
||||||
|
def test_glob(self):
|
||||||
|
self.assertEqual(MessageQueueRuleset().get_glob('mqueue create,'), 'mqueue,')
|
||||||
|
|
||||||
|
|
||||||
|
setup_all_loops(__name__)
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(verbosity=1)
|
Loading…
x
Reference in New Issue
Block a user