diff --git a/config/user-selinux.m4 b/config/user-selinux.m4 new file mode 100644 index 000000000000..91eb64ef7b91 --- /dev/null +++ b/config/user-selinux.m4 @@ -0,0 +1,23 @@ +AC_DEFUN([ZFS_AC_CONFIG_USER_SELINUX], [ + AC_ARG_WITH([selinux], + AS_HELP_STRING([--with-selinux=@<:@/usr/share/selinux/devel@:>@], + [build pam_zfs_key module SELinux policy [[default: check]]]), + [ + AS_IF([test "x$with_selinux" = xyes], + with_selinux=/usr/share/selinux/devel) + ], + [with_selinux=no]) + + AS_IF([test "x$with_selinux" != "xno"], [ + AS_IF([test -f "$with_selinux/Makefile"], + [selinux_makefile="$with_selinux/Makefile"], + [ + AC_MSG_FAILURE([ + *** SELinux policy development tools missing. + ]) + with_selinux=no + ] + ) + ]) + AC_SUBST(selinux_makefile) +]) diff --git a/config/user.m4 b/config/user.m4 index f450af47e04d..7b81625cd702 100644 --- a/config/user.m4 +++ b/config/user.m4 @@ -26,6 +26,7 @@ AC_DEFUN([ZFS_AC_CONFIG_USER], [ ZFS_AC_CONFIG_USER_AIO_H ZFS_AC_CONFIG_USER_CLOCK_GETTIME ZFS_AC_CONFIG_USER_PAM + ZFS_AC_CONFIG_USER_SELINUX ZFS_AC_CONFIG_USER_RUNSTATEDIR ZFS_AC_CONFIG_USER_MAKEDEV_IN_SYSMACROS ZFS_AC_CONFIG_USER_MAKEDEV_IN_MKDEV diff --git a/config/zfs-build.m4 b/config/zfs-build.m4 index d516f3d2969f..8b539d90018c 100644 --- a/config/zfs-build.m4 +++ b/config/zfs-build.m4 @@ -282,6 +282,7 @@ AC_DEFUN([ZFS_AC_CONFIG], [ AM_CONDITIONAL([WANT_DEVNAME2DEVID], [test "x$user_libudev" = xyes ]) AM_CONDITIONAL([WANT_MMAP_LIBAIO], [test "x$user_libaio" = xyes ]) AM_CONDITIONAL([PAM_ZFS_ENABLED], [test "x$enable_pam" = xyes]) + AM_CONDITIONAL([PAM_ZFS_SELINUX_ENABLED], [test "x$enable_pam" = xyes -a "x$with_selinux" != xno ]) ]) dnl # diff --git a/configure.ac b/configure.ac index 990958bafa1e..555b0b7de3d8 100644 --- a/configure.ac +++ b/configure.ac @@ -101,6 +101,7 @@ AC_CONFIG_FILES([ contrib/initramfs/scripts/Makefile contrib/initramfs/scripts/local-top/Makefile contrib/pam_zfs_key/Makefile + contrib/pam_zfs_key/selinux/Makefile contrib/pyzfs/Makefile contrib/pyzfs/setup.py contrib/zcp/Makefile diff --git a/contrib/pam_zfs_key/Makefile.am b/contrib/pam_zfs_key/Makefile.am index f0f2550afccb..08ac0d2a9444 100644 --- a/contrib/pam_zfs_key/Makefile.am +++ b/contrib/pam_zfs_key/Makefile.am @@ -17,3 +17,9 @@ pam_zfs_key_la_LDFLAGS = -version-info 1:0:0 -avoid-version -module -shared pam_zfs_key_la_LIBADD += -lpam $(LIBCRYPTO_LIBS) dist_pamconfigs_DATA = zfs_key + +if PAM_ZFS_SELINUX_ENABLED +SUBDIRS = selinux +endif + +DIST_SUBDIRS = selinux diff --git a/contrib/pam_zfs_key/selinux/.gitignore b/contrib/pam_zfs_key/selinux/.gitignore new file mode 100644 index 000000000000..823eb56ce055 --- /dev/null +++ b/contrib/pam_zfs_key/selinux/.gitignore @@ -0,0 +1,2 @@ +/*.pp +/tmp/ diff --git a/contrib/pam_zfs_key/selinux/Makefile.am b/contrib/pam_zfs_key/selinux/Makefile.am new file mode 100644 index 000000000000..b3c65e8f2abc --- /dev/null +++ b/contrib/pam_zfs_key/selinux/Makefile.am @@ -0,0 +1,17 @@ + +refpoldir = $(datadir)/selinux/packages +refpol_DATA = pam_zfs_key.pp + +EXTRA_DIST = pam_zfs_key.te pam_zfs_key.fc + +refpolifdir = $(datadir)/selinux/devel/include/contrib +dist_refpolif_DATA = pam_zfs_key.if + +%.pp: %.te %.fc %.if + $(MAKE) -f $(selinux_makefile) $@ + +clean-local: + $(MAKE) -f $(selinux_makefile) clean + +# Avoid race condition with tmp/all_interfaces.conf +.NOTPARALLEL: %.pp diff --git a/contrib/pam_zfs_key/selinux/pam_zfs_key-selinux.spec b/contrib/pam_zfs_key/selinux/pam_zfs_key-selinux.spec new file mode 100644 index 000000000000..db6f5d28dfca --- /dev/null +++ b/contrib/pam_zfs_key/selinux/pam_zfs_key-selinux.spec @@ -0,0 +1,69 @@ + +%define relabel_files() \ +restorecon -R /usr/lib64/security/pam_zfs_key.so; \ +restorecon -R /dev/zfs; \ +restorecon -R /var/run/pam_zfs_key; \ + +%define selinux_policyver 34.23-1 + +Name: @PACKAGE@ +Version: @VERSION@ +Release: @RELEASE@%{?dist} +Summary: SELinux policy module for pam_zfs_key + +Name: pam_zfs_key-selinux +Version: 1.0 +Release: 1%{?dist} + + +License: @ZFS_META_LICENSE@ +URL: https://github.com/openzfs/zfs +Source0: pam_zfs_key.pp +Source1: pam_zfs_key.if + + +Requires: policycoreutils, libselinux-utils +Requires(post): selinux-policy-base >= %{selinux_policyver}, policycoreutils +Requires(postun): policycoreutils +BuildArch: noarch + +%description +This package installs and sets up the SELinux policy security module for +pam_zfs_key. + +%install +install -d %{buildroot}%{_datadir}/selinux/packages +install -m 644 %{SOURCE0} %{buildroot}%{_datadir}/selinux/packages +install -d %{buildroot}%{_datadir}/selinux/devel/include/contrib +install -m 644 %{SOURCE1} %{buildroot}%{_datadir}/selinux/devel/include/contrib/ +install -d %{buildroot}/etc/selinux/targeted/contexts/users/ + + +%post +semodule -n -i %{_datadir}/selinux/packages/pam_zfs_key.pp +if /usr/sbin/selinuxenabled ; then + /usr/sbin/load_policy + %relabel_files + +fi; +exit 0 + +%postun +if [ $1 -eq 0 ]; then + semodule -n -r pam_zfs_key + if /usr/sbin/selinuxenabled ; then + /usr/sbin/load_policy + %relabel_files + + fi; +fi; +exit 0 + +%files +%attr(0600,root,root) %{_datadir}/selinux/packages/pam_zfs_key.pp +%{_datadir}/selinux/devel/include/contrib/pam_zfs_key.if + + +%changelog +* Thu Jan 13 2022 YOUR NAME 1.0-1 +- Initial version diff --git a/contrib/pam_zfs_key/selinux/pam_zfs_key.fc b/contrib/pam_zfs_key/selinux/pam_zfs_key.fc new file mode 100644 index 000000000000..c42434a5b693 --- /dev/null +++ b/contrib/pam_zfs_key/selinux/pam_zfs_key.fc @@ -0,0 +1 @@ +/var/run/pam_zfs_key(/.*)? gen_context(system_u:object_r:pam_var_run_t,s0) diff --git a/contrib/pam_zfs_key/selinux/pam_zfs_key.if b/contrib/pam_zfs_key/selinux/pam_zfs_key.if new file mode 100644 index 000000000000..e3eb9e533735 --- /dev/null +++ b/contrib/pam_zfs_key/selinux/pam_zfs_key.if @@ -0,0 +1,101 @@ + +## policy for pam_zfs_key + +######################################## +## +## Allow domain to query datasets on ZFS. +## +## +## +## Domain allowed access. +## +## +# +interface(`pam_zfs_key_query',` + gen_require(` + type device_t; + ') + + dnl Using ifdef() keeps `sepolgen-ifgen' from complaining + define(`ZFS_IOC') + ifdef(`ZFS_IOC', ` + define(`ZFS_IOC_POOL_STATS', `0x5a05') + define(`ZFS_IOC_OBJSET_STATS', `0x5a12') + define(`ZFS_IOC_POOL_GET_PROPS', `0x5a27') + define(`ZFS_IOC_LOAD_KEY', `0x5a49') + define(`ZFS_IOC_UNLOAD_KEY', `0x5a4a') + define(`ZFS_IOC_CHANGE_KEY', `0x5a4b') + ') + + allow $1 device_t:chr_file { open ioctl }; + allowxperm $1 device_t:chr_file ioctl ZFS_IOC_OBJSET_STATS; + + # While /dev/zfs does not support read() or write(), libzfs opens it with + # O_RDWR, meaning we need these permissions. Remove these if ZFS changes + # to using the Linux specialization O_PATH (or O_EXEC). + allow $1 device_t:chr_file { read write }; +') + +######################################## +## +## Allow domain to open and close a session on the pam_zfs_key PAM module. +## +## +## +## Domain allowed access. +## +## +# +interface(`pam_zfs_key_session',` + gen_require(` + type device_t; + type fs_t; + type pam_var_run_t; + type user_home_dir_t; + ') + + files_pid_filetrans($1, pam_var_run_t, dir, "pam_zfs_key") + + pam_zfs_key_query($1) + allowxperm $1 device_t:chr_file ioctl { ZFS_IOC_LOAD_KEY ZFS_IOC_UNLOAD_KEY }; + + # The zfs_open() function returns both a zfs handle and a zpool handle for + # its containing pool; as part of the zpool fetching, the function grabs + # the pool stats. Despite marking the pool as unavailable, the library + # will still query the pool properties to check for read-only status. + dontauditxperm $1 device_t:chr_file ioctl ZFS_IOC_POOL_STATS; + + # Needed to determine if the zpool is read-only (if so, the dataset must be + # mounted read-only too). + allowxperm $1 device_t:chr_file ioctl ZFS_IOC_POOL_GET_PROPS; + + allow $1 fs_t:filesystem { mount unmount }; + allow $1 user_home_dir_t:dir mounton; +') + +######################################## +## +## Allow domain to change password on the pam_zfs_key PAM module. +## +## +## +## Domain allowed access. +## +## +# +interface(`pam_zfs_key_password',` + gen_require(` + type device_t; + ') + + pam_zfs_key_query($1) + + # The zfs_open() function returns both a zfs handle and a zpool handle for + # its containing pool; as part of the zpool fetching, the function grabs + # the pool stats. The zpool information does not appear to be used when + # changing the key, but cannot be skipped easily, so silence the denial. + dontauditxperm $1 device_t:chr_file ioctl ZFS_IOC_POOL_STATS; + + auditallowxperm $1 device_t:chr_file ioctl ZFS_IOC_CHANGE_KEY; + allowxperm $1 device_t:chr_file ioctl ZFS_IOC_CHANGE_KEY; +') diff --git a/contrib/pam_zfs_key/selinux/pam_zfs_key.te b/contrib/pam_zfs_key/selinux/pam_zfs_key.te new file mode 100644 index 000000000000..04d745803183 --- /dev/null +++ b/contrib/pam_zfs_key/selinux/pam_zfs_key.te @@ -0,0 +1,17 @@ +policy_module(pam_zfs_key, 1.0.0) + +######################################## +# +# pam_zfs_key local policy +# + +gen_require(` + type local_login_t; + type sshd_t; + + type passwd_t; +') +pam_zfs_key_session(local_login_t) +pam_zfs_key_session(sshd_t) + +pam_zfs_key_password(passwd_t)