diff options
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 18 | ||||
-rw-r--r-- | src/core/job.c | 12 | ||||
-rw-r--r-- | src/core/job.h | 2 | ||||
-rw-r--r-- | src/resolve/resolved-dns-dnssec.c | 201 | ||||
-rw-r--r-- | src/resolve/resolved-dns-dnssec.h | 1 | ||||
-rw-r--r-- | src/udev/udev-builtin-net_id.c | 11 | ||||
-rw-r--r-- | test/TEST-06-SELINUX/Makefile | 10 | ||||
-rw-r--r-- | test/TEST-06-SELINUX/systemd_test.if | 8 | ||||
-rw-r--r-- | test/TEST-06-SELINUX/systemd_test.te | 50 | ||||
-rwxr-xr-x | test/TEST-06-SELINUX/test-selinux-checks.sh | 13 | ||||
-rwxr-xr-x | test/TEST-06-SELINUX/test.sh | 135 |
12 files changed, 388 insertions, 75 deletions
diff --git a/Makefile.am b/Makefile.am index 27b727294e..0b4a6b9e2e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5184,7 +5184,6 @@ EXTRA_DIST += \ # ------------------------------------------------------------------------------ if ENABLE_RESOLVED -if HAVE_GCRYPT systemd_resolved_SOURCES = \ src/resolve/resolved.c \ @@ -5355,7 +5354,6 @@ test_dnssec_complex_LDADD = \ libshared.la endif -endif gperf_txt_sources += \ src/resolve/dns_type-list.txt diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 386ea96d1b..dca9f77528 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -458,7 +458,10 @@ int bus_unit_method_start_generic( assert(u); assert(job_type >= 0 && job_type < _JOB_TYPE_MAX); - r = mac_selinux_unit_access_check(u, message, job_type == JOB_STOP ? "stop" : "start", error); + r = mac_selinux_unit_access_check( + u, message, + job_type_to_access_method(job_type), + error); if (r < 0) return r; @@ -983,6 +986,13 @@ int bus_unit_queue_job( assert(type >= 0 && type < _JOB_TYPE_MAX); assert(mode >= 0 && mode < _JOB_MODE_MAX); + r = mac_selinux_unit_access_check( + u, message, + job_type_to_access_method(type), + error); + if (r < 0) + return r; + if (reload_if_possible && unit_can_reload(u)) { if (type == JOB_RESTART) type = JOB_RELOAD_OR_START; @@ -990,12 +1000,6 @@ int bus_unit_queue_job( type = JOB_TRY_RELOAD; } - r = mac_selinux_unit_access_check( - u, message, - (type == JOB_START || type == JOB_RESTART || type == JOB_TRY_RESTART) ? "start" : - type == JOB_STOP ? "stop" : "reload", error); - if (r < 0) - return r; if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && diff --git a/src/core/job.c b/src/core/job.c index 4e111ffb46..d8fdf1b53f 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -1240,3 +1240,15 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); + +const char* job_type_to_access_method(JobType t) { + assert(t >= 0); + assert(t < _JOB_TYPE_MAX); + + if (IN_SET(t, JOB_START, JOB_RESTART, JOB_TRY_RESTART)) + return "start"; + else if (t == JOB_STOP) + return "stop"; + else + return "reload"; +} diff --git a/src/core/job.h b/src/core/job.h index 52866fdc48..bbf5471e8b 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -240,3 +240,5 @@ const char* job_result_to_string(JobResult t) _const_; JobResult job_result_from_string(const char *s) _pure_; int job_get_timeout(Job *j, uint64_t *timeout) _pure_; + +const char* job_type_to_access_method(JobType t); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 76c801cce8..21cf161494 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -19,7 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#ifdef HAVE_GCRYPT #include <gcrypt.h> +#endif #include "alloc-util.h" #include "dns-domain.h" @@ -48,19 +50,6 @@ * Normal RR → RRSIG/DNSKEY+ → DS → RRSIG/DNSKEY+ → DS → ... → DS → RRSIG/DNSKEY+ → DS */ -static void initialize_libgcrypt(void) { - const char *p; - - if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) - return; - - p = gcry_check_version("1.4.5"); - assert(p); - - gcry_control(GCRYCTL_DISABLE_SECMEM); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); -} - uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) { const uint8_t *p; uint32_t sum, f; @@ -88,6 +77,70 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) { return sum & UINT32_C(0xFFFF); } +int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { + size_t c = 0; + int r; + + /* Converts the specified hostname into DNSSEC canonicalized + * form. */ + + if (buffer_max < 2) + return -ENOBUFS; + + for (;;) { + r = dns_label_unescape(&n, buffer, buffer_max); + if (r < 0) + return r; + if (r == 0) + break; + + if (buffer_max < (size_t) r + 2) + return -ENOBUFS; + + /* The DNSSEC canonical form is not clear on what to + * do with dots appearing in labels, the way DNS-SD + * does it. Refuse it for now. */ + + if (memchr(buffer, '.', r)) + return -EINVAL; + + ascii_strlower_n(buffer, (size_t) r); + buffer[r] = '.'; + + buffer += r + 1; + c += r + 1; + + buffer_max -= r + 1; + } + + if (c <= 0) { + /* Not even a single label: this is the root domain name */ + + assert(buffer_max > 2); + buffer[0] = '.'; + buffer[1] = 0; + + return 1; + } + + return (int) c; +} + +#ifdef HAVE_GCRYPT + +static void initialize_libgcrypt(void) { + const char *p; + + if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) + return; + + p = gcry_check_version("1.4.5"); + assert(p); + + gcry_control(GCRYCTL_DISABLE_SECMEM); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); +} + static int rr_compare(const void *a, const void *b) { DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b; size_t m; @@ -971,55 +1024,6 @@ int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) { return 0; } -int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { - size_t c = 0; - int r; - - /* Converts the specified hostname into DNSSEC canonicalized - * form. */ - - if (buffer_max < 2) - return -ENOBUFS; - - for (;;) { - r = dns_label_unescape(&n, buffer, buffer_max); - if (r < 0) - return r; - if (r == 0) - break; - - if (buffer_max < (size_t) r + 2) - return -ENOBUFS; - - /* The DNSSEC canonical form is not clear on what to - * do with dots appearing in labels, the way DNS-SD - * does it. Refuse it for now. */ - - if (memchr(buffer, '.', r)) - return -EINVAL; - - ascii_strlower_n(buffer, (size_t) r); - buffer[r] = '.'; - - buffer += r + 1; - c += r + 1; - - buffer_max -= r + 1; - } - - if (c <= 0) { - /* Not even a single label: this is the root domain name */ - - assert(buffer_max > 2); - buffer[0] = '.'; - buffer[1] = 0; - - return 1; - } - - return (int) c; -} - static int digest_to_gcrypt_md(uint8_t algorithm) { /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */ @@ -1882,7 +1886,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r return 0; } -int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) { +static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) { DnsResourceRecord *rr; DnsAnswerFlags flags; int r; @@ -2114,6 +2118,77 @@ int dnssec_test_positive_wildcard( return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated); } +#else + +int dnssec_verify_rrset( + DnsAnswer *a, + const DnsResourceKey *key, + DnsResourceRecord *rrsig, + DnsResourceRecord *dnskey, + usec_t realtime, + DnssecResult *result) { + + return -EOPNOTSUPP; +} + +int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok) { + + return -EOPNOTSUPP; +} + +int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) { + + return -EOPNOTSUPP; +} + +int dnssec_verify_rrset_search( + DnsAnswer *a, + const DnsResourceKey *key, + DnsAnswer *validated_dnskeys, + usec_t realtime, + DnssecResult *result, + DnsResourceRecord **ret_rrsig) { + + return -EOPNOTSUPP; +} + +int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) { + + return -EOPNOTSUPP; +} + +int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { + + return -EOPNOTSUPP; +} + +int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *validated_ds) { + + return -EOPNOTSUPP; +} + +int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { + + return -EOPNOTSUPP; +} + +int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { + + return -EOPNOTSUPP; +} + +int dnssec_test_positive_wildcard( + DnsAnswer *answer, + const char *name, + const char *source, + const char *zone, + bool *authenticated) { + + return -EOPNOTSUPP; +} + +#endif + static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = { [DNSSEC_VALIDATED] = "validated", [DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard", diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h index c99861b8e5..4542f0aa89 100644 --- a/src/resolve/resolved-dns-dnssec.h +++ b/src/resolve/resolved-dns-dnssec.h @@ -94,7 +94,6 @@ typedef enum DnssecNsecResult { int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl); -int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated); int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *source, const char *zone, bool *authenticated); diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 104d5111c5..4f8a759d04 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -36,7 +36,7 @@ * * Type of names: * b<number> -- BCMA bus core number - * ccw<name> -- CCW bus group name + * c<bus_id> -- CCW bus group name, without leading zeros [s390] * o<index>[d<dev_port>] -- on-board device index number * s<slot>[f<function>][d<dev_port>] -- hotplug slot index number * x<MAC> -- MAC address @@ -430,8 +430,15 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) { if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9) return -EINVAL; + /* Strip leading zeros from the bus id for aesthetic purposes. This + * keeps the ccw names stable, yet much shorter in general case of + * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is + * not prepended when it is zero. + */ + bus_id += strspn(bus_id, ".0"); + /* Store the CCW bus-ID for use as network device name */ - rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "ccw%s", bus_id); + rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id); if (rc >= 0 && rc < (int)sizeof(names->ccw_group)) names->type = NET_CCWGROUP; return 0; diff --git a/test/TEST-06-SELINUX/Makefile b/test/TEST-06-SELINUX/Makefile new file mode 100644 index 0000000000..5e89a29eff --- /dev/null +++ b/test/TEST-06-SELINUX/Makefile @@ -0,0 +1,10 @@ +all: + @make -s --no-print-directory -C ../.. all + @basedir=../.. TEST_BASE_DIR=../ ./test.sh --all +setup: + @make --no-print-directory -C ../.. all + @basedir=../.. TEST_BASE_DIR=../ ./test.sh --setup +clean: + @basedir=../.. TEST_BASE_DIR=../ ./test.sh --clean +run: + @basedir=../.. TEST_BASE_DIR=../ ./test.sh --run diff --git a/test/TEST-06-SELINUX/systemd_test.if b/test/TEST-06-SELINUX/systemd_test.if new file mode 100644 index 0000000000..25c91adce9 --- /dev/null +++ b/test/TEST-06-SELINUX/systemd_test.if @@ -0,0 +1,8 @@ +template(`systemd_test_base_template', ` + gen_require(` + attribute systemd_test_domain_type; + ') + + type $1_t, systemd_test_domain_type; + domain_type($1_t) +') diff --git a/test/TEST-06-SELINUX/systemd_test.te b/test/TEST-06-SELINUX/systemd_test.te new file mode 100644 index 0000000000..ff01c09b5e --- /dev/null +++ b/test/TEST-06-SELINUX/systemd_test.te @@ -0,0 +1,50 @@ +policy_module(systemd_test, 0.0.1) + +# declarations +attribute systemd_test_domain_type; + +systemd_test_base_template(systemd_test) +systemd_test_base_template(systemd_test_status) +systemd_test_base_template(systemd_test_start) +systemd_test_base_template(systemd_test_stop) +systemd_test_base_template(systemd_test_reload) + +# systemd_test_domain_type + +require { + role system_r; + role unconfined_r; + type bin_t; + type initrc_t; + type systemd_systemctl_exec_t; + type unconfined_service_t; +} + +role system_r types systemd_test_domain_type; +role unconfined_r types systemd_test_domain_type; + +allow systemd_test_domain_type bin_t: file entrypoint; +allow systemd_test_domain_type systemd_systemctl_exec_t: file entrypoint; +allow initrc_t systemd_test_domain_type: process transition; +allow unconfined_service_t systemd_test_domain_type: process transition; +corecmd_exec_bin(systemd_test_domain_type) +init_signal_script(systemd_test_domain_type) +init_sigchld_script(systemd_test_domain_type) +systemd_exec_systemctl(systemd_test_domain_type) +userdom_use_user_ttys(systemd_test_domain_type) +userdom_use_user_ptys(systemd_test_domain_type) + +optional_policy(` + dbus_system_bus_client(systemd_test_domain_type) + init_dbus_chat(systemd_test_domain_type) +') + +# systemd_test_*_t +require { + type systemd_unit_file_t; +} + +allow systemd_test_status_t systemd_unit_file_t: service { status }; +allow systemd_test_start_t systemd_unit_file_t: service { start }; +allow systemd_test_stop_t systemd_unit_file_t: service { stop }; +allow systemd_test_reload_t systemd_unit_file_t: service { reload }; diff --git a/test/TEST-06-SELINUX/test-selinux-checks.sh b/test/TEST-06-SELINUX/test-selinux-checks.sh new file mode 100755 index 0000000000..08d2ddf4f0 --- /dev/null +++ b/test/TEST-06-SELINUX/test-selinux-checks.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -x +set -e +set -o pipefail + +echo 1 >/sys/fs/selinux/enforce +runcon -t systemd_test_start_t systemctl start hola +runcon -t systemd_test_reload_t systemctl reload hola +runcon -t systemd_test_stop_t systemctl stop hola + +touch /testok +exit 0 diff --git a/test/TEST-06-SELINUX/test.sh b/test/TEST-06-SELINUX/test.sh new file mode 100755 index 0000000000..4f5895be66 --- /dev/null +++ b/test/TEST-06-SELINUX/test.sh @@ -0,0 +1,135 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +TEST_DESCRIPTION="SELinux tests" + +# Requirements: +# Fedora 23 +# selinux-policy-targeted +# selinux-policy-devel + +. $TEST_BASE_DIR/test-functions +SETUP_SELINUX=yes +KERNEL_APPEND="$KERNEL_APPEND selinux=1" + +check_result_qemu() { + ret=1 + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + [[ -e $TESTDIR/root/testok ]] && ret=0 + [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR + cp -a $TESTDIR/root/var/log/journal $TESTDIR + umount $TESTDIR/root + [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed + ls -l $TESTDIR/journal/*/*.journal + test -s $TESTDIR/failed && ret=$(($ret+1)) + return $ret +} + +test_run() { + if run_qemu; then + check_result_qemu || return 1 + else + dwarn "can't run QEMU, skipping" + fi + return 0 +} + +test_setup() { + create_empty_image + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + + # Create what will eventually be our root filesystem onto an overlay + ( + LOG_LEVEL=5 + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + setup_basic_environment + + # setup the testsuite service + cat <<EOF >$initdir/etc/systemd/system/testsuite.service +[Unit] +Description=Testsuite service +After=multi-user.target + +[Service] +ExecStart=/test-selinux-checks.sh +Type=oneshot +EOF + + cat <<EOF >$initdir/etc/systemd/system/hola.service +[Service] +Type=oneshot +ExecStart=/bin/echo Start Hola +ExecReload=/bin/echo Reload Hola +ExecStop=/bin/echo Stop Hola +RemainAfterExit=yes +EOF + + setup_testsuite + + cat <<EOF >$initdir/etc/systemd/system/load-systemd-test-module.service +[Unit] +Description=Load systemd-test module +DefaultDependencies=no +Requires=local-fs.target +Conflicts=shutdown.target +After=local-fs.target +Before=sysinit.target shutdown.target autorelabel.service +ConditionSecurity=selinux +ConditionPathExists=|/.load-systemd-test-module + +[Service] +ExecStart=/bin/sh -x -c 'echo 0 >/sys/fs/selinux/enforce && cd /systemd-test-module && make -f /usr/share/selinux/devel/Makefile load && rm /.load-systemd-test-module' +Type=oneshot +TimeoutSec=0 +RemainAfterExit=yes +EOF + + touch $initdir/.load-systemd-test-module + mkdir -p $initdir/etc/systemd/system/basic.target.wants + ln -fs load-systemd-test-module.service $initdir/etc/systemd/system/basic.target.wants/load-systemd-test-module.service + + local _modules_dir=/var/lib/selinux + rm -rf $initdir/$_modules_dir + if ! cp -ar $_modules_dir $initdir/$_modules_dir; then + dfatal "Failed to copy $_modules_dir" + exit 1 + fi + + local _policy_headers_dir=/usr/share/selinux/devel + rm -rf $initdir/$_policy_headers_dir + inst_dir /usr/share/selinux + if ! cp -ar $_policy_headers_dir $initdir/$_policy_headers_dir; then + dfatal "Failed to copy $_policy_headers_dir" + exit 1 + fi + + mkdir $initdir/systemd-test-module + cp systemd_test.te $initdir/systemd-test-module + cp systemd_test.if $initdir/systemd-test-module + cp test-selinux-checks.sh $initdir + dracut_install -o sesearch + dracut_install runcon + dracut_install checkmodule semodule semodule_package m4 make /usr/libexec/selinux/hll/pp load_policy sefcontext_compile + ) || return 1 + + # mask some services that we do not want to run in these tests + ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.service + ln -s /dev/null $initdir/etc/systemd/system/systemd-networkd.socket + ln -s /dev/null $initdir/etc/systemd/system/systemd-resolved.service + + ddebug "umount $TESTDIR/root" + umount $TESTDIR/root +} + +test_cleanup() { + umount $TESTDIR/root 2>/dev/null + [[ $LOOPDEV ]] && losetup -d $LOOPDEV + return 0 +} + +do_test "$@" |