2 * Redistribution and use in source and binary forms, with or without
3 * modification, are permitted provided that the following conditions are met:
4 * * Redistributions of source code must retain the above copyright
5 * notice, this list of conditions and the following disclaimer.
6 * * Redistributions in binary form must reproduce the above copyright
7 * notice, this list of conditions and the following disclaimer in the
8 * documentation and/or other materials provided with the distribution.
9 * * Neither the name of the <organization> nor the
10 * names of its contributors may be used to endorse or promote products
11 * derived from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 * Copyright (c) 2020, Felix Dörre
25 * All rights reserved.
28 #include <sys/dsl_crypt.h>
29 #include <sys/byteorder.h>
34 #include <sys/zio_crypt.h>
35 #include <openssl/evp.h>
38 #define PAM_SM_PASSWORD
39 #define PAM_SM_SESSION
40 #include <security/pam_modules.h>
42 #if defined(__linux__)
43 #include <security/pam_ext.h>
44 #define MAP_FLAGS MAP_PRIVATE | MAP_ANONYMOUS
45 #elif defined(__FreeBSD__)
46 #include <security/pam_appl.h>
48 pam_syslog(pam_handle_t
*pamh
, int loglevel
, const char *fmt
, ...)
53 vsyslog(loglevel
, fmt
, args
);
56 #define MAP_FLAGS MAP_PRIVATE | MAP_ANON | MAP_NOCORE
69 static const char PASSWORD_VAR_NAME
[] = "pam_zfs_key_authtok";
71 static libzfs_handle_t
*g_zfs
;
73 static void destroy_pw(pam_handle_t
*pamh
, void *data
, int errcode
);
75 typedef int (*mlock_func_t
) (const void *, size_t);
83 * Try to mlock(2) or munlock(2) addr while handling EAGAIN by retrying ten
84 * times and sleeping 10 milliseconds in between for a total of 0.1
85 * seconds. lock_func must point to either mlock(2) or munlock(2).
88 try_lock(mlock_func_t lock_func
, const void *addr
, size_t len
)
92 useconds_t sleep_dur
= 10 * 1000;
94 if ((err
= (*lock_func
)(addr
, len
)) != EAGAIN
) {
97 for (int i
= retries
; i
> 0; --i
) {
98 (void) usleep(sleep_dur
);
99 if ((err
= (*lock_func
)(addr
, len
)) != EAGAIN
) {
107 static pw_password_t
*
108 alloc_pw_size(size_t len
)
110 pw_password_t
*pw
= malloc(sizeof (pw_password_t
));
116 * We use mmap(2) rather than malloc(3) since later on we mlock(2) the
117 * memory region. Since mlock(2) and munlock(2) operate on whole memory
118 * pages we should allocate a whole page here as mmap(2) does. Further
119 * this ensures that the addresses passed to mlock(2) an munlock(2) are
120 * on a page boundary as suggested by FreeBSD and required by some
121 * other implementations. Finally we avoid inadvertently munlocking
122 * memory mlocked by an concurrently running instance of us.
124 pw
->value
= mmap(NULL
, pw
->len
, PROT_READ
| PROT_WRITE
, MAP_FLAGS
,
127 if (pw
->value
== MAP_FAILED
) {
131 if (try_lock(mlock
, pw
->value
, pw
->len
) != 0) {
132 (void) munmap(pw
->value
, pw
->len
);
139 static pw_password_t
*
140 alloc_pw_string(const char *source
)
142 size_t len
= strlen(source
) + 1;
143 pw_password_t
*pw
= alloc_pw_size(len
);
148 memcpy(pw
->value
, source
, pw
->len
);
153 pw_free(pw_password_t
*pw
)
155 memset(pw
->value
, 0, pw
->len
);
156 if (try_lock(munlock
, pw
->value
, pw
->len
) == 0) {
157 (void) munmap(pw
->value
, pw
->len
);
162 static pw_password_t
*
163 pw_fetch(pam_handle_t
*pamh
)
166 if (pam_get_authtok(pamh
, PAM_AUTHTOK
, &token
, NULL
) != PAM_SUCCESS
) {
167 pam_syslog(pamh
, LOG_ERR
,
168 "couldn't get password from PAM stack");
172 pam_syslog(pamh
, LOG_ERR
,
173 "token from PAM stack is null");
176 return (alloc_pw_string(token
));
179 static const pw_password_t
*
180 pw_fetch_lazy(pam_handle_t
*pamh
)
182 pw_password_t
*pw
= pw_fetch(pamh
);
186 int ret
= pam_set_data(pamh
, PASSWORD_VAR_NAME
, pw
, destroy_pw
);
187 if (ret
!= PAM_SUCCESS
) {
189 pam_syslog(pamh
, LOG_ERR
, "pam_set_data failed");
195 static const pw_password_t
*
196 pw_get(pam_handle_t
*pamh
)
198 const pw_password_t
*authtok
= NULL
;
199 int ret
= pam_get_data(pamh
, PASSWORD_VAR_NAME
,
200 (const void**)(&authtok
));
201 if (ret
== PAM_SUCCESS
)
203 if (ret
== PAM_NO_MODULE_DATA
)
204 return (pw_fetch_lazy(pamh
));
205 pam_syslog(pamh
, LOG_ERR
, "password not available");
210 pw_clear(pam_handle_t
*pamh
)
212 int ret
= pam_set_data(pamh
, PASSWORD_VAR_NAME
, NULL
, NULL
);
213 if (ret
!= PAM_SUCCESS
) {
214 pam_syslog(pamh
, LOG_ERR
, "clearing password failed");
221 destroy_pw(pam_handle_t
*pamh
, void *data
, int errcode
)
223 (void) pamh
, (void) errcode
;
226 pw_free((pw_password_t
*)data
);
231 pam_zfs_init(pam_handle_t
*pamh
)
234 if ((g_zfs
= libzfs_init()) == NULL
) {
236 pam_syslog(pamh
, LOG_ERR
, "Zfs initialization error: %s",
237 libzfs_error_init(error
));
248 static pw_password_t
*
249 prepare_passphrase(pam_handle_t
*pamh
, zfs_handle_t
*ds
,
250 const char *passphrase
, nvlist_t
*nvlist
)
252 pw_password_t
*key
= alloc_pw_size(WRAPPING_KEY_LEN
);
258 if (nvlist
!= NULL
) {
259 int fd
= open("/dev/urandom", O_RDONLY
);
265 char *buf
= (char *)&salt
;
266 size_t bytes
= sizeof (uint64_t);
267 while (bytes_read
< bytes
) {
268 ssize_t len
= read(fd
, buf
+ bytes_read
, bytes
279 if (nvlist_add_uint64(nvlist
,
280 zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT
), salt
)) {
281 pam_syslog(pamh
, LOG_ERR
,
282 "failed to add salt to nvlist");
286 iters
= DEFAULT_PBKDF2_ITERATIONS
;
287 if (nvlist_add_uint64(nvlist
, zfs_prop_to_name(
288 ZFS_PROP_PBKDF2_ITERS
), iters
)) {
289 pam_syslog(pamh
, LOG_ERR
,
290 "failed to add iters to nvlist");
295 salt
= zfs_prop_get_int(ds
, ZFS_PROP_PBKDF2_SALT
);
296 iters
= zfs_prop_get_int(ds
, ZFS_PROP_PBKDF2_ITERS
);
300 if (!PKCS5_PBKDF2_HMAC_SHA1((char *)passphrase
,
301 strlen(passphrase
), (uint8_t *)&salt
,
302 sizeof (uint64_t), iters
, WRAPPING_KEY_LEN
,
303 (uint8_t *)key
->value
)) {
304 pam_syslog(pamh
, LOG_ERR
, "pbkdf failed");
312 is_key_loaded(pam_handle_t
*pamh
, const char *ds_name
)
314 zfs_handle_t
*ds
= zfs_open(g_zfs
, ds_name
, ZFS_TYPE_FILESYSTEM
);
316 pam_syslog(pamh
, LOG_ERR
, "dataset %s not found", ds_name
);
319 int keystatus
= zfs_prop_get_int(ds
, ZFS_PROP_KEYSTATUS
);
321 return (keystatus
!= ZFS_KEYSTATUS_UNAVAILABLE
);
325 change_key(pam_handle_t
*pamh
, const char *ds_name
,
326 const char *passphrase
)
328 zfs_handle_t
*ds
= zfs_open(g_zfs
, ds_name
, ZFS_TYPE_FILESYSTEM
);
330 pam_syslog(pamh
, LOG_ERR
, "dataset %s not found", ds_name
);
333 nvlist_t
*nvlist
= fnvlist_alloc();
334 pw_password_t
*key
= prepare_passphrase(pamh
, ds
, passphrase
, nvlist
);
340 if (nvlist_add_string(nvlist
,
341 zfs_prop_to_name(ZFS_PROP_KEYLOCATION
),
343 pam_syslog(pamh
, LOG_ERR
, "nvlist_add failed for keylocation");
349 if (nvlist_add_uint64(nvlist
,
350 zfs_prop_to_name(ZFS_PROP_KEYFORMAT
),
351 ZFS_KEYFORMAT_PASSPHRASE
)) {
352 pam_syslog(pamh
, LOG_ERR
, "nvlist_add failed for keyformat");
358 int ret
= lzc_change_key(ds_name
, DCP_CMD_NEW_KEY
, nvlist
,
359 (uint8_t *)key
->value
, WRAPPING_KEY_LEN
);
362 pam_syslog(pamh
, LOG_ERR
, "change_key failed: %d", ret
);
373 decrypt_mount(pam_handle_t
*pamh
, const char *ds_name
,
374 const char *passphrase
, boolean_t noop
)
376 zfs_handle_t
*ds
= zfs_open(g_zfs
, ds_name
, ZFS_TYPE_FILESYSTEM
);
378 pam_syslog(pamh
, LOG_ERR
, "dataset %s not found", ds_name
);
381 pw_password_t
*key
= prepare_passphrase(pamh
, ds
, passphrase
, NULL
);
386 int ret
= lzc_load_key(ds_name
, noop
, (uint8_t *)key
->value
,
389 if (ret
&& ret
!= EEXIST
) {
390 pam_syslog(pamh
, LOG_ERR
, "load_key failed: %d", ret
);
397 ret
= zfs_mount(ds
, NULL
, 0);
399 pam_syslog(pamh
, LOG_ERR
, "mount failed: %d", ret
);
409 unmount_unload(pam_handle_t
*pamh
, const char *ds_name
)
411 zfs_handle_t
*ds
= zfs_open(g_zfs
, ds_name
, ZFS_TYPE_FILESYSTEM
);
413 pam_syslog(pamh
, LOG_ERR
, "dataset %s not found", ds_name
);
416 int ret
= zfs_unmount(ds
, NULL
, 0);
418 pam_syslog(pamh
, LOG_ERR
, "zfs_unmount failed with: %d", ret
);
423 ret
= lzc_unload_key(ds_name
);
425 pam_syslog(pamh
, LOG_ERR
, "unload_key failed with: %d", ret
);
439 const char *username
;
440 boolean_t unmount_and_unload
;
444 zfs_key_config_load(pam_handle_t
*pamh
, zfs_key_config_t
*config
,
445 int argc
, const char **argv
)
447 config
->homes_prefix
= strdup("rpool/home");
448 if (config
->homes_prefix
== NULL
) {
449 pam_syslog(pamh
, LOG_ERR
, "strdup failure");
450 return (PAM_SERVICE_ERR
);
452 config
->runstatedir
= strdup(RUNSTATEDIR
"/pam_zfs_key");
453 if (config
->runstatedir
== NULL
) {
454 pam_syslog(pamh
, LOG_ERR
, "strdup failure");
455 free(config
->homes_prefix
);
456 return (PAM_SERVICE_ERR
);
459 if (pam_get_user(pamh
, &name
, NULL
) != PAM_SUCCESS
) {
460 pam_syslog(pamh
, LOG_ERR
,
461 "couldn't get username from PAM stack");
462 free(config
->runstatedir
);
463 free(config
->homes_prefix
);
464 return (PAM_SERVICE_ERR
);
466 struct passwd
*entry
= getpwnam(name
);
468 free(config
->runstatedir
);
469 free(config
->homes_prefix
);
470 return (PAM_USER_UNKNOWN
);
472 config
->uid
= entry
->pw_uid
;
473 config
->username
= name
;
474 config
->unmount_and_unload
= B_TRUE
;
475 config
->dsname
= NULL
;
476 config
->homedir
= NULL
;
477 for (int c
= 0; c
< argc
; c
++) {
478 if (strncmp(argv
[c
], "homes=", 6) == 0) {
479 free(config
->homes_prefix
);
480 config
->homes_prefix
= strdup(argv
[c
] + 6);
481 } else if (strncmp(argv
[c
], "runstatedir=", 12) == 0) {
482 free(config
->runstatedir
);
483 config
->runstatedir
= strdup(argv
[c
] + 12);
484 } else if (strcmp(argv
[c
], "nounmount") == 0) {
485 config
->unmount_and_unload
= B_FALSE
;
486 } else if (strcmp(argv
[c
], "prop_mountpoint") == 0) {
487 if (config
->homedir
== NULL
)
488 config
->homedir
= strdup(entry
->pw_dir
);
491 return (PAM_SUCCESS
);
495 zfs_key_config_free(zfs_key_config_t
*config
)
497 free(config
->homes_prefix
);
498 free(config
->runstatedir
);
499 free(config
->homedir
);
500 free(config
->dsname
);
504 find_dsname_by_prop_value(zfs_handle_t
*zhp
, void *data
)
506 zfs_type_t type
= zfs_get_type(zhp
);
507 zfs_key_config_t
*target
= data
;
508 char mountpoint
[ZFS_MAXPROPLEN
];
510 /* Skip any datasets whose type does not match */
511 if ((type
& ZFS_TYPE_FILESYSTEM
) == 0) {
516 /* Skip any datasets whose mountpoint does not match */
517 (void) zfs_prop_get(zhp
, ZFS_PROP_MOUNTPOINT
, mountpoint
,
518 sizeof (mountpoint
), NULL
, NULL
, 0, B_FALSE
);
519 if (strcmp(target
->homedir
, mountpoint
) != 0) {
524 target
->dsname
= strdup(zfs_get_name(zhp
));
530 zfs_key_config_get_dataset(zfs_key_config_t
*config
)
532 if (config
->homedir
!= NULL
&&
533 config
->homes_prefix
!= NULL
) {
534 zfs_handle_t
*zhp
= zfs_open(g_zfs
, config
->homes_prefix
,
535 ZFS_TYPE_FILESYSTEM
);
537 pam_syslog(NULL
, LOG_ERR
, "dataset %s not found",
538 config
->homes_prefix
);
542 (void) zfs_iter_filesystems_v2(zhp
, 0,
543 find_dsname_by_prop_value
, config
);
545 char *dsname
= config
->dsname
;
546 config
->dsname
= NULL
;
550 if (config
->homes_prefix
== NULL
) {
554 size_t len
= ZFS_MAX_DATASET_NAME_LEN
;
555 size_t total_len
= strlen(config
->homes_prefix
) + 1
556 + strlen(config
->username
);
557 if (total_len
> len
) {
560 char *ret
= malloc(len
+ 1);
565 (void) snprintf(ret
, len
+ 1, "%s/%s", config
->homes_prefix
,
571 zfs_key_config_modify_session_counter(pam_handle_t
*pamh
,
572 zfs_key_config_t
*config
, int delta
)
574 const char *runtime_path
= config
->runstatedir
;
575 if (mkdir(runtime_path
, S_IRWXU
) != 0 && errno
!= EEXIST
) {
576 pam_syslog(pamh
, LOG_ERR
, "Can't create runtime path: %d",
580 if (chown(runtime_path
, 0, 0) != 0) {
581 pam_syslog(pamh
, LOG_ERR
, "Can't chown runtime path: %d",
585 if (chmod(runtime_path
, S_IRWXU
) != 0) {
586 pam_syslog(pamh
, LOG_ERR
, "Can't chmod runtime path: %d",
592 if (asprintf(&counter_path
, "%s/%u", runtime_path
, config
->uid
) == -1)
595 const int fd
= open(counter_path
,
596 O_RDWR
| O_CLOEXEC
| O_CREAT
| O_NOFOLLOW
,
600 pam_syslog(pamh
, LOG_ERR
, "Can't open counter file: %d", errno
);
603 if (flock(fd
, LOCK_EX
) != 0) {
604 pam_syslog(pamh
, LOG_ERR
, "Can't lock counter file: %d", errno
);
610 int remaining
= sizeof (counter
) - 1;
612 counter
[sizeof (counter
) - 1] = 0;
613 while (remaining
> 0 && (ret
= read(fd
, pos
, remaining
)) > 0) {
618 long int counter_value
= strtol(counter
, NULL
, 10);
619 counter_value
+= delta
;
620 if (counter_value
< 0) {
623 lseek(fd
, 0, SEEK_SET
);
624 if (ftruncate(fd
, 0) != 0) {
625 pam_syslog(pamh
, LOG_ERR
, "Can't truncate counter file: %d",
630 snprintf(counter
, sizeof (counter
), "%ld", counter_value
);
631 remaining
= strlen(counter
);
633 while (remaining
> 0 && (ret
= write(fd
, pos
, remaining
)) > 0) {
638 return (counter_value
);
641 __attribute__((visibility("default")))
643 pam_sm_authenticate(pam_handle_t
*pamh
, int flags
,
644 int argc
, const char **argv
)
648 if (geteuid() != 0) {
649 pam_syslog(pamh
, LOG_ERR
,
650 "Cannot zfs_mount when not being root.");
651 return (PAM_SERVICE_ERR
);
653 zfs_key_config_t config
;
654 int config_err
= zfs_key_config_load(pamh
, &config
, argc
, argv
);
655 if (config_err
!= PAM_SUCCESS
) {
659 const pw_password_t
*token
= pw_fetch_lazy(pamh
);
661 zfs_key_config_free(&config
);
662 return (PAM_AUTH_ERR
);
664 if (pam_zfs_init(pamh
) != 0) {
665 zfs_key_config_free(&config
);
666 return (PAM_SERVICE_ERR
);
668 char *dataset
= zfs_key_config_get_dataset(&config
);
671 zfs_key_config_free(&config
);
672 return (PAM_SERVICE_ERR
);
674 if (decrypt_mount(pamh
, dataset
, token
->value
, B_TRUE
) == -1) {
677 zfs_key_config_free(&config
);
678 return (PAM_AUTH_ERR
);
682 zfs_key_config_free(&config
);
683 return (PAM_SUCCESS
);
686 __attribute__((visibility("default")))
688 pam_sm_setcred(pam_handle_t
*pamh
, int flags
,
689 int argc
, const char **argv
)
691 (void) pamh
, (void) flags
, (void) argc
, (void) argv
;
692 return (PAM_SUCCESS
);
695 __attribute__((visibility("default")))
697 pam_sm_chauthtok(pam_handle_t
*pamh
, int flags
,
698 int argc
, const char **argv
)
700 if (geteuid() != 0) {
701 pam_syslog(pamh
, LOG_ERR
,
702 "Cannot zfs_mount when not being root.");
703 return (PAM_PERM_DENIED
);
705 zfs_key_config_t config
;
706 if (zfs_key_config_load(pamh
, &config
, argc
, argv
) != PAM_SUCCESS
) {
707 return (PAM_SERVICE_ERR
);
709 if (config
.uid
< 1000) {
710 zfs_key_config_free(&config
);
711 return (PAM_SUCCESS
);
714 if (pam_zfs_init(pamh
) != 0) {
715 zfs_key_config_free(&config
);
716 return (PAM_SERVICE_ERR
);
718 char *dataset
= zfs_key_config_get_dataset(&config
);
721 zfs_key_config_free(&config
);
722 return (PAM_SERVICE_ERR
);
724 int key_loaded
= is_key_loaded(pamh
, dataset
);
725 if (key_loaded
== -1) {
728 zfs_key_config_free(&config
);
729 return (PAM_SERVICE_ERR
);
734 pam_syslog(pamh
, LOG_ERR
,
735 "key not loaded, returning try_again");
736 zfs_key_config_free(&config
);
737 return (PAM_PERM_DENIED
);
741 if ((flags
& PAM_UPDATE_AUTHTOK
) != 0) {
742 const pw_password_t
*token
= pw_get(pamh
);
744 zfs_key_config_free(&config
);
745 return (PAM_SERVICE_ERR
);
747 if (pam_zfs_init(pamh
) != 0) {
748 zfs_key_config_free(&config
);
749 return (PAM_SERVICE_ERR
);
751 char *dataset
= zfs_key_config_get_dataset(&config
);
754 zfs_key_config_free(&config
);
755 return (PAM_SERVICE_ERR
);
757 if (change_key(pamh
, dataset
, token
->value
) == -1) {
760 zfs_key_config_free(&config
);
761 return (PAM_SERVICE_ERR
);
765 zfs_key_config_free(&config
);
766 if (pw_clear(pamh
) == -1) {
767 return (PAM_SERVICE_ERR
);
770 zfs_key_config_free(&config
);
772 return (PAM_SUCCESS
);
776 pam_sm_open_session(pam_handle_t
*pamh
, int flags
,
777 int argc
, const char **argv
)
781 if (geteuid() != 0) {
782 pam_syslog(pamh
, LOG_ERR
,
783 "Cannot zfs_mount when not being root.");
784 return (PAM_SUCCESS
);
786 zfs_key_config_t config
;
787 if (zfs_key_config_load(pamh
, &config
, argc
, argv
) != PAM_SUCCESS
) {
788 return (PAM_SESSION_ERR
);
791 if (config
.uid
< 1000) {
792 zfs_key_config_free(&config
);
793 return (PAM_SUCCESS
);
796 int counter
= zfs_key_config_modify_session_counter(pamh
, &config
, 1);
798 zfs_key_config_free(&config
);
799 return (PAM_SUCCESS
);
802 const pw_password_t
*token
= pw_get(pamh
);
804 zfs_key_config_free(&config
);
805 return (PAM_SESSION_ERR
);
807 if (pam_zfs_init(pamh
) != 0) {
808 zfs_key_config_free(&config
);
809 return (PAM_SERVICE_ERR
);
811 char *dataset
= zfs_key_config_get_dataset(&config
);
814 zfs_key_config_free(&config
);
815 return (PAM_SERVICE_ERR
);
817 if (decrypt_mount(pamh
, dataset
, token
->value
, B_FALSE
) == -1) {
820 zfs_key_config_free(&config
);
821 return (PAM_SERVICE_ERR
);
825 zfs_key_config_free(&config
);
826 if (pw_clear(pamh
) == -1) {
827 return (PAM_SERVICE_ERR
);
829 return (PAM_SUCCESS
);
833 __attribute__((visibility("default")))
835 pam_sm_close_session(pam_handle_t
*pamh
, int flags
,
836 int argc
, const char **argv
)
840 if (geteuid() != 0) {
841 pam_syslog(pamh
, LOG_ERR
,
842 "Cannot zfs_mount when not being root.");
843 return (PAM_SUCCESS
);
845 zfs_key_config_t config
;
846 if (zfs_key_config_load(pamh
, &config
, argc
, argv
) != PAM_SUCCESS
) {
847 return (PAM_SESSION_ERR
);
849 if (config
.uid
< 1000) {
850 zfs_key_config_free(&config
);
851 return (PAM_SUCCESS
);
854 int counter
= zfs_key_config_modify_session_counter(pamh
, &config
, -1);
856 zfs_key_config_free(&config
);
857 return (PAM_SUCCESS
);
860 if (config
.unmount_and_unload
) {
861 if (pam_zfs_init(pamh
) != 0) {
862 zfs_key_config_free(&config
);
863 return (PAM_SERVICE_ERR
);
865 char *dataset
= zfs_key_config_get_dataset(&config
);
868 zfs_key_config_free(&config
);
869 return (PAM_SESSION_ERR
);
871 if (unmount_unload(pamh
, dataset
) == -1) {
874 zfs_key_config_free(&config
);
875 return (PAM_SESSION_ERR
);
881 zfs_key_config_free(&config
);
882 return (PAM_SUCCESS
);