Commit 80654389 authored by Simon McVittie's avatar Simon McVittie

Add an integration test for AppArmor mediating activation

This requires libapparmor 2.10, for aa_features_new_from_kernel()
and related functions.
Signed-off-by: default avatarSimon McVittie <simon.mcvittie@collabora.co.uk>
Reviewed-by: Philip Withnall's avatarPhilip Withnall <philip.withnall@collabora.co.uk>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=98666
parent dc25979e
......@@ -1066,7 +1066,7 @@ fi
AS_IF([test x$enable_apparmor = xno],
[have_apparmor=no],
[
PKG_CHECK_MODULES([APPARMOR], [libapparmor >= 2.8.95],
PKG_CHECK_MODULES([APPARMOR], [libapparmor >= 2.10],
[have_apparmor=yes], [have_apparmor=no])
AS_IF([test x$enable_apparmor = xauto && test x$have_apparmor = xno],
......
......@@ -12,6 +12,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
$(DBUS_STATIC_BUILD_CPPFLAGS) \
-DDBUS_COMPILATION \
$(APPARMOR_CFLAGS) \
$(GLIB_CFLAGS) \
$(GIO_UNIX_CFLAGS) \
$(NULL)
......@@ -138,6 +139,8 @@ dist_testexec_SCRIPTS =
testexec_PROGRAMS =
testmeta_DATA =
installable_helpers = \
$(NULL)
installable_tests = \
test-shell \
test-printf \
......@@ -149,6 +152,8 @@ installable_manual_tests = \
$(NULL)
dist_installable_test_scripts = \
$(NULL)
dist_installed_test_scripts = \
$(NULL)
if DBUS_WIN
installable_manual_tests += manual-paths
......@@ -171,6 +176,11 @@ installable_tests += \
$(NULL)
if DBUS_UNIX
# These binaries are used in tests but are not themselves tests
installable_helpers += \
test-apparmor-activation \
$(NULL)
installable_tests += \
test-sd-activation \
$(NULL)
......@@ -179,6 +189,11 @@ dist_installable_test_scripts += \
test-dbus-daemon-fork.sh \
$(NULL)
# Only runnable when installed, not from the source tree
dist_installed_test_scripts += \
test-apparmor-activation.sh \
$(NULL)
# Testing dbus-launch relies on special code in that binary.
if DBUS_ENABLE_EMBEDDED_TESTS
dist_installable_test_scripts += \
......@@ -201,10 +216,12 @@ endif DBUS_WITH_GLIB
installable_test_meta = \
$(dist_installable_test_scripts:=.test) \
$(dist_installed_test_scripts:=.test) \
$(installable_tests:=.test) \
$(NULL)
installable_test_meta_with_config = \
$(dist_installable_test_scripts:=_with_config.test) \
$(dist_installed_test_scripts:=_with_config.test) \
$(installable_tests:=_with_config.test) \
$(NULL)
......@@ -236,6 +253,21 @@ manual_authz_LDADD = \
$(GLIB_LIBS) \
$(NULL)
if DBUS_UNIX
test_apparmor_activation_CPPFLAGS = \
$(AM_CPPFLAGS) \
-DDBUS_TEST_APPARMOR_ACTIVATION \
$(NULL)
test_apparmor_activation_SOURCES = \
sd-activation.c \
$(NULL)
test_apparmor_activation_LDADD = \
libdbus-testutils.la \
$(APPARMOR_LIBS) \
$(GLIB_LIBS) \
$(NULL)
endif
test_corrupt_SOURCES = corrupt.c
test_corrupt_LDADD = \
libdbus-testutils.la \
......@@ -321,7 +353,10 @@ TESTS += $(installable_tests)
installcheck_tests += $(installable_tests)
if DBUS_ENABLE_INSTALLED_TESTS
testexec_PROGRAMS += $(installable_tests) $(installable_manual_tests)
testexec_PROGRAMS += $(installable_helpers)
testexec_PROGRAMS += $(installable_manual_tests)
testexec_PROGRAMS += $(installable_tests)
dist_testexec_SCRIPTS += $(dist_installed_test_scripts)
dist_testexec_SCRIPTS += $(dist_installable_test_scripts)
testmeta_DATA += $(installable_test_meta)
......@@ -347,6 +382,9 @@ if DBUS_ENABLE_INSTALLED_TESTS
endif DBUS_ENABLE_INSTALLED_TESTS
in_data = \
data/dbus-installed-tests.aaprofile.in \
data/systemd-activation/com.example.ReceiveDeniedByAppArmorLabel.service.in \
data/systemd-activation/com.example.SendDeniedByAppArmorLabel.service.in \
data/valid-config-files-system/debug-allow-all-fail.conf.in \
data/valid-config-files-system/debug-allow-all-pass.conf.in \
data/valid-config-files/debug-allow-all-sha1.conf.in \
......@@ -432,6 +470,7 @@ static_data = \
data/sha-1/byte-messages.sha1 \
data/systemd-activation/com.example.ReceiveDenied.service \
data/systemd-activation/com.example.SendDenied.service \
data/systemd-activation/com.example.SendDeniedByAppArmorName.service \
data/systemd-activation/com.example.SystemdActivatable1.service \
data/systemd-activation/com.example.SystemdActivatable2.service \
data/systemd-activation/com.example.SystemdActivatable3.service \
......@@ -574,7 +613,7 @@ $(installable_test_meta_with_config): %_with_config.test: %$(EXEEXT) Makefile
echo '[Test]'; \
echo 'Type=session'; \
echo 'Output=TAP'; \
echo 'Exec=env DBUS_TEST_DATA=$(testexecdir)/data $(testexecdir)/$* --tap'; \
echo 'Exec=env DBUS_TEST_EXEC=$(testexecdir) DBUS_TEST_DATA=$(testexecdir)/data $(testexecdir)/$* --tap'; \
) > $@.tmp && mv $@.tmp $@
# Add rules for code-coverage testing, as defined by AX_CODE_COVERAGE
......
# Copyright © 2016 Collabora Ltd.
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#include <tunables/global>
#include <tunables/sys>
@DBUS_TEST_EXEC@/test-apparmor-activation {
#include <abstractions/apparmor_api/change_profile>
#include <abstractions/apparmor_api/find_mountpoint>
#include <abstractions/apparmor_api/is_enabled>
#include <abstractions/base>
# We aren't really confining this process seriously; allow most things.
/** mrix,
@{sys}/kernel/security/apparmor/** w,
dbus (send, receive, bind),
network,
signal,
# "Hat" subprofile used for the part of the process that imitates a client
# trying to cause service activation via auto-starting.
^caller {
#include <abstractions/apparmor_api/change_profile>
#include <abstractions/base>
/** mrix,
@{sys}/kernel/security/apparmor/** w,
dbus (send, receive, bind),
network,
signal,
deny dbus send peer=(label=@DBUS_TEST_EXEC@/test-apparmor-activation//com.example.SendDeniedByAppArmorLabel),
deny dbus send peer=(name=com.example.SendDeniedByAppArmorName),
}
# Used when we check that XML-based policy still works.
^com.example.ReceiveDenied {
#include <abstractions/apparmor_api/change_profile>
#include <abstractions/base>
/** mrix,
@{sys}/kernel/security/apparmor/** w,
dbus,
network,
signal,
}
# This one is never actually used, but needs to exist so ^caller can
# refer to it
^com.example.SendDeniedByAppArmorLabel {
#include <abstractions/apparmor_api/change_profile>
#include <abstractions/base>
/** mrix,
@{sys}/kernel/security/apparmor/** w,
dbus (send, receive, bind),
network,
signal,
}
# "Hat" subprofile used for the part of the process that imitates a service
# that is not allowed to receive from the caller.
^com.example.ReceiveDeniedByAppArmorLabel {
#include <abstractions/apparmor_api/change_profile>
#include <abstractions/base>
/** mrix,
@{sys}/kernel/security/apparmor/** w,
dbus (send, receive, bind),
network,
signal,
deny dbus receive peer=(label=@DBUS_TEST_EXEC@/test-apparmor-activation//caller),
}
}
[D-BUS Service]
Name=com.example.ReceiveDeniedByAppArmorLabel
Exec=/bin/false ReceiveDeniedByAppArmorLabel
SystemdService=dbus-com.example.ReceiveDeniedByAppArmorLabel.service
AssumedAppArmorLabel=@DBUS_TEST_EXEC@/test-apparmor-activation//com.example.ReceiveDeniedByAppArmorLabel
[D-BUS Service]
Name=com.example.SendDeniedByAppArmorLabel
Exec=/bin/false SendDeniedByAppArmorLabel
SystemdService=dbus-com.example.SendDeniedByAppArmorLabel.service
AssumedAppArmorLabel=@DBUS_TEST_EXEC@/test-apparmor-activation//com.example.SendDeniedByAppArmorLabel
[D-BUS Service]
Name=com.example.SendDeniedByAppArmorName
Exec=/bin/false SendDeniedByAppArmorName
SystemdService=dbus-com.example.SendDeniedByAppArmorName.service
/* Unit tests for systemd activation.
/* Unit tests for systemd activation, with or without AppArmor.
*
* We compile this source file twice: once with AppArmor support (if available)
* and once without.
*
* Copyright © 2010-2011 Nokia Corporation
* Copyright © 2015 Collabora Ltd.
......@@ -26,7 +29,14 @@
#include <config.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#if defined(HAVE_APPARMOR) && defined(DBUS_TEST_APPARMOR_ACTIVATION)
#include <sys/apparmor.h>
#endif
#include "test-utils-glib.h"
......@@ -196,6 +206,40 @@ static void
setup (Fixture *f,
gconstpointer context G_GNUC_UNUSED)
{
#if defined(DBUS_TEST_APPARMOR_ACTIVATION) && !defined(HAVE_APPARMOR)
g_test_skip ("AppArmor support not compiled");
return;
#else
#if defined(DBUS_TEST_APPARMOR_ACTIVATION)
aa_features *features;
if (!aa_is_enabled ())
{
g_test_message ("aa_is_enabled() -> %s", g_strerror (errno));
g_test_skip ("AppArmor not enabled");
return;
}
if (aa_features_new_from_kernel (&features) != 0)
{
g_test_skip ("Unable to check AppArmor features");
return;
}
if (!aa_features_supports (features, "dbus/mask/send") ||
!aa_features_supports (features, "dbus/mask/receive"))
{
g_test_skip ("D-Bus send/receive mediation unavailable");
aa_features_unref (features);
return;
}
aa_features_unref (features);
#endif
f->ctx = test_main_context_get ();
f->ge = NULL;
......@@ -208,8 +252,31 @@ setup (Fixture *f,
if (f->address == NULL)
return;
#if defined(DBUS_TEST_APPARMOR_ACTIVATION)
/*
* Make use of the fact that the LSM security label (and other process
* properties) that are used for access-control are whatever was current
* at the time the connection was opened.
*
* 42 is arbitrary. In a real use of AppArmor it would be a securely-random
* value, to prevent less-privileged code (that does not know the magic
* value) from changing back.
*/
if (aa_change_hat ("caller", 42) != 0)
g_error ("Unable to change profile to ...//^caller: %s",
g_strerror (errno));
#endif
f->caller = test_connect_to_bus (f->ctx, f->address);
f->caller_name = dbus_bus_get_unique_name (f->caller);
#if defined(DBUS_TEST_APPARMOR_ACTIVATION)
if (aa_change_hat (NULL, 42) != 0)
g_error ("Unable to change back to initial profile: %s",
g_strerror (errno));
#endif
#endif
}
static void
......@@ -557,6 +624,7 @@ test_deny_send (Fixture *f,
gconstpointer context)
{
DBusMessage *m;
const char *bus_name = context;
if (f->address == NULL)
return;
......@@ -567,7 +635,7 @@ test_deny_send (Fixture *f,
f->caller_filter_added = TRUE;
/* The sender sends a message to an activatable service. */
m = dbus_message_new_method_call ("com.example.SendDenied", "/foo",
m = dbus_message_new_method_call (bus_name, "/foo",
"com.example.bar", "Call");
if (m == NULL)
g_error ("OOM");
......@@ -575,8 +643,20 @@ test_deny_send (Fixture *f,
dbus_connection_send (f->caller, m, NULL);
dbus_message_unref (m);
/* Even before the fake systemd connects to the bus, we get an error
* back: activation is not allowed. */
/*
* Even before the fake systemd connects to the bus, we get an error
* back: activation is not allowed.
*
* In the normal case, this is because the XML policy does not allow
* anyone to send messages to the bus name com.example.SendDenied.
*
* In the AppArmor case, this is because the AppArmor policy does not allow
* this process to send messages to the bus name
* com.example.SendDeniedByAppArmorName, or to the label
* @DBUS_TEST_EXEC@/com.example.SendDeniedByAppArmorLabel that we assume the
* service com.example.SendDeniedByAppArmorLabel will receive after systemd
* runs it.
*/
while (f->caller_message == NULL)
test_main_context_iterate (f->ctx, TRUE);
......@@ -593,6 +673,7 @@ test_deny_receive (Fixture *f,
gconstpointer context)
{
DBusMessage *m;
const char *bus_name = context;
if (f->address == NULL)
return;
......@@ -602,9 +683,10 @@ test_deny_receive (Fixture *f,
f->caller_filter_added = TRUE;
/* The sender sends a message to an activatable service. */
m = dbus_message_new_method_call ("com.example.ReceiveDenied", "/foo",
"com.example.ReceiveDenied", "Call");
/* The sender sends a message to an activatable service.
* We set the interface name equal to the bus name to make it
* easier to write the necessary policy rules. */
m = dbus_message_new_method_call (bus_name, "/foo", bus_name, "Call");
if (m == NULL)
g_error ("OOM");
......@@ -631,16 +713,44 @@ test_deny_receive (Fixture *f,
dbus_message_unref (m);
/* systemd starts the activatable service. */
#if defined(DBUS_TEST_APPARMOR_ACTIVATION) && defined(HAVE_APPARMOR)
/* The use of 42 here is arbitrary, see setup(). */
if (aa_change_hat (bus_name, 42) != 0)
g_error ("Unable to change profile to ...//^%s: %s",
bus_name, g_strerror (errno));
#endif
f->activated = test_connect_to_bus (f->ctx, f->address);
if (!dbus_connection_add_filter (f->activated, activated_filter,
f, NULL))
g_error ("OOM");
f->activated_filter_added = TRUE;
f->activated_name = dbus_bus_get_unique_name (f->activated);
take_well_known_name (f, f->activated, "com.example.ReceiveDenied");
/* We re-do the message matching, and now the message is
* forbidden by the receive policy. */
take_well_known_name (f, f->activated, bus_name);
#if defined(DBUS_TEST_APPARMOR_ACTIVATION) && defined(HAVE_APPARMOR)
if (aa_change_hat (NULL, 42) != 0)
g_error ("Unable to change back to initial profile: %s",
g_strerror (errno));
#endif
/*
* We re-do the message matching, and now the message is
* forbidden by the receive policy.
*
* In the normal case, this is because the XML policy does not allow
* receiving any message with interface com.example.ReceiveDenied.
* We can't use the recipient's bus name here because the XML policy
* has no syntax for preventing the owner of a name from receiving
* messages - that would be pointless, because the sender could just
* open another connection and not own the same name on that connection.
*
* In the AppArmor case, this is because the AppArmor policy does not allow
* receiving messages with interface com.example.ReceiveDeniedByAppArmor
* from a peer with the same label we have. Again, we can't use the
* recipient's bus name because there is no syntax for this.
*/
while (f->caller_message == NULL)
test_main_context_iterate (f->ctx, TRUE);
......@@ -707,10 +817,24 @@ main (int argc,
setup, test_activation, teardown);
g_test_add ("/sd-activation/uae", Fixture, NULL,
setup, test_uae, teardown);
g_test_add ("/sd-activation/deny-send", Fixture, NULL,
g_test_add ("/sd-activation/deny-send", Fixture,
"com.example.SendDenied",
setup, test_deny_send, teardown);
g_test_add ("/sd-activation/deny-receive", Fixture,
"com.example.ReceiveDenied",
setup, test_deny_receive, teardown);
#if defined(DBUS_TEST_APPARMOR_ACTIVATION)
g_test_add ("/sd-activation/apparmor/deny-send/by-label", Fixture,
"com.example.SendDeniedByAppArmorLabel",
setup, test_deny_send, teardown);
g_test_add ("/sd-activation/apparmor/deny-send/by-name", Fixture,
"com.example.SendDeniedByAppArmorName",
setup, test_deny_send, teardown);
g_test_add ("/sd-activation/deny-receive", Fixture, NULL,
g_test_add ("/sd-activation/apparmor/deny-receive/by-label", Fixture,
"com.example.ReceiveDeniedByAppArmorLabel",
setup, test_deny_receive, teardown);
#endif
return g_test_run ();
}
#!/bin/sh
# Copyright © 2016 Collabora Ltd.
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
set -e
if [ "x$(id -u)" != "x0" ]; then
echo "1..0 # SKIP - this test can only be run as root"
exit 0
fi
if [ -z "$DBUS_TEST_EXEC" ]; then
DBUS_TEST_EXEC="$(dirname "$0")"
if ! [ -x "$DBUS_TEST_EXEC/test-apparmor-activation" ]; then
echo "1..0 # SKIP - executable not found and DBUS_TEST_EXEC not set"
exit 0
fi
fi
if [ -z "$DBUS_TEST_DATA" ]; then
DBUS_TEST_DATA="$DBUS_TEST_EXEC/data"
if ! [ -e "$DBUS_TEST_DATA/dbus-installed-tests.aaprofile" ]; then
echo "1..0 # SKIP - required data not found and DBUS_TEST_DATA not set"
exit 0
fi
fi
echo "# Attempting to load AppArmor profiles"
if ! apparmor_parser --skip-cache --replace \
"$DBUS_TEST_DATA/dbus-installed-tests.aaprofile"; then
echo "1..0 # SKIP - unable to load AppArmor profiles"
exit 0
fi
exec "$DBUS_TEST_EXEC/test-apparmor-activation" --tap "$@"
# vim:set sts=4 sw=4 et:
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment