]> git.proxmox.com Git - mirror_zfs.git/commitdiff
PAM: add 'recursive_homes' flag to use with 'prop_mountpoint'
authorVal Packett <val@packett.cool>
Fri, 5 May 2023 22:35:57 +0000 (19:35 -0300)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 1 Jun 2023 00:00:58 +0000 (17:00 -0700)
It's not always desirable to have a fixed flat homes directory.
With the 'recursive_homes' flag, 'prop_mountpoint' search would
traverse the whole tree starting at 'homes' (which can now be '*'
to mean all pools) to find a dataset with a mountpoint matching
the home directory.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Felix Dörre <felix@dogcraft.de>
Signed-off-by: Val Packett <val@packett.cool>
Closes #14834

contrib/pam_zfs_key/pam_zfs_key.c
tests/runfiles/linux.run
tests/zfs-tests/tests/functional/pam/cleanup.ksh
tests/zfs-tests/tests/functional/pam/pam_recursive.ksh [new file with mode: 0755]

index b3086e038e5b7a350a5681d259844af7bae2af56..259ac7a8f1919174f9a1f67711ef0ee5e8f12090 100644 (file)
@@ -438,6 +438,7 @@ typedef struct {
        uid_t uid;
        const char *username;
        boolean_t unmount_and_unload;
+       boolean_t recursive_homes;
 } zfs_key_config_t;
 
 static int
@@ -472,6 +473,7 @@ zfs_key_config_load(pam_handle_t *pamh, zfs_key_config_t *config,
        config->uid = entry->pw_uid;
        config->username = name;
        config->unmount_and_unload = B_TRUE;
+       config->recursive_homes = B_FALSE;
        config->dsname = NULL;
        config->homedir = NULL;
        for (int c = 0; c < argc; c++) {
@@ -483,6 +485,8 @@ zfs_key_config_load(pam_handle_t *pamh, zfs_key_config_t *config,
                        config->runstatedir = strdup(argv[c] + 12);
                } else if (strcmp(argv[c], "nounmount") == 0) {
                        config->unmount_and_unload = B_FALSE;
+               } else if (strcmp(argv[c], "recursive_homes") == 0) {
+                       config->recursive_homes = B_TRUE;
                } else if (strcmp(argv[c], "prop_mountpoint") == 0) {
                        if (config->homedir == NULL)
                                config->homedir = strdup(entry->pw_dir);
@@ -517,8 +521,12 @@ find_dsname_by_prop_value(zfs_handle_t *zhp, void *data)
        (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
            sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
        if (strcmp(target->homedir, mountpoint) != 0) {
+               if (target->recursive_homes) {
+                       (void) zfs_iter_filesystems_v2(zhp, 0,
+                           find_dsname_by_prop_value, target);
+               }
                zfs_close(zhp);
-               return (0);
+               return (target->dsname != NULL);
        }
 
        target->dsname = strdup(zfs_get_name(zhp));
@@ -531,17 +539,23 @@ zfs_key_config_get_dataset(zfs_key_config_t *config)
 {
        if (config->homedir != NULL &&
            config->homes_prefix != NULL) {
-               zfs_handle_t *zhp = zfs_open(g_zfs, config->homes_prefix,
-                   ZFS_TYPE_FILESYSTEM);
-               if (zhp == NULL) {
-                       pam_syslog(NULL, LOG_ERR, "dataset %s not found",
-                           config->homes_prefix);
-                       return (NULL);
-               }
+               if (strcmp(config->homes_prefix, "*") == 0) {
+                       (void) zfs_iter_root(g_zfs,
+                           find_dsname_by_prop_value, config);
+               } else {
+                       zfs_handle_t *zhp = zfs_open(g_zfs,
+                           config->homes_prefix, ZFS_TYPE_FILESYSTEM);
+                       if (zhp == NULL) {
+                               pam_syslog(NULL, LOG_ERR,
+                                   "dataset %s not found",
+                                   config->homes_prefix);
+                               return (NULL);
+                       }
 
-               (void) zfs_iter_filesystems_v2(zhp, 0,
-                   find_dsname_by_prop_value, config);
-               zfs_close(zhp);
+                       (void) zfs_iter_filesystems_v2(zhp, 0,
+                           find_dsname_by_prop_value, config);
+                       zfs_close(zhp);
+               }
                char *dsname = config->dsname;
                config->dsname = NULL;
                return (dsname);
index 4df770d61f07226b379b5cb22f93eeef414107cb..97fc250a7cbfbc25275d071a617c4cdf019af52e 100644 (file)
@@ -140,7 +140,7 @@ tests = ['umount_unlinked_drain']
 tags = ['functional', 'mount']
 
 [tests/functional/pam:Linux]
-tests = ['pam_basic', 'pam_nounmount', 'pam_short_password']
+tests = ['pam_basic', 'pam_nounmount', 'pam_recursive', 'pam_short_password']
 tags = ['functional', 'pam']
 
 [tests/functional/procfs:Linux]
index 971c7fce64e528fd32f8c97a8be256ab8fa97828..dbcb175ed069e84e893ebe3d6de6129312a94f02 100755 (executable)
@@ -25,5 +25,6 @@
 rmconfig
 destroy_pool $TESTPOOL
 del_user ${username}
+del_user ${username}rec
 del_group pamtestgroup
 log_must rm -rf "$runstatedir" $TESTDIRS
diff --git a/tests/zfs-tests/tests/functional/pam/pam_recursive.ksh b/tests/zfs-tests/tests/functional/pam/pam_recursive.ksh
new file mode 100755 (executable)
index 0000000..3714b17
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+. $STF_SUITE/tests/functional/pam/utilities.kshlib
+
+if [ -n "$ASAN_OPTIONS" ]; then
+       export LD_PRELOAD=$(ldd "$(command -v zfs)" | awk '/libasan\.so/ {print $3}')
+fi
+
+username="${username}rec"
+
+# Set up a deeper hierarchy, a mountpoint that doesn't interfere with other tests,
+# and a user which references that mountpoint
+log_must zfs create "$TESTPOOL/pampam"
+log_must zfs create -o mountpoint="$TESTDIR/rec" "$TESTPOOL/pampam/pam"
+echo "recurpass" | zfs create -o encryption=aes-256-gcm -o keyformat=passphrase \
+       -o keylocation=prompt "$TESTPOOL/pampam/pam/${username}"
+log_must zfs unmount "$TESTPOOL/pampam/pam/${username}"
+log_must zfs unload-key "$TESTPOOL/pampam/pam/${username}"
+log_must add_user pamtestgroup ${username} "$TESTDIR/rec"
+
+function keystatus {
+       log_must [ "$(get_prop keystatus "$TESTPOOL/pampam/pam/${username}")" = "$1" ]
+}
+
+log_mustnot ismounted "$TESTPOOL/pampam/pam/${username}"
+keystatus unavailable
+
+function test_session {
+       echo "recurpass" | pamtester ${pamservice} ${username} open_session
+       references 1
+       log_must ismounted "$TESTPOOL/pampam/pam/${username}"
+       keystatus available
+
+       log_must pamtester ${pamservice} ${username} close_session
+       references 0
+       log_mustnot ismounted "$TESTPOOL/pampam/pam/${username}"
+       keystatus unavailable
+}
+
+genconfig "homes=$TESTPOOL/pampam/pam prop_mountpoint runstatedir=${runstatedir}"
+test_session
+
+genconfig "homes=$TESTPOOL/pampam recursive_homes prop_mountpoint runstatedir=${runstatedir}"
+test_session
+
+genconfig "homes=$TESTPOOL recursive_homes prop_mountpoint runstatedir=${runstatedir}"
+test_session
+
+genconfig "homes=* recursive_homes prop_mountpoint runstatedir=${runstatedir}"
+test_session
+
+log_pass "done."