Skip to content

Commit 6d2beeb

Browse files
committed
[Draft] pam_zfs_key: Add SELinux policy for PAM module
Provide a policy that allows pam_zfs_key to work on an SELinux protected system without giving excessive access to the PAM module. This way, any exploited application using PAM remains unable to destroy datasets or do anything similarly damaging. Alas, libzfs opens the device with both read & write permissions, so any policy that doesn't label the /dev/zfs device specially could open up other devices to be exploited. Unfortunately, relabeling the device has the potential to impact existing systems, so it is not part of this commit. Signed-off-by: Chris Lindee <[email protected]>
1 parent bc3f12b commit 6d2beeb

File tree

11 files changed

+239
-0
lines changed

11 files changed

+239
-0
lines changed

config/user-selinux.m4

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
AC_DEFUN([ZFS_AC_CONFIG_USER_SELINUX], [
2+
AC_ARG_WITH([selinux],
3+
AS_HELP_STRING([--with-selinux=@<:@/usr/share/selinux/devel@:>@],
4+
[build pam_zfs_key module SELinux policy [[default: check]]]),
5+
[
6+
AS_IF([test "x$with_selinux" = xyes],
7+
with_selinux=/usr/share/selinux/devel)
8+
],
9+
[with_selinux=no])
10+
11+
AS_IF([test "x$with_selinux" != "xno"], [
12+
AS_IF([test -f "$with_selinux/Makefile"],
13+
[selinux_makefile="$with_selinux/Makefile"],
14+
[
15+
AC_MSG_FAILURE([
16+
*** SELinux policy development tools missing.
17+
])
18+
with_selinux=no
19+
]
20+
)
21+
])
22+
AC_SUBST(selinux_makefile)
23+
])

config/user.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ AC_DEFUN([ZFS_AC_CONFIG_USER], [
2626
ZFS_AC_CONFIG_USER_AIO_H
2727
ZFS_AC_CONFIG_USER_CLOCK_GETTIME
2828
ZFS_AC_CONFIG_USER_PAM
29+
ZFS_AC_CONFIG_USER_SELINUX
2930
ZFS_AC_CONFIG_USER_RUNSTATEDIR
3031
ZFS_AC_CONFIG_USER_MAKEDEV_IN_SYSMACROS
3132
ZFS_AC_CONFIG_USER_MAKEDEV_IN_MKDEV

config/zfs-build.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ AC_DEFUN([ZFS_AC_CONFIG], [
282282
AM_CONDITIONAL([WANT_DEVNAME2DEVID], [test "x$user_libudev" = xyes ])
283283
AM_CONDITIONAL([WANT_MMAP_LIBAIO], [test "x$user_libaio" = xyes ])
284284
AM_CONDITIONAL([PAM_ZFS_ENABLED], [test "x$enable_pam" = xyes])
285+
AM_CONDITIONAL([PAM_ZFS_SELINUX_ENABLED], [test "x$enable_pam" = xyes -a "x$with_selinux" != xno ])
285286
])
286287

287288
dnl #

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ AC_CONFIG_FILES([
101101
contrib/initramfs/scripts/Makefile
102102
contrib/initramfs/scripts/local-top/Makefile
103103
contrib/pam_zfs_key/Makefile
104+
contrib/pam_zfs_key/selinux/Makefile
104105
contrib/pyzfs/Makefile
105106
contrib/pyzfs/setup.py
106107
contrib/zcp/Makefile

contrib/pam_zfs_key/Makefile.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,9 @@ pam_zfs_key_la_LDFLAGS = -version-info 1:0:0 -avoid-version -module -shared
1717
pam_zfs_key_la_LIBADD += -lpam $(LIBCRYPTO_LIBS)
1818

1919
dist_pamconfigs_DATA = zfs_key
20+
21+
if PAM_ZFS_SELINUX_ENABLED
22+
SUBDIRS = selinux
23+
endif
24+
25+
DIST_SUBDIRS = selinux
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/*.pp
2+
/tmp/
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
refpoldir = $(datadir)/selinux/packages
3+
refpol_DATA = pam_zfs_key.pp
4+
5+
EXTRA_DIST = pam_zfs_key.te pam_zfs_key.fc
6+
7+
refpolifdir = $(datadir)/selinux/devel/include/contrib
8+
dist_refpolif_DATA = pam_zfs_key.if
9+
10+
%.pp: %.te %.fc %.if
11+
$(MAKE) -f $(selinux_makefile) $@
12+
13+
clean-local:
14+
$(MAKE) -f $(selinux_makefile) clean
15+
16+
# Avoid race condition with tmp/all_interfaces.conf
17+
.NOTPARALLEL: %.pp
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
%define relabel_files() \
3+
restorecon -R /usr/lib64/security/pam_zfs_key.so; \
4+
restorecon -R /dev/zfs; \
5+
restorecon -R /var/run/pam_zfs_key; \
6+
7+
%define selinux_policyver 34.23-1
8+
9+
Name: @PACKAGE@
10+
Version: @VERSION@
11+
Release: @RELEASE@%{?dist}
12+
Summary: SELinux policy module for pam_zfs_key
13+
14+
Name: pam_zfs_key-selinux
15+
Version: 1.0
16+
Release: 1%{?dist}
17+
18+
19+
License: @ZFS_META_LICENSE@
20+
URL: https://github.com/openzfs/zfs
21+
Source0: pam_zfs_key.pp
22+
Source1: pam_zfs_key.if
23+
24+
25+
Requires: policycoreutils, libselinux-utils
26+
Requires(post): selinux-policy-base >= %{selinux_policyver}, policycoreutils
27+
Requires(postun): policycoreutils
28+
BuildArch: noarch
29+
30+
%description
31+
This package installs and sets up the SELinux policy security module for
32+
pam_zfs_key.
33+
34+
%install
35+
install -d %{buildroot}%{_datadir}/selinux/packages
36+
install -m 644 %{SOURCE0} %{buildroot}%{_datadir}/selinux/packages
37+
install -d %{buildroot}%{_datadir}/selinux/devel/include/contrib
38+
install -m 644 %{SOURCE1} %{buildroot}%{_datadir}/selinux/devel/include/contrib/
39+
install -d %{buildroot}/etc/selinux/targeted/contexts/users/
40+
41+
42+
%post
43+
semodule -n -i %{_datadir}/selinux/packages/pam_zfs_key.pp
44+
if /usr/sbin/selinuxenabled ; then
45+
/usr/sbin/load_policy
46+
%relabel_files
47+
48+
fi;
49+
exit 0
50+
51+
%postun
52+
if [ $1 -eq 0 ]; then
53+
semodule -n -r pam_zfs_key
54+
if /usr/sbin/selinuxenabled ; then
55+
/usr/sbin/load_policy
56+
%relabel_files
57+
58+
fi;
59+
fi;
60+
exit 0
61+
62+
%files
63+
%attr(0600,root,root) %{_datadir}/selinux/packages/pam_zfs_key.pp
64+
%{_datadir}/selinux/devel/include/contrib/pam_zfs_key.if
65+
66+
67+
%changelog
68+
* Thu Jan 13 2022 YOUR NAME <YOUR@EMAILADDRESS> 1.0-1
69+
- Initial version
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/var/run/pam_zfs_key(/.*)? gen_context(system_u:object_r:pam_var_run_t,s0)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
2+
## <summary>policy for pam_zfs_key</summary>
3+
4+
########################################
5+
## <summary>
6+
## Allow domain to query datasets on ZFS.
7+
## </summary>
8+
## <param name="domain">
9+
## <summary>
10+
## Domain allowed access.
11+
## </summary>
12+
## </param>
13+
#
14+
interface(`pam_zfs_key_query',`
15+
gen_require(`
16+
type device_t;
17+
')
18+
19+
dnl Using ifdef() keeps `sepolgen-ifgen' from complaining
20+
define(`ZFS_IOC')
21+
ifdef(`ZFS_IOC', `
22+
define(`ZFS_IOC_POOL_STATS', `0x5a05')
23+
define(`ZFS_IOC_OBJSET_STATS', `0x5a12')
24+
define(`ZFS_IOC_POOL_GET_PROPS', `0x5a27')
25+
define(`ZFS_IOC_LOAD_KEY', `0x5a49')
26+
define(`ZFS_IOC_UNLOAD_KEY', `0x5a4a')
27+
define(`ZFS_IOC_CHANGE_KEY', `0x5a4b')
28+
')
29+
30+
allow $1 device_t:chr_file { open ioctl };
31+
allowxperm $1 device_t:chr_file ioctl ZFS_IOC_OBJSET_STATS;
32+
33+
# While /dev/zfs does not support read() or write(), libzfs opens it with
34+
# O_RDWR, meaning we need these permissions. Remove these if ZFS changes
35+
# to using the Linux specialization O_PATH (or O_EXEC).
36+
allow $1 device_t:chr_file { read write };
37+
')
38+
39+
########################################
40+
## <summary>
41+
## Allow domain to open and close a session on the pam_zfs_key PAM module.
42+
## </summary>
43+
## <param name="domain">
44+
## <summary>
45+
## Domain allowed access.
46+
## </summary>
47+
## </param>
48+
#
49+
interface(`pam_zfs_key_session',`
50+
gen_require(`
51+
type device_t;
52+
type fs_t;
53+
type pam_var_run_t;
54+
type user_home_dir_t;
55+
')
56+
57+
files_pid_filetrans($1, pam_var_run_t, dir, "pam_zfs_key")
58+
59+
pam_zfs_key_query($1)
60+
allowxperm $1 device_t:chr_file ioctl { ZFS_IOC_LOAD_KEY ZFS_IOC_UNLOAD_KEY };
61+
62+
# The zfs_open() function returns both a zfs handle and a zpool handle for
63+
# its containing pool; as part of the zpool fetching, the function grabs
64+
# the pool stats. Despite marking the pool as unavailable, the library
65+
# will still query the pool properties to check for read-only status.
66+
dontauditxperm $1 device_t:chr_file ioctl ZFS_IOC_POOL_STATS;
67+
68+
# Needed to determine if the zpool is read-only (if so, the dataset must be
69+
# mounted read-only too).
70+
allowxperm $1 device_t:chr_file ioctl ZFS_IOC_POOL_GET_PROPS;
71+
72+
allow $1 fs_t:filesystem { mount unmount };
73+
allow $1 user_home_dir_t:dir mounton;
74+
')
75+
76+
########################################
77+
## <summary>
78+
## Allow domain to change password on the pam_zfs_key PAM module.
79+
## </summary>
80+
## <param name="domain">
81+
## <summary>
82+
## Domain allowed access.
83+
## </summary>
84+
## </param>
85+
#
86+
interface(`pam_zfs_key_password',`
87+
gen_require(`
88+
type device_t;
89+
')
90+
91+
pam_zfs_key_query($1)
92+
93+
# The zfs_open() function returns both a zfs handle and a zpool handle for
94+
# its containing pool; as part of the zpool fetching, the function grabs
95+
# the pool stats. The zpool information does not appear to be used when
96+
# changing the key, but cannot be skipped easily, so silence the denial.
97+
dontauditxperm $1 device_t:chr_file ioctl ZFS_IOC_POOL_STATS;
98+
99+
auditallowxperm $1 device_t:chr_file ioctl ZFS_IOC_CHANGE_KEY;
100+
allowxperm $1 device_t:chr_file ioctl ZFS_IOC_CHANGE_KEY;
101+
')

0 commit comments

Comments
 (0)