2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <libzfs.h>
2N/A#include <libzfs_impl.h>
2N/A#include <sys/zfs_ioctl.h>
2N/A
2N/A#include <libgen.h>
2N/A#include <nss_dbdefs.h>
2N/A#include <pwd.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <syslog.h>
2N/A#include <unistd.h>
2N/A#include <errno.h>
2N/A#include <strings.h>
2N/A#include <sys/mount.h>
2N/A#include <libintl.h>
2N/A#include <fcntl.h>
2N/A#include <sys/types.h>
2N/A#include <sys/wait.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/statvfs.h>
2N/A#include <sys/mntent.h>
2N/A
2N/A#include <security/pam_appl.h>
2N/A#include <security/pam_modules.h>
2N/A#include <security/pam_impl.h>
2N/A
2N/A#define _PATH_EXPORT_HOME "/export/home"
2N/A#define DEFAULT_HOMES "rpool/export/home"
2N/A#define DEFAULT_ENCRYPTION "on"
2N/A
2N/A/*ARGSUSED*/
2N/Aint
2N/Apam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
2N/A{
2N/A return (PAM_IGNORE);
2N/A}
2N/A
2N/A/*PRINTFLIKE3*/
2N/Astatic void
2N/Adisplay_msg(pam_handle_t *pamh, int msg_style, char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A char messages[1][PAM_MAX_MSG_SIZE];
2N/A
2N/A va_start(ap, fmt);
2N/A (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap);
2N/A (void) __pam_display_msg(pamh, msg_style, 1, messages, NULL);
2N/A va_end(ap);
2N/A}
2N/A
2N/Astatic void
2N/Aprompt_for_passphrase(zfs_handle_t *zhp, pam_handle_t *pamh)
2N/A{
2N/A char *passphrase;
2N/A char prompt[PAM_MAX_MSG_SIZE];
2N/A
2N/A (void) snprintf(prompt, sizeof (prompt),
2N/A dgettext(TEXT_DOMAIN, "Enter passphrase for ZFS filesystem %s:"),
2N/A zfs_get_name(zhp));
2N/A if (__pam_get_authtok(pamh, PAM_PROMPT, 0, prompt,
2N/A &passphrase) != PAM_SUCCESS)
2N/A return;
2N/A
2N/A zfs_crypto_set_key(zhp, passphrase, strlen(passphrase));
2N/A}
2N/A
2N/Astatic int
2N/Aset_delegation(zfs_handle_t *zhp, uid_t uid)
2N/A{
2N/A int ret;
2N/A nvlist_t *fsaclnv;
2N/A nvlist_t *acenv;
2N/A size_t nvsz;
2N/A char *nvbuf;
2N/A zfs_cmd_t zc = { 0 };
2N/A char acewho[13];
2N/A
2N/A (void) nvlist_alloc(&fsaclnv, NV_UNIQUE_NAME, 0);
2N/A (void) nvlist_alloc(&acenv, NV_UNIQUE_NAME, 0);
2N/A
2N/A (void) nvlist_add_boolean_value(acenv, "key", B_TRUE);
2N/A (void) nvlist_add_boolean_value(acenv, "keychange", B_TRUE);
2N/A (void) nvlist_add_boolean_value(acenv, "mount", B_TRUE);
2N/A
2N/A (void) snprintf(acewho, sizeof (acewho), "ul$%u", uid);
2N/A (void) nvlist_add_nvlist(fsaclnv, acewho, acenv);
2N/A (void) snprintf(acewho, sizeof (acewho), "ud$%u", uid);
2N/A (void) nvlist_add_nvlist(fsaclnv, acewho, acenv);
2N/A
2N/A (void) nvlist_size(fsaclnv, &nvsz, NV_ENCODE_NATIVE);
2N/A nvbuf = malloc(nvsz);
2N/A (void) nvlist_pack(fsaclnv, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
2N/A (void) strlcpy(zc.zc_name, zfs_get_name(zhp), sizeof (zc.zc_name));
2N/A zc.zc_nvlist_src_size = nvsz;
2N/A zc.zc_nvlist_src = (uintptr_t)nvbuf;
2N/A zc.zc_perm_action = 0;
2N/A
2N/A ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SET_FSACL, &zc);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Astatic void
2N/Aget_default_homes(char *homes, size_t homes_len)
2N/A{
2N/A FILE *mtab;
2N/A struct mnttab mget;
2N/A struct mnttab mref;
2N/A int err;
2N/A
2N/A mtab = fopen(MNTTAB, "r");
2N/A if (mtab == NULL) {
2N/A (void) strlcpy(homes, DEFAULT_HOMES, homes_len);
2N/A return;
2N/A }
2N/A
2N/A mntnull(&mref);
2N/A mref.mnt_mountp = (char *)_PATH_EXPORT_HOME;
2N/A mref.mnt_fstype = MNTTYPE_ZFS;
2N/A err = getmntany(mtab, &mget, &mref);
2N/A (void) fclose(mtab);
2N/A if (err == 0 && mget.mnt_special != NULL) {
2N/A (void) strlcpy(homes, mget.mnt_special, homes_len);
2N/A } else {
2N/A (void) strlcpy(homes, DEFAULT_HOMES, homes_len);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * zfs_key - pam_sm_setcred
2N/A *
2N/A * Entry flags = PAM_ESTABLISH_CRED, load key
2N/A * PAM_DELETE_CRED, unload key
2N/A * PAM_REINITIALIZE_CRED NOOP
2N/A * PAM_REFRESH_CRED NOOP
2N/A * PAM_SILENT, print no messages to user.
2N/A *
2N/A * Returns PAM_SUCCESS, if all successful.
2N/A * PAM_CRED_ERR, if unable to set credentials.
2N/A * PAM_USER_UNKNOWN, if PAM_USER not set, or unable to find
2N/A * user in databases.
2N/A * PAM_SYSTEM_ERR, if no valid flag, or unable to get/set
2N/A * user's audit state.
2N/A */
2N/A
2N/Aint
2N/Apam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
2N/A{
2N/A int i;
2N/A boolean_t debug = B_FALSE;
2N/A boolean_t force = B_FALSE;
2N/A boolean_t create = B_FALSE;
2N/A boolean_t nowarn = (flags & PAM_SILENT) == PAM_SILENT;
2N/A int ret = PAM_SUCCESS;
2N/A int err = 0;
2N/A char *user;
2N/A char pwbuf[NSS_BUFLEN_PASSWD];
2N/A struct passwd pw;
2N/A libzfs_handle_t *g_zfs = NULL;
2N/A zfs_handle_t *zhp = NULL;
2N/A char homes[ZFS_MAXNAMELEN];
2N/A char dataset[ZFS_MAXNAMELEN];
2N/A char encryption[ZFS_MAXNAMELEN];
2N/A int keystatus;
2N/A char keysource[ZFS_MAXNAMELEN];
2N/A char propsrc[ZFS_MAXNAMELEN];
2N/A zprop_source_t propsrctype;
2N/A char *authtok;
2N/A
2N/A get_default_homes(homes, sizeof (homes));
2N/A (void) strlcpy(encryption, DEFAULT_ENCRYPTION, sizeof (encryption));
2N/A for (i = 0; i < argc; i++) {
2N/A if (strcmp(argv[i], "debug") == 0) {
2N/A debug = B_TRUE;
2N/A } else if (strcmp(argv[i], "nowarn") == 0) {
2N/A nowarn = B_TRUE;
2N/A } else if (strncmp(argv[i], "homes=", 6) == 0) {
2N/A (void) strlcpy(homes,
2N/A &argv[i][6], sizeof (homes));
2N/A if (strlen(homes) == 0) {
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "pam_zfs_key invalid configuration 'homes='"
2N/A " can not be empty"));
2N/A return (PAM_SERVICE_ERR);
2N/A }
2N/A } else if (strcmp(argv[i], "force") == 0) {
2N/A force = B_TRUE;
2N/A } else if (strcmp(argv[i], "create") == 0) {
2N/A create = B_TRUE;
2N/A } else if (strncmp(argv[i], "encryption=", 11) == 0) {
2N/A (void) strlcpy(encryption,
2N/A &argv[i][11], sizeof (encryption));
2N/A } else {
2N/A display_msg(pamh, PAM_ERROR_MSG, dgettext(TEXT_DOMAIN,
2N/A "pam_zfs_key unknown option '%s'"), argv[i]);
2N/A return (PAM_SERVICE_ERR);
2N/A }
2N/A }
2N/A
2N/A if (debug)
2N/A __pam_log(LOG_AUTH | LOG_DEBUG,
2N/A "pam_zfs_key: pam_sm_setcred(flags = %x, argc= %d)",
2N/A flags, argc);
2N/A
2N/A (void) pam_get_item(pamh, PAM_USER, (void **)&user);
2N/A if (user == NULL || *user == '\0' ||
2N/A (getpwnam_r(user, &pw, pwbuf, sizeof (pwbuf)) == NULL)) {
2N/A __pam_log(LOG_AUTH | LOG_ERR,
2N/A "pam_zfs_key: USER NULL or empty!\n");
2N/A return (PAM_USER_UNKNOWN);
2N/A }
2N/A /* validate flags */
2N/A switch (flags & (PAM_ESTABLISH_CRED | PAM_DELETE_CRED |
2N/A PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) {
2N/A case 0:
2N/A /* set default flag */
2N/A flags |= PAM_ESTABLISH_CRED;
2N/A break;
2N/A case PAM_REFRESH_CRED:
2N/A return (PAM_IGNORE);
2N/A case PAM_REINITIALIZE_CRED:
2N/A case PAM_ESTABLISH_CRED:
2N/A case PAM_DELETE_CRED:
2N/A break;
2N/A default:
2N/A __pam_log(LOG_AUTH | LOG_ERR,
2N/A "pam_zfs_key: invalid flags %x", flags);
2N/A return (PAM_SYSTEM_ERR);
2N/A }
2N/A
2N/A (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&authtok);
2N/A
2N/A g_zfs = libzfs_init();
2N/A (void) snprintf(dataset, sizeof (dataset), "%s/%s", homes, user);
2N/A
2N/A zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM);
2N/A if (zhp == NULL && (!create || flags & PAM_DELETE_CRED)) {
2N/A libzfs_fini(g_zfs);
2N/A return (PAM_IGNORE);
2N/A } else if (zhp == NULL && create && !(flags & PAM_DELETE_CRED)) {
2N/A nvlist_t *props;
2N/A uint64_t crypt;
2N/A char mountpoint[MAXPATHLEN];
2N/A if (!nowarn) {
2N/A display_msg(pamh, PAM_TEXT_INFO,
2N/A dgettext(TEXT_DOMAIN,
2N/A "Creating home directory with encryption=%s.\n"
2N/A "Your login password will be used as the "
2N/A "wrapping key."), encryption);
2N/A }
2N/A (void) nvlist_alloc(&props, NV_UNIQUE_NAME, 0);
2N/A (void) nvlist_add_string(props,
2N/A zfs_prop_to_name(ZFS_PROP_ENCRYPTION), encryption);
2N/A (void) zfs_prop_string_to_index(ZFS_PROP_ENCRYPTION,
2N/A encryption, &crypt);
2N/A if (crypt != ZIO_CRYPT_OFF) {
2N/A (void) nvlist_add_string(props,
2N/A zfs_prop_to_name(ZFS_PROP_KEYSOURCE),
2N/A "passphrase,prompt");
2N/A libzfs_crypto_set_key(g_zfs, authtok, strlen(authtok));
2N/A }
2N/A err = zfs_create(g_zfs, dataset, ZFS_TYPE_FILESYSTEM, props);
2N/A if (err != 0) {
2N/A if (!nowarn)
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "creating home directory failed: %s"),
2N/A libzfs_error_description(g_zfs));
2N/A return (PAM_CRED_ERR);
2N/A }
2N/A zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM);
2N/A if (zhp == NULL) {
2N/A err = -1;
2N/A goto out;
2N/A }
2N/A err = set_delegation(zhp, pw.pw_uid);
2N/A if (err != 0) {
2N/A goto out;
2N/A }
2N/A err = zfs_mount(zhp, NULL, 0);
2N/A if (err != 0) {
2N/A goto out;
2N/A }
2N/A err = zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
2N/A sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
2N/A if (err != 0) {
2N/A goto out;
2N/A }
2N/A err = chown(mountpoint, pw.pw_uid, pw.pw_gid);
2N/A goto out;
2N/A }
2N/A
2N/A keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
2N/A /*
2N/A * Checking keystatus of none means we don't need to
2N/A * check the value of the encryption property since
2N/A * datasets with encryption=off always have an undefined
2N/A * keystatus.
2N/A */
2N/A if (keystatus == ZFS_CRYPT_KEY_NONE) {
2N/A __pam_log(LOG_AUTH | LOG_DEBUG,
2N/A "home dir %s for %s is not encrytped", dataset, user);
2N/A ret = PAM_IGNORE;
2N/A goto out;
2N/A }
2N/A
2N/A (void) zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource,
2N/A ZFS_MAXNAMELEN, &propsrctype, propsrc, sizeof (propsrc), B_FALSE);
2N/A
2N/A if (propsrctype != ZPROP_SRC_LOCAL ||
2N/A (strcmp(keysource, "passphrase,prompt") != 0)) {
2N/A __pam_log(LOG_AUTH | LOG_DEBUG,
2N/A "home dir %s for %s has incompatible keysource %s",
2N/A dataset, user, keysource);
2N/A ret = PAM_IGNORE;
2N/A goto out;
2N/A }
2N/A
2N/A if (keystatus == ZFS_CRYPT_KEY_UNAVAILABLE &&
2N/A !(flags & PAM_DELETE_CRED)) {
2N/A struct statvfs sbuf;
2N/A char *parent = strdup(pw.pw_dir);
2N/A /*
2N/A * First try an unmount of pw_dir if it is autofs
2N/A * in case automounter already attempted to
2N/A * mount up the pw_dir.
2N/A */
2N/A if (parent == NULL) {
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "pam_zfs_key strdup failed: %m"));
2N/A ret = PAM_BUF_ERR;
2N/A goto out;
2N/A } else {
2N/A parent = dirname(parent);
2N/A }
2N/A if (statvfs(parent, &sbuf) == 0 &&
2N/A strcmp(sbuf.f_basetype, "autofs") == 0) {
2N/A (void) umount(pw.pw_dir);
2N/A }
2N/A free(parent);
2N/A
2N/A if (authtok != NULL)
2N/A zfs_crypto_set_key(zhp, authtok, strlen(authtok));
2N/A if (zfs_key_load(zhp, B_TRUE, B_TRUE, B_TRUE) != 0) {
2N/A if (!nowarn) {
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "ZFS Key load failed for %s: %s"),
2N/A dataset, libzfs_error_description(g_zfs));
2N/A }
2N/A prompt_for_passphrase(zhp, pamh);
2N/A if (zfs_key_load(zhp, B_TRUE, B_TRUE, B_TRUE) != 0) {
2N/A ret = PAM_CRED_ERR;
2N/A if (!nowarn) {
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "ZFS Key load failed for %s: %s"),
2N/A dataset,
2N/A libzfs_error_description(g_zfs));
2N/A }
2N/A }
2N/A }
2N/A } else if (keystatus == ZFS_CRYPT_KEY_AVAILABLE &&
2N/A !(flags & PAM_DELETE_CRED)) {
2N/A __pam_log(LOG_AUTH | LOG_DEBUG,
2N/A "ZFS home directory key already present", dataset, user);
2N/A ret = PAM_IGNORE;
2N/A goto out;
2N/A } else if (keystatus == ZFS_CRYPT_KEY_AVAILABLE &&
2N/A (flags & PAM_DELETE_CRED)) {
2N/A /*
2N/A * Don't fail on the unmount just in case the module
2N/A * isn't running with all privs. If this is
2N/A * the automount point it will just end up stale and timeout,
2N/A * if the underlying real home dir does end up unmounted.
2N/A */
2N/A (void) chdir("/");
2N/A if (umount(pw.pw_dir) != 0 && force) {
2N/A (void) umount2(pw.pw_dir, MS_FORCE);
2N/A }
2N/A if (zfs_key_unload(zhp, force) != 0) {
2N/A ret = PAM_SYSTEM_ERR;
2N/A if (!nowarn) {
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "ZFS Key unload for %s failed: %s "),
2N/A dataset, libzfs_error_description(g_zfs));
2N/A }
2N/A }
2N/A /* Try again to remove the possibly automounted dir */
2N/A if (umount(pw.pw_dir) != 0 && force) {
2N/A (void) umount2(pw.pw_dir, MS_FORCE);
2N/A }
2N/A }
2N/A
2N/Aout:
2N/A if (zhp != NULL)
2N/A zfs_close(zhp);
2N/A if (g_zfs != NULL)
2N/A libzfs_fini(g_zfs);
2N/A
2N/A if (err != 0 && ret == PAM_SUCCESS) {
2N/A ret = PAM_CRED_ERR;
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Aint
2N/Apam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
2N/A{
2N/A boolean_t nowarn = (flags & PAM_SILENT) == PAM_SILENT;
2N/A int ret = PAM_SUCCESS;
2N/A int err;
2N/A char *user;
2N/A char pwbuf[NSS_BUFLEN_PASSWD];
2N/A struct passwd pw;
2N/A libzfs_handle_t *g_zfs = NULL;
2N/A zfs_handle_t *zhp = NULL;
2N/A char homes[ZFS_MAXNAMELEN];
2N/A char dataset[ZFS_MAXNAMELEN];
2N/A char keysource[ZFS_MAXNAMELEN];
2N/A char propsrc[ZFS_MAXNAMELEN];
2N/A char *authtok, *oldauthtok;
2N/A int keystatus;
2N/A zprop_source_t propsrctype;
2N/A uid_t euid;
2N/A gid_t egid;
2N/A int i;
2N/A
2N/A get_default_homes(homes, sizeof (homes));
2N/A for (i = 0; i < argc; i++) {
2N/A if (strcmp(argv[i], "nowarn") == 0) {
2N/A nowarn = B_TRUE;
2N/A } else if (strncmp(argv[i], "homes=", 6) == 0) {
2N/A (void) strlcpy(homes,
2N/A &argv[i][6], sizeof (homes));
2N/A }
2N/A }
2N/A
2N/A if ((flags & PAM_PRELIM_CHECK) != 0)
2N/A return (PAM_IGNORE);
2N/A
2N/A if ((flags & PAM_UPDATE_AUTHTOK) == 0)
2N/A return (PAM_SYSTEM_ERR);
2N/A
2N/A (void) pam_get_item(pamh, PAM_USER, (void **)&user);
2N/A if (user == NULL || *user == '\0' ||
2N/A (getpwnam_r(user, &pw, pwbuf, sizeof (pwbuf)) == NULL)) {
2N/A __pam_log(LOG_AUTH | LOG_ERR,
2N/A "pam_zfs_key: USER NULL or empty!\n");
2N/A return (PAM_USER_UNKNOWN);
2N/A }
2N/A
2N/A g_zfs = libzfs_init();
2N/A (void) snprintf(dataset, sizeof (dataset), "%s/%s", homes, user);
2N/A
2N/A zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM);
2N/A if (zhp == NULL) {
2N/A libzfs_fini(g_zfs);
2N/A return (PAM_IGNORE);
2N/A }
2N/A
2N/A (void) zfs_prop_get(zhp, ZFS_PROP_KEYSOURCE, keysource,
2N/A ZFS_MAXNAMELEN, &propsrctype, propsrc, sizeof (propsrc), B_FALSE);
2N/A
2N/A if (propsrctype != ZPROP_SRC_LOCAL ||
2N/A (strcmp(keysource, "passphrase,prompt") != 0)) {
2N/A ret = PAM_IGNORE;
2N/A goto out;
2N/A }
2N/A
2N/A keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
2N/A /*
2N/A * Checking keystatus for undefined means we don't need to
2N/A * check the value of the encryption property since
2N/A * datasets with encryption=off always have an undefined
2N/A * keystatus.
2N/A */
2N/A if (keystatus == ZFS_CRYPT_KEY_NONE) {
2N/A ret = PAM_IGNORE;
2N/A goto out;
2N/A }
2N/A
2N/A if (keystatus == ZFS_CRYPT_KEY_UNAVAILABLE) {
2N/A (void) pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&oldauthtok);
2N/A if (oldauthtok == NULL) {
2N/A if (!nowarn)
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN, "ZFS Key load failed"
2N/A " for %s: old passphrase required"),
2N/A dataset);
2N/A ret = PAM_AUTHTOK_ERR;
2N/A goto out;
2N/A }
2N/A zfs_crypto_set_key(zhp, oldauthtok, strlen(oldauthtok));
2N/A err = zfs_key_load(zhp, B_TRUE, B_TRUE, B_TRUE);
2N/A if (err != 0) {
2N/A prompt_for_passphrase(zhp, pamh);
2N/A err = zfs_key_load(zhp, B_TRUE, B_TRUE, B_TRUE) != 0;
2N/A if (err != 0) {
2N/A ret = PAM_AUTHTOK_ERR;
2N/A if (!nowarn) {
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "ZFS Key load failed for %s: %s"),
2N/A dataset,
2N/A libzfs_error_description(g_zfs));
2N/A }
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&authtok);
2N/A zfs_crypto_set_key(zhp, authtok, strlen(authtok));
2N/A
2N/A /*
2N/A * Temporarily switch over euid to ruid so that the kernel
2N/A * side of ZFS checks the 'keychange' delgation of the real user
2N/A * changing their password and it isn't bypassed because
2N/A * passwd(1) is setuid.
2N/A */
2N/A euid = geteuid();
2N/A egid = getegid();
2N/A if (egid != pw.pw_gid && setregid(-1, pw.pw_gid) != 0) {
2N/A ret = PAM_PERM_DENIED;
2N/A goto out;
2N/A }
2N/A if (euid != pw.pw_uid && setreuid(-1, pw.pw_uid) != 0) {
2N/A (void) setregid(-1, euid);
2N/A ret = PAM_PERM_DENIED;
2N/A goto out;
2N/A }
2N/A err = zfs_key_change(zhp, B_FALSE, NULL);
2N/A
2N/A if (err != 0) {
2N/A ret = PAM_AUTHTOK_ERR;
2N/A if (!nowarn) {
2N/A display_msg(pamh, PAM_ERROR_MSG,
2N/A dgettext(TEXT_DOMAIN,
2N/A "ZFS Key change failed for %s: %s"), dataset,
2N/A libzfs_error_description(g_zfs));
2N/A }
2N/A } else if (!nowarn) {
2N/A display_msg(pamh, PAM_TEXT_INFO, dgettext(TEXT_DOMAIN,
2N/A "ZFS Key change for %s successful"), dataset);
2N/A }
2N/A
2N/A (void) setreuid(-1, euid);
2N/A (void) setregid(-1, egid);
2N/A
2N/Aout:
2N/A if (zhp != NULL)
2N/A zfs_close(zhp);
2N/A if (g_zfs != NULL)
2N/A libzfs_fini(g_zfs);
2N/A
2N/A if (err != 0 && ret == PAM_SUCCESS)
2N/A ret = PAM_AUTHTOK_ERR;
2N/A
2N/A return (ret);
2N/A}