1 /*
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.
12  *
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.
23  *
24  * Copyright (c) 2020, Felix Dörre
25  * All rights reserved.
26  */
27 
28 #include <sys/dsl_crypt.h>
29 #include <sys/byteorder.h>
30 #include <libzfs.h>
31 
32 #include <syslog.h>
33 
34 #include <sys/zio_crypt.h>
35 #include <openssl/evp.h>
36 
37 #define	PAM_SM_AUTH
38 #define	PAM_SM_PASSWORD
39 #define	PAM_SM_SESSION
40 #include <security/pam_modules.h>
41 
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>
47 static void
48 pam_syslog(pam_handle_t *pamh, int loglevel, const char *fmt, ...)
49 {
50 	(void) pamh;
51 	va_list args;
52 	va_start(args, fmt);
53 	vsyslog(loglevel, fmt, args);
54 	va_end(args);
55 }
56 #define	MAP_FLAGS MAP_PRIVATE | MAP_ANON | MAP_NOCORE
57 #endif
58 
59 #include <string.h>
60 #include <unistd.h>
61 #include <fcntl.h>
62 #include <sys/stat.h>
63 #include <sys/file.h>
64 #include <sys/wait.h>
65 #include <pwd.h>
66 
67 #include <sys/mman.h>
68 
69 static const char PASSWORD_VAR_NAME[] = "pam_zfs_key_authtok";
70 
71 static libzfs_handle_t *g_zfs;
72 
73 static void destroy_pw(pam_handle_t *pamh, void *data, int errcode);
74 
75 typedef int (*mlock_func_t) (const void *, size_t);
76 
77 typedef struct {
78 	size_t len;
79 	char *value;
80 } pw_password_t;
81 
82 /*
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).
86  */
87 static int
88 try_lock(mlock_func_t lock_func, const void *addr, size_t len)
89 {
90 	int err;
91 	int retries = 10;
92 	useconds_t sleep_dur = 10 * 1000;
93 
94 	if ((err = (*lock_func)(addr, len)) != EAGAIN) {
95 		return (err);
96 	}
97 	for (int i = retries; i > 0; --i) {
98 		(void) usleep(sleep_dur);
99 		if ((err = (*lock_func)(addr, len)) != EAGAIN) {
100 			break;
101 		}
102 	}
103 	return (err);
104 }
105 
106 
107 static pw_password_t *
108 alloc_pw_size(size_t len)
109 {
110 	pw_password_t *pw = malloc(sizeof (pw_password_t));
111 	if (!pw) {
112 		return (NULL);
113 	}
114 	pw->len = len;
115 	/*
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.
123 	 */
124 	pw->value = mmap(NULL, pw->len, PROT_READ | PROT_WRITE, MAP_FLAGS,
125 	    -1, 0);
126 
127 	if (pw->value == MAP_FAILED) {
128 		free(pw);
129 		return (NULL);
130 	}
131 	if (try_lock(mlock, pw->value, pw->len) != 0) {
132 		(void) munmap(pw->value, pw->len);
133 		free(pw);
134 		return (NULL);
135 	}
136 	return (pw);
137 }
138 
139 static pw_password_t *
140 alloc_pw_string(const char *source)
141 {
142 	size_t len = strlen(source) + 1;
143 	pw_password_t *pw = alloc_pw_size(len);
144 
145 	if (!pw) {
146 		return (NULL);
147 	}
148 	memcpy(pw->value, source, pw->len);
149 	return (pw);
150 }
151 
152 static void
153 pw_free(pw_password_t *pw)
154 {
155 	memset(pw->value, 0, pw->len);
156 	if (try_lock(munlock, pw->value, pw->len) == 0) {
157 		(void) munmap(pw->value, pw->len);
158 	}
159 	free(pw);
160 }
161 
162 static pw_password_t *
163 pw_fetch(pam_handle_t *pamh)
164 {
165 	const char *token;
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");
169 		return (NULL);
170 	}
171 	if (!token) {
172 		pam_syslog(pamh, LOG_ERR,
173 		    "token from PAM stack is null");
174 		return (NULL);
175 	}
176 	return (alloc_pw_string(token));
177 }
178 
179 static const pw_password_t *
180 pw_fetch_lazy(pam_handle_t *pamh)
181 {
182 	pw_password_t *pw = pw_fetch(pamh);
183 	if (pw == NULL) {
184 		return (NULL);
185 	}
186 	int ret = pam_set_data(pamh, PASSWORD_VAR_NAME, pw, destroy_pw);
187 	if (ret != PAM_SUCCESS) {
188 		pw_free(pw);
189 		pam_syslog(pamh, LOG_ERR, "pam_set_data failed");
190 		return (NULL);
191 	}
192 	return (pw);
193 }
194 
195 static const pw_password_t *
196 pw_get(pam_handle_t *pamh)
197 {
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)
202 		return (authtok);
203 	if (ret == PAM_NO_MODULE_DATA)
204 		return (pw_fetch_lazy(pamh));
205 	pam_syslog(pamh, LOG_ERR, "password not available");
206 	return (NULL);
207 }
208 
209 static int
210 pw_clear(pam_handle_t *pamh)
211 {
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");
215 		return (-1);
216 	}
217 	return (0);
218 }
219 
220 static void
221 destroy_pw(pam_handle_t *pamh, void *data, int errcode)
222 {
223 	(void) pamh, (void) errcode;
224 
225 	if (data != NULL) {
226 		pw_free((pw_password_t *)data);
227 	}
228 }
229 
230 static int
231 pam_zfs_init(pam_handle_t *pamh)
232 {
233 	int error = 0;
234 	if ((g_zfs = libzfs_init()) == NULL) {
235 		error = errno;
236 		pam_syslog(pamh, LOG_ERR, "Zfs initialization error: %s",
237 		    libzfs_error_init(error));
238 	}
239 	return (error);
240 }
241 
242 static void
243 pam_zfs_free(void)
244 {
245 	libzfs_fini(g_zfs);
246 }
247 
248 static pw_password_t *
249 prepare_passphrase(pam_handle_t *pamh, zfs_handle_t *ds,
250     const char *passphrase, nvlist_t *nvlist)
251 {
252 	pw_password_t *key = alloc_pw_size(WRAPPING_KEY_LEN);
253 	if (!key) {
254 		return (NULL);
255 	}
256 	uint64_t salt;
257 	uint64_t iters;
258 	if (nvlist != NULL) {
259 		int fd = open("/dev/urandom", O_RDONLY);
260 		if (fd < 0) {
261 			pw_free(key);
262 			return (NULL);
263 		}
264 		int bytes_read = 0;
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
269 			    - bytes_read);
270 			if (len < 0) {
271 				close(fd);
272 				pw_free(key);
273 				return (NULL);
274 			}
275 			bytes_read += len;
276 		}
277 		close(fd);
278 
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");
283 			pw_free(key);
284 			return (NULL);
285 		}
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");
291 			pw_free(key);
292 			return (NULL);
293 		}
294 	} else {
295 		salt = zfs_prop_get_int(ds, ZFS_PROP_PBKDF2_SALT);
296 		iters = zfs_prop_get_int(ds, ZFS_PROP_PBKDF2_ITERS);
297 	}
298 
299 	salt = LE_64(salt);
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");
305 		pw_free(key);
306 		return (NULL);
307 	}
308 	return (key);
309 }
310 
311 static int
312 is_key_loaded(pam_handle_t *pamh, const char *ds_name)
313 {
314 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
315 	if (ds == NULL) {
316 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
317 		return (-1);
318 	}
319 	int keystatus = zfs_prop_get_int(ds, ZFS_PROP_KEYSTATUS);
320 	zfs_close(ds);
321 	return (keystatus != ZFS_KEYSTATUS_UNAVAILABLE);
322 }
323 
324 static int
325 change_key(pam_handle_t *pamh, const char *ds_name,
326     const char *passphrase)
327 {
328 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
329 	if (ds == NULL) {
330 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
331 		return (-1);
332 	}
333 	nvlist_t *nvlist = fnvlist_alloc();
334 	pw_password_t *key = prepare_passphrase(pamh, ds, passphrase, nvlist);
335 	if (key == NULL) {
336 		nvlist_free(nvlist);
337 		zfs_close(ds);
338 		return (-1);
339 	}
340 	if (nvlist_add_string(nvlist,
341 	    zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
342 	    "prompt")) {
343 		pam_syslog(pamh, LOG_ERR, "nvlist_add failed for keylocation");
344 		pw_free(key);
345 		nvlist_free(nvlist);
346 		zfs_close(ds);
347 		return (-1);
348 	}
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");
353 		pw_free(key);
354 		nvlist_free(nvlist);
355 		zfs_close(ds);
356 		return (-1);
357 	}
358 	int ret = lzc_change_key(ds_name, DCP_CMD_NEW_KEY, nvlist,
359 	    (uint8_t *)key->value, WRAPPING_KEY_LEN);
360 	pw_free(key);
361 	if (ret) {
362 		pam_syslog(pamh, LOG_ERR, "change_key failed: %d", ret);
363 		nvlist_free(nvlist);
364 		zfs_close(ds);
365 		return (-1);
366 	}
367 	nvlist_free(nvlist);
368 	zfs_close(ds);
369 	return (0);
370 }
371 
372 static int
373 decrypt_mount(pam_handle_t *pamh, const char *ds_name,
374     const char *passphrase)
375 {
376 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
377 	if (ds == NULL) {
378 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
379 		return (-1);
380 	}
381 	pw_password_t *key = prepare_passphrase(pamh, ds, passphrase, NULL);
382 	if (key == NULL) {
383 		zfs_close(ds);
384 		return (-1);
385 	}
386 	int ret = lzc_load_key(ds_name, B_FALSE, (uint8_t *)key->value,
387 	    WRAPPING_KEY_LEN);
388 	pw_free(key);
389 	if (ret) {
390 		pam_syslog(pamh, LOG_ERR, "load_key failed: %d", ret);
391 		zfs_close(ds);
392 		return (-1);
393 	}
394 	ret = zfs_mount(ds, NULL, 0);
395 	if (ret) {
396 		pam_syslog(pamh, LOG_ERR, "mount failed: %d", ret);
397 		zfs_close(ds);
398 		return (-1);
399 	}
400 	zfs_close(ds);
401 	return (0);
402 }
403 
404 static int
405 unmount_unload(pam_handle_t *pamh, const char *ds_name)
406 {
407 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
408 	if (ds == NULL) {
409 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
410 		return (-1);
411 	}
412 	int ret = zfs_unmount(ds, NULL, 0);
413 	if (ret) {
414 		pam_syslog(pamh, LOG_ERR, "zfs_unmount failed with: %d", ret);
415 		zfs_close(ds);
416 		return (-1);
417 	}
418 
419 	ret = lzc_unload_key(ds_name);
420 	if (ret) {
421 		pam_syslog(pamh, LOG_ERR, "unload_key failed with: %d", ret);
422 		zfs_close(ds);
423 		return (-1);
424 	}
425 	zfs_close(ds);
426 	return (0);
427 }
428 
429 typedef struct {
430 	char *homes_prefix;
431 	char *runstatedir;
432 	char *homedir;
433 	char *dsname;
434 	uid_t uid;
435 	const char *username;
436 	int unmount_and_unload;
437 } zfs_key_config_t;
438 
439 static int
440 zfs_key_config_load(pam_handle_t *pamh, zfs_key_config_t *config,
441     int argc, const char **argv)
442 {
443 	config->homes_prefix = strdup("rpool/home");
444 	if (config->homes_prefix == NULL) {
445 		pam_syslog(pamh, LOG_ERR, "strdup failure");
446 		return (-1);
447 	}
448 	config->runstatedir = strdup(RUNSTATEDIR "/pam_zfs_key");
449 	if (config->runstatedir == NULL) {
450 		pam_syslog(pamh, LOG_ERR, "strdup failure");
451 		free(config->homes_prefix);
452 		return (-1);
453 	}
454 	const char *name;
455 	if (pam_get_user(pamh, &name, NULL) != PAM_SUCCESS) {
456 		pam_syslog(pamh, LOG_ERR,
457 		    "couldn't get username from PAM stack");
458 		free(config->runstatedir);
459 		free(config->homes_prefix);
460 		return (-1);
461 	}
462 	struct passwd *entry = getpwnam(name);
463 	if (!entry) {
464 		free(config->runstatedir);
465 		free(config->homes_prefix);
466 		return (-1);
467 	}
468 	config->uid = entry->pw_uid;
469 	config->username = name;
470 	config->unmount_and_unload = 1;
471 	config->dsname = NULL;
472 	config->homedir = NULL;
473 	for (int c = 0; c < argc; c++) {
474 		if (strncmp(argv[c], "homes=", 6) == 0) {
475 			free(config->homes_prefix);
476 			config->homes_prefix = strdup(argv[c] + 6);
477 		} else if (strncmp(argv[c], "runstatedir=", 12) == 0) {
478 			free(config->runstatedir);
479 			config->runstatedir = strdup(argv[c] + 12);
480 		} else if (strcmp(argv[c], "nounmount") == 0) {
481 			config->unmount_and_unload = 0;
482 		} else if (strcmp(argv[c], "prop_mountpoint") == 0) {
483 			if (config->homedir == NULL)
484 				config->homedir = strdup(entry->pw_dir);
485 		}
486 	}
487 	return (0);
488 }
489 
490 static void
491 zfs_key_config_free(zfs_key_config_t *config)
492 {
493 	free(config->homes_prefix);
494 	free(config->runstatedir);
495 	free(config->homedir);
496 	free(config->dsname);
497 }
498 
499 static int
500 find_dsname_by_prop_value(zfs_handle_t *zhp, void *data)
501 {
502 	zfs_type_t type = zfs_get_type(zhp);
503 	zfs_key_config_t *target = data;
504 	char mountpoint[ZFS_MAXPROPLEN];
505 
506 	/* Skip any datasets whose type does not match */
507 	if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
508 		zfs_close(zhp);
509 		return (0);
510 	}
511 
512 	/* Skip any datasets whose mountpoint does not match */
513 	(void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
514 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
515 	if (strcmp(target->homedir, mountpoint) != 0) {
516 		zfs_close(zhp);
517 		return (0);
518 	}
519 
520 	target->dsname = strdup(zfs_get_name(zhp));
521 	zfs_close(zhp);
522 	return (1);
523 }
524 
525 static char *
526 zfs_key_config_get_dataset(zfs_key_config_t *config)
527 {
528 	if (config->homedir != NULL &&
529 	    config->homes_prefix != NULL) {
530 		zfs_handle_t *zhp = zfs_open(g_zfs, config->homes_prefix,
531 		    ZFS_TYPE_FILESYSTEM);
532 		if (zhp == NULL) {
533 			pam_syslog(NULL, LOG_ERR, "dataset %s not found",
534 			    config->homes_prefix);
535 			return (NULL);
536 		}
537 
538 		(void) zfs_iter_filesystems(zhp, find_dsname_by_prop_value,
539 		    config);
540 		zfs_close(zhp);
541 		char *dsname = config->dsname;
542 		config->dsname = NULL;
543 		return (dsname);
544 	}
545 
546 	if (config->homes_prefix == NULL) {
547 		return (NULL);
548 	}
549 
550 	size_t len = ZFS_MAX_DATASET_NAME_LEN;
551 	size_t total_len = strlen(config->homes_prefix) + 1
552 	    + strlen(config->username);
553 	if (total_len > len) {
554 		return (NULL);
555 	}
556 	char *ret = malloc(len + 1);
557 	if (!ret) {
558 		return (NULL);
559 	}
560 	ret[0] = 0;
561 	(void) snprintf(ret, len + 1, "%s/%s", config->homes_prefix,
562 	    config->username);
563 	return (ret);
564 }
565 
566 static int
567 zfs_key_config_modify_session_counter(pam_handle_t *pamh,
568     zfs_key_config_t *config, int delta)
569 {
570 	const char *runtime_path = config->runstatedir;
571 	if (mkdir(runtime_path, S_IRWXU) != 0 && errno != EEXIST) {
572 		pam_syslog(pamh, LOG_ERR, "Can't create runtime path: %d",
573 		    errno);
574 		return (-1);
575 	}
576 	if (chown(runtime_path, 0, 0) != 0) {
577 		pam_syslog(pamh, LOG_ERR, "Can't chown runtime path: %d",
578 		    errno);
579 		return (-1);
580 	}
581 	if (chmod(runtime_path, S_IRWXU) != 0) {
582 		pam_syslog(pamh, LOG_ERR, "Can't chmod runtime path: %d",
583 		    errno);
584 		return (-1);
585 	}
586 	size_t runtime_path_len = strlen(runtime_path);
587 	size_t counter_path_len = runtime_path_len + 1 + 10;
588 	char *counter_path = malloc(counter_path_len + 1);
589 	if (!counter_path) {
590 		return (-1);
591 	}
592 	counter_path[0] = 0;
593 	strcat(counter_path, runtime_path);
594 	snprintf(counter_path + runtime_path_len, counter_path_len, "/%d",
595 	    config->uid);
596 	const int fd = open(counter_path,
597 	    O_RDWR | O_CLOEXEC | O_CREAT | O_NOFOLLOW,
598 	    S_IRUSR | S_IWUSR);
599 	free(counter_path);
600 	if (fd < 0) {
601 		pam_syslog(pamh, LOG_ERR, "Can't open counter file: %d", errno);
602 		return (-1);
603 	}
604 	if (flock(fd, LOCK_EX) != 0) {
605 		pam_syslog(pamh, LOG_ERR, "Can't lock counter file: %d", errno);
606 		close(fd);
607 		return (-1);
608 	}
609 	char counter[20];
610 	char *pos = counter;
611 	int remaining = sizeof (counter) - 1;
612 	int ret;
613 	counter[sizeof (counter) - 1] = 0;
614 	while (remaining > 0 && (ret = read(fd, pos, remaining)) > 0) {
615 		remaining -= ret;
616 		pos += ret;
617 	}
618 	*pos = 0;
619 	long int counter_value = strtol(counter, NULL, 10);
620 	counter_value += delta;
621 	if (counter_value < 0) {
622 		counter_value = 0;
623 	}
624 	lseek(fd, 0, SEEK_SET);
625 	if (ftruncate(fd, 0) != 0) {
626 		pam_syslog(pamh, LOG_ERR, "Can't truncate counter file: %d",
627 		    errno);
628 		close(fd);
629 		return (-1);
630 	}
631 	snprintf(counter, sizeof (counter), "%ld", counter_value);
632 	remaining = strlen(counter);
633 	pos = counter;
634 	while (remaining > 0 && (ret = write(fd, pos, remaining)) > 0) {
635 		remaining -= ret;
636 		pos += ret;
637 	}
638 	close(fd);
639 	return (counter_value);
640 }
641 
642 __attribute__((visibility("default")))
643 PAM_EXTERN int
644 pam_sm_authenticate(pam_handle_t *pamh, int flags,
645     int argc, const char **argv)
646 {
647 	(void) flags, (void) argc, (void) argv;
648 
649 	if (pw_fetch_lazy(pamh) == NULL) {
650 		return (PAM_AUTH_ERR);
651 	}
652 
653 	return (PAM_SUCCESS);
654 }
655 
656 __attribute__((visibility("default")))
657 PAM_EXTERN int
658 pam_sm_setcred(pam_handle_t *pamh, int flags,
659     int argc, const char **argv)
660 {
661 	(void) pamh, (void) flags, (void) argc, (void) argv;
662 	return (PAM_SUCCESS);
663 }
664 
665 __attribute__((visibility("default")))
666 PAM_EXTERN int
667 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
668     int argc, const char **argv)
669 {
670 	if (geteuid() != 0) {
671 		pam_syslog(pamh, LOG_ERR,
672 		    "Cannot zfs_mount when not being root.");
673 		return (PAM_PERM_DENIED);
674 	}
675 	zfs_key_config_t config;
676 	if (zfs_key_config_load(pamh, &config, argc, argv) == -1) {
677 		return (PAM_SERVICE_ERR);
678 	}
679 	if (config.uid < 1000) {
680 		zfs_key_config_free(&config);
681 		return (PAM_SUCCESS);
682 	}
683 	{
684 		if (pam_zfs_init(pamh) != 0) {
685 			zfs_key_config_free(&config);
686 			return (PAM_SERVICE_ERR);
687 		}
688 		char *dataset = zfs_key_config_get_dataset(&config);
689 		if (!dataset) {
690 			pam_zfs_free();
691 			zfs_key_config_free(&config);
692 			return (PAM_SERVICE_ERR);
693 		}
694 		int key_loaded = is_key_loaded(pamh, dataset);
695 		if (key_loaded == -1) {
696 			free(dataset);
697 			pam_zfs_free();
698 			zfs_key_config_free(&config);
699 			return (PAM_SERVICE_ERR);
700 		}
701 		free(dataset);
702 		pam_zfs_free();
703 		if (! key_loaded) {
704 			pam_syslog(pamh, LOG_ERR,
705 			    "key not loaded, returning try_again");
706 			zfs_key_config_free(&config);
707 			return (PAM_PERM_DENIED);
708 		}
709 	}
710 
711 	if ((flags & PAM_UPDATE_AUTHTOK) != 0) {
712 		const pw_password_t *token = pw_get(pamh);
713 		if (token == NULL) {
714 			zfs_key_config_free(&config);
715 			return (PAM_SERVICE_ERR);
716 		}
717 		if (pam_zfs_init(pamh) != 0) {
718 			zfs_key_config_free(&config);
719 			return (PAM_SERVICE_ERR);
720 		}
721 		char *dataset = zfs_key_config_get_dataset(&config);
722 		if (!dataset) {
723 			pam_zfs_free();
724 			zfs_key_config_free(&config);
725 			return (PAM_SERVICE_ERR);
726 		}
727 		if (change_key(pamh, dataset, token->value) == -1) {
728 			free(dataset);
729 			pam_zfs_free();
730 			zfs_key_config_free(&config);
731 			return (PAM_SERVICE_ERR);
732 		}
733 		free(dataset);
734 		pam_zfs_free();
735 		zfs_key_config_free(&config);
736 		if (pw_clear(pamh) == -1) {
737 			return (PAM_SERVICE_ERR);
738 		}
739 	} else {
740 		zfs_key_config_free(&config);
741 	}
742 	return (PAM_SUCCESS);
743 }
744 
745 PAM_EXTERN int
746 pam_sm_open_session(pam_handle_t *pamh, int flags,
747     int argc, const char **argv)
748 {
749 	(void) flags;
750 
751 	if (geteuid() != 0) {
752 		pam_syslog(pamh, LOG_ERR,
753 		    "Cannot zfs_mount when not being root.");
754 		return (PAM_SUCCESS);
755 	}
756 	zfs_key_config_t config;
757 	zfs_key_config_load(pamh, &config, argc, argv);
758 	if (config.uid < 1000) {
759 		zfs_key_config_free(&config);
760 		return (PAM_SUCCESS);
761 	}
762 
763 	int counter = zfs_key_config_modify_session_counter(pamh, &config, 1);
764 	if (counter != 1) {
765 		zfs_key_config_free(&config);
766 		return (PAM_SUCCESS);
767 	}
768 
769 	const pw_password_t *token = pw_get(pamh);
770 	if (token == NULL) {
771 		zfs_key_config_free(&config);
772 		return (PAM_SESSION_ERR);
773 	}
774 	if (pam_zfs_init(pamh) != 0) {
775 		zfs_key_config_free(&config);
776 		return (PAM_SERVICE_ERR);
777 	}
778 	char *dataset = zfs_key_config_get_dataset(&config);
779 	if (!dataset) {
780 		pam_zfs_free();
781 		zfs_key_config_free(&config);
782 		return (PAM_SERVICE_ERR);
783 	}
784 	if (decrypt_mount(pamh, dataset, token->value) == -1) {
785 		free(dataset);
786 		pam_zfs_free();
787 		zfs_key_config_free(&config);
788 		return (PAM_SERVICE_ERR);
789 	}
790 	free(dataset);
791 	pam_zfs_free();
792 	zfs_key_config_free(&config);
793 	if (pw_clear(pamh) == -1) {
794 		return (PAM_SERVICE_ERR);
795 	}
796 	return (PAM_SUCCESS);
797 
798 }
799 
800 __attribute__((visibility("default")))
801 PAM_EXTERN int
802 pam_sm_close_session(pam_handle_t *pamh, int flags,
803     int argc, const char **argv)
804 {
805 	(void) flags;
806 
807 	if (geteuid() != 0) {
808 		pam_syslog(pamh, LOG_ERR,
809 		    "Cannot zfs_mount when not being root.");
810 		return (PAM_SUCCESS);
811 	}
812 	zfs_key_config_t config;
813 	if (zfs_key_config_load(pamh, &config, argc, argv) != 0) {
814 		return (PAM_SESSION_ERR);
815 	}
816 	if (config.uid < 1000) {
817 		zfs_key_config_free(&config);
818 		return (PAM_SUCCESS);
819 	}
820 
821 	int counter = zfs_key_config_modify_session_counter(pamh, &config, -1);
822 	if (counter != 0) {
823 		zfs_key_config_free(&config);
824 		return (PAM_SUCCESS);
825 	}
826 
827 	if (config.unmount_and_unload) {
828 		if (pam_zfs_init(pamh) != 0) {
829 			zfs_key_config_free(&config);
830 			return (PAM_SERVICE_ERR);
831 		}
832 		char *dataset = zfs_key_config_get_dataset(&config);
833 		if (!dataset) {
834 			pam_zfs_free();
835 			zfs_key_config_free(&config);
836 			return (PAM_SESSION_ERR);
837 		}
838 		if (unmount_unload(pamh, dataset) == -1) {
839 			free(dataset);
840 			pam_zfs_free();
841 			zfs_key_config_free(&config);
842 			return (PAM_SESSION_ERR);
843 		}
844 		free(dataset);
845 		pam_zfs_free();
846 	}
847 
848 	zfs_key_config_free(&config);
849 	return (PAM_SUCCESS);
850 }
851