summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcragw <[email protected]>2020-11-23 01:32:34 +0800
committerGitHub <[email protected]>2020-11-22 09:32:34 -0800
commitdc6d39a85eccbfdd9b34d89f7ab05436fe3138e5 (patch)
treea331f66dcf7de5e5c6704372847875aafddedb23
parentf1ece319fd701b1e78a98cc17a20b64bbaa8be73 (diff)
pam_zfs_key: accommodate different dataset naming scheme
Name of dataset for user home directory may vary from the expected $homes_prefix/$username, if different naming scheme is being used. We can use property mountpoint to specify the dataset for $username as long as its value is identical to passwd's pw_dir. For example: NAME PROPERTY VALUE rpool/home/myuser_123456 mountpoint /home/myuser Reviewed-by: Felix Dörre <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Crag Wang <[email protected]> Closes #11165
-rw-r--r--contrib/pam_zfs_key/pam_zfs_key.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/contrib/pam_zfs_key/pam_zfs_key.c b/contrib/pam_zfs_key/pam_zfs_key.c
index 0a96f19a3..4cafc37b9 100644
--- a/contrib/pam_zfs_key/pam_zfs_key.c
+++ b/contrib/pam_zfs_key/pam_zfs_key.c
@@ -386,6 +386,8 @@ unmount_unload(pam_handle_t *pamh, const char *ds_name)
typedef struct {
char *homes_prefix;
char *runstatedir;
+ char *homedir;
+ char *dsname;
uid_t uid;
const char *username;
int unmount_and_unload;
@@ -423,6 +425,8 @@ 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 = 1;
+ config->dsname = NULL;
+ config->homedir = NULL;
for (int c = 0; c < argc; c++) {
if (strncmp(argv[c], "homes=", 6) == 0) {
free(config->homes_prefix);
@@ -432,6 +436,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 = 0;
+ } else if (strcmp(argv[c], "prop_mountpoint") == 0) {
+ config->homedir = strdup(entry->pw_dir);
}
}
return (0);
@@ -441,11 +447,59 @@ static void
zfs_key_config_free(zfs_key_config_t *config)
{
free(config->homes_prefix);
+ free(config->runstatedir);
+ free(config->homedir);
+ free(config->dsname);
+}
+
+static int
+find_dsname_by_prop_value(zfs_handle_t *zhp, void *data)
+{
+ zfs_type_t type = zfs_get_type(zhp);
+ zfs_key_config_t *target = data;
+ char mountpoint[ZFS_MAXPROPLEN];
+
+ /* Skip any datasets whose type does not match */
+ if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ /* Skip any datasets whose mountpoint does not match */
+ (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
+ if (strcmp(target->homedir, mountpoint) != 0) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ target->dsname = strdup(zfs_get_name(zhp));
+ zfs_close(zhp);
+ return (1);
}
static char *
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);
+ zfs_close(zhp);
+ return (NULL);
+ }
+
+ (void) zfs_iter_filesystems(zhp, find_dsname_by_prop_value,
+ config);
+ zfs_close(zhp);
+ char *dsname = config->dsname;
+ config->dsname = NULL;
+ return (dsname);
+ }
+
size_t len = ZFS_MAX_DATASET_NAME_LEN;
size_t total_len = strlen(config->homes_prefix) + 1
+ strlen(config->username);