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 #elif	defined(__FreeBSD__)
45 #include <security/pam_appl.h>
46 static void
47 pam_syslog(pam_handle_t *pamh, int loglevel, const char *fmt, ...)
48 {
49 	va_list args;
50 	va_start(args, fmt);
51 	vsyslog(loglevel, fmt, args);
52 	va_end(args);
53 }
54 #endif
55 
56 #include <string.h>
57 
58 #include <fcntl.h>
59 #include <sys/stat.h>
60 #include <sys/file.h>
61 #include <sys/wait.h>
62 #include <pwd.h>
63 
64 #include <sys/mman.h>
65 
66 static const char PASSWORD_VAR_NAME[] = "pam_zfs_key_authtok";
67 
68 static libzfs_handle_t *g_zfs;
69 
70 static void destroy_pw(pam_handle_t *pamh, void *data, int errcode);
71 
72 typedef struct {
73 	size_t len;
74 	char *value;
75 } pw_password_t;
76 
77 static pw_password_t *
78 alloc_pw_size(size_t len)
79 {
80 	pw_password_t *pw = malloc(sizeof (pw_password_t));
81 	if (!pw) {
82 		return (NULL);
83 	}
84 	pw->len = len;
85 	pw->value = malloc(len);
86 	if (!pw->value) {
87 		free(pw);
88 		return (NULL);
89 	}
90 	mlock(pw->value, pw->len);
91 	return (pw);
92 }
93 
94 static pw_password_t *
95 alloc_pw_string(const char *source)
96 {
97 	pw_password_t *pw = malloc(sizeof (pw_password_t));
98 	if (!pw) {
99 		return (NULL);
100 	}
101 	pw->len = strlen(source) + 1;
102 	pw->value = malloc(pw->len);
103 	if (!pw->value) {
104 		free(pw);
105 		return (NULL);
106 	}
107 	mlock(pw->value, pw->len);
108 	memcpy(pw->value, source, pw->len);
109 	return (pw);
110 }
111 
112 static void
113 pw_free(pw_password_t *pw)
114 {
115 	bzero(pw->value, pw->len);
116 	munlock(pw->value, pw->len);
117 	free(pw->value);
118 	free(pw);
119 }
120 
121 static pw_password_t *
122 pw_fetch(pam_handle_t *pamh)
123 {
124 	const char *token;
125 	if (pam_get_authtok(pamh, PAM_AUTHTOK, &token, NULL) != PAM_SUCCESS) {
126 		pam_syslog(pamh, LOG_ERR,
127 		    "couldn't get password from PAM stack");
128 		return (NULL);
129 	}
130 	if (!token) {
131 		pam_syslog(pamh, LOG_ERR,
132 		    "token from PAM stack is null");
133 		return (NULL);
134 	}
135 	return (alloc_pw_string(token));
136 }
137 
138 static const pw_password_t *
139 pw_fetch_lazy(pam_handle_t *pamh)
140 {
141 	pw_password_t *pw = pw_fetch(pamh);
142 	if (pw == NULL) {
143 		return (NULL);
144 	}
145 	int ret = pam_set_data(pamh, PASSWORD_VAR_NAME, pw, destroy_pw);
146 	if (ret != PAM_SUCCESS) {
147 		pw_free(pw);
148 		pam_syslog(pamh, LOG_ERR, "pam_set_data failed");
149 		return (NULL);
150 	}
151 	return (pw);
152 }
153 
154 static const pw_password_t *
155 pw_get(pam_handle_t *pamh)
156 {
157 	const pw_password_t *authtok = NULL;
158 	int ret = pam_get_data(pamh, PASSWORD_VAR_NAME,
159 	    (const void**)(&authtok));
160 	if (ret == PAM_SUCCESS)
161 		return (authtok);
162 	if (ret == PAM_NO_MODULE_DATA)
163 		return (pw_fetch_lazy(pamh));
164 	pam_syslog(pamh, LOG_ERR, "password not available");
165 	return (NULL);
166 }
167 
168 static int
169 pw_clear(pam_handle_t *pamh)
170 {
171 	int ret = pam_set_data(pamh, PASSWORD_VAR_NAME, NULL, NULL);
172 	if (ret != PAM_SUCCESS) {
173 		pam_syslog(pamh, LOG_ERR, "clearing password failed");
174 		return (-1);
175 	}
176 	return (0);
177 }
178 
179 static void
180 destroy_pw(pam_handle_t *pamh, void *data, int errcode)
181 {
182 	if (data != NULL) {
183 		pw_free((pw_password_t *)data);
184 	}
185 }
186 
187 static int
188 pam_zfs_init(pam_handle_t *pamh)
189 {
190 	int error = 0;
191 	if ((g_zfs = libzfs_init()) == NULL) {
192 		error = errno;
193 		pam_syslog(pamh, LOG_ERR, "Zfs initialization error: %s",
194 		    libzfs_error_init(error));
195 	}
196 	return (error);
197 }
198 
199 static void
200 pam_zfs_free(void)
201 {
202 	libzfs_fini(g_zfs);
203 }
204 
205 static pw_password_t *
206 prepare_passphrase(pam_handle_t *pamh, zfs_handle_t *ds,
207     const char *passphrase, nvlist_t *nvlist)
208 {
209 	pw_password_t *key = alloc_pw_size(WRAPPING_KEY_LEN);
210 	if (!key) {
211 		return (NULL);
212 	}
213 	uint64_t salt;
214 	uint64_t iters;
215 	if (nvlist != NULL) {
216 		int fd = open("/dev/urandom", O_RDONLY);
217 		if (fd < 0) {
218 			pw_free(key);
219 			return (NULL);
220 		}
221 		int bytes_read = 0;
222 		char *buf = (char *)&salt;
223 		size_t bytes = sizeof (uint64_t);
224 		while (bytes_read < bytes) {
225 			ssize_t len = read(fd, buf + bytes_read, bytes
226 			    - bytes_read);
227 			if (len < 0) {
228 				close(fd);
229 				pw_free(key);
230 				return (NULL);
231 			}
232 			bytes_read += len;
233 		}
234 		close(fd);
235 
236 		if (nvlist_add_uint64(nvlist,
237 		    zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), salt)) {
238 			pam_syslog(pamh, LOG_ERR,
239 			    "failed to add salt to nvlist");
240 			pw_free(key);
241 			return (NULL);
242 		}
243 		iters = DEFAULT_PBKDF2_ITERATIONS;
244 		if (nvlist_add_uint64(nvlist, zfs_prop_to_name(
245 		    ZFS_PROP_PBKDF2_ITERS), iters)) {
246 			pam_syslog(pamh, LOG_ERR,
247 			    "failed to add iters to nvlist");
248 			pw_free(key);
249 			return (NULL);
250 		}
251 	} else {
252 		salt = zfs_prop_get_int(ds, ZFS_PROP_PBKDF2_SALT);
253 		iters = zfs_prop_get_int(ds, ZFS_PROP_PBKDF2_ITERS);
254 	}
255 
256 	salt = LE_64(salt);
257 	if (!PKCS5_PBKDF2_HMAC_SHA1((char *)passphrase,
258 	    strlen(passphrase), (uint8_t *)&salt,
259 	    sizeof (uint64_t), iters, WRAPPING_KEY_LEN,
260 	    (uint8_t *)key->value)) {
261 		pam_syslog(pamh, LOG_ERR, "pbkdf failed");
262 		pw_free(key);
263 		return (NULL);
264 	}
265 	return (key);
266 }
267 
268 static int
269 is_key_loaded(pam_handle_t *pamh, const char *ds_name)
270 {
271 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
272 	if (ds == NULL) {
273 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
274 		return (-1);
275 	}
276 	int keystatus = zfs_prop_get_int(ds, ZFS_PROP_KEYSTATUS);
277 	zfs_close(ds);
278 	return (keystatus != ZFS_KEYSTATUS_UNAVAILABLE);
279 }
280 
281 static int
282 change_key(pam_handle_t *pamh, const char *ds_name,
283     const char *passphrase)
284 {
285 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
286 	if (ds == NULL) {
287 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
288 		return (-1);
289 	}
290 	nvlist_t *nvlist = fnvlist_alloc();
291 	pw_password_t *key = prepare_passphrase(pamh, ds, passphrase, nvlist);
292 	if (key == NULL) {
293 		nvlist_free(nvlist);
294 		zfs_close(ds);
295 		return (-1);
296 	}
297 	if (nvlist_add_string(nvlist,
298 	    zfs_prop_to_name(ZFS_PROP_KEYLOCATION),
299 	    "prompt")) {
300 		pam_syslog(pamh, LOG_ERR, "nvlist_add failed for keylocation");
301 		pw_free(key);
302 		nvlist_free(nvlist);
303 		zfs_close(ds);
304 		return (-1);
305 	}
306 	if (nvlist_add_uint64(nvlist,
307 	    zfs_prop_to_name(ZFS_PROP_KEYFORMAT),
308 	    ZFS_KEYFORMAT_PASSPHRASE)) {
309 		pam_syslog(pamh, LOG_ERR, "nvlist_add failed for keyformat");
310 		pw_free(key);
311 		nvlist_free(nvlist);
312 		zfs_close(ds);
313 		return (-1);
314 	}
315 	int ret = lzc_change_key(ds_name, DCP_CMD_NEW_KEY, nvlist,
316 	    (uint8_t *)key->value, WRAPPING_KEY_LEN);
317 	pw_free(key);
318 	if (ret) {
319 		pam_syslog(pamh, LOG_ERR, "change_key failed: %d", ret);
320 		nvlist_free(nvlist);
321 		zfs_close(ds);
322 		return (-1);
323 	}
324 	nvlist_free(nvlist);
325 	zfs_close(ds);
326 	return (0);
327 }
328 
329 static int
330 decrypt_mount(pam_handle_t *pamh, const char *ds_name,
331     const char *passphrase)
332 {
333 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
334 	if (ds == NULL) {
335 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
336 		return (-1);
337 	}
338 	pw_password_t *key = prepare_passphrase(pamh, ds, passphrase, NULL);
339 	if (key == NULL) {
340 		zfs_close(ds);
341 		return (-1);
342 	}
343 	int ret = lzc_load_key(ds_name, B_FALSE, (uint8_t *)key->value,
344 	    WRAPPING_KEY_LEN);
345 	pw_free(key);
346 	if (ret) {
347 		pam_syslog(pamh, LOG_ERR, "load_key failed: %d", ret);
348 		zfs_close(ds);
349 		return (-1);
350 	}
351 	ret = zfs_mount(ds, NULL, 0);
352 	if (ret) {
353 		pam_syslog(pamh, LOG_ERR, "mount failed: %d", ret);
354 		zfs_close(ds);
355 		return (-1);
356 	}
357 	zfs_close(ds);
358 	return (0);
359 }
360 
361 static int
362 unmount_unload(pam_handle_t *pamh, const char *ds_name)
363 {
364 	zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
365 	if (ds == NULL) {
366 		pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
367 		return (-1);
368 	}
369 	int ret = zfs_unmount(ds, NULL, 0);
370 	if (ret) {
371 		pam_syslog(pamh, LOG_ERR, "zfs_unmount failed with: %d", ret);
372 		zfs_close(ds);
373 		return (-1);
374 	}
375 
376 	ret = lzc_unload_key(ds_name);
377 	if (ret) {
378 		pam_syslog(pamh, LOG_ERR, "unload_key failed with: %d", ret);
379 		zfs_close(ds);
380 		return (-1);
381 	}
382 	zfs_close(ds);
383 	return (0);
384 }
385 
386 typedef struct {
387 	char *homes_prefix;
388 	char *runstatedir;
389 	uid_t uid;
390 	const char *username;
391 	int unmount_and_unload;
392 } zfs_key_config_t;
393 
394 static int
395 zfs_key_config_load(pam_handle_t *pamh, zfs_key_config_t *config,
396     int argc, const char **argv)
397 {
398 	config->homes_prefix = strdup("rpool/home");
399 	if (config->homes_prefix == NULL) {
400 		pam_syslog(pamh, LOG_ERR, "strdup failure");
401 		return (-1);
402 	}
403 	config->runstatedir = strdup(RUNSTATEDIR "/pam_zfs_key");
404 	if (config->runstatedir == NULL) {
405 		pam_syslog(pamh, LOG_ERR, "strdup failure");
406 		free(config->homes_prefix);
407 		return (-1);
408 	}
409 	const char *name;
410 	if (pam_get_user(pamh, &name, NULL) != PAM_SUCCESS) {
411 		pam_syslog(pamh, LOG_ERR,
412 		    "couldn't get username from PAM stack");
413 		free(config->runstatedir);
414 		free(config->homes_prefix);
415 		return (-1);
416 	}
417 	struct passwd *entry = getpwnam(name);
418 	if (!entry) {
419 		free(config->runstatedir);
420 		free(config->homes_prefix);
421 		return (-1);
422 	}
423 	config->uid = entry->pw_uid;
424 	config->username = name;
425 	config->unmount_and_unload = 1;
426 	for (int c = 0; c < argc; c++) {
427 		if (strncmp(argv[c], "homes=", 6) == 0) {
428 			free(config->homes_prefix);
429 			config->homes_prefix = strdup(argv[c] + 6);
430 		} else if (strncmp(argv[c], "runstatedir=", 12) == 0) {
431 			free(config->runstatedir);
432 			config->runstatedir = strdup(argv[c] + 12);
433 		} else if (strcmp(argv[c], "nounmount") == 0) {
434 			config->unmount_and_unload = 0;
435 		}
436 	}
437 	return (0);
438 }
439 
440 static void
441 zfs_key_config_free(zfs_key_config_t *config)
442 {
443 	free(config->homes_prefix);
444 }
445 
446 static char *
447 zfs_key_config_get_dataset(zfs_key_config_t *config)
448 {
449 	size_t len = ZFS_MAX_DATASET_NAME_LEN;
450 	size_t total_len = strlen(config->homes_prefix) + 1
451 	    + strlen(config->username);
452 	if (total_len > len) {
453 		return (NULL);
454 	}
455 	char *ret = malloc(len + 1);
456 	if (!ret) {
457 		return (NULL);
458 	}
459 	ret[0] = 0;
460 	strcat(ret, config->homes_prefix);
461 	strcat(ret, "/");
462 	strcat(ret, config->username);
463 	return (ret);
464 }
465 
466 static int
467 zfs_key_config_modify_session_counter(pam_handle_t *pamh,
468     zfs_key_config_t *config, int delta)
469 {
470 	const char *runtime_path = config->runstatedir;
471 	if (mkdir(runtime_path, S_IRWXU) != 0 && errno != EEXIST) {
472 		pam_syslog(pamh, LOG_ERR, "Can't create runtime path: %d",
473 		    errno);
474 		return (-1);
475 	}
476 	if (chown(runtime_path, 0, 0) != 0) {
477 		pam_syslog(pamh, LOG_ERR, "Can't chown runtime path: %d",
478 		    errno);
479 		return (-1);
480 	}
481 	if (chmod(runtime_path, S_IRWXU) != 0) {
482 		pam_syslog(pamh, LOG_ERR, "Can't chmod runtime path: %d",
483 		    errno);
484 		return (-1);
485 	}
486 	size_t runtime_path_len = strlen(runtime_path);
487 	size_t counter_path_len = runtime_path_len + 1 + 10;
488 	char *counter_path = malloc(counter_path_len + 1);
489 	if (!counter_path) {
490 		return (-1);
491 	}
492 	counter_path[0] = 0;
493 	strcat(counter_path, runtime_path);
494 	snprintf(counter_path + runtime_path_len, counter_path_len, "/%d",
495 	    config->uid);
496 	const int fd = open(counter_path,
497 	    O_RDWR | O_CLOEXEC | O_CREAT | O_NOFOLLOW,
498 	    S_IRUSR | S_IWUSR);
499 	free(counter_path);
500 	if (fd < 0) {
501 		pam_syslog(pamh, LOG_ERR, "Can't open counter file: %d", errno);
502 		return (-1);
503 	}
504 	if (flock(fd, LOCK_EX) != 0) {
505 		pam_syslog(pamh, LOG_ERR, "Can't lock counter file: %d", errno);
506 		close(fd);
507 		return (-1);
508 	}
509 	char counter[20];
510 	char *pos = counter;
511 	int remaining = sizeof (counter) - 1;
512 	int ret;
513 	counter[sizeof (counter) - 1] = 0;
514 	while (remaining > 0 && (ret = read(fd, pos, remaining)) > 0) {
515 		remaining -= ret;
516 		pos += ret;
517 	}
518 	*pos = 0;
519 	long int counter_value = strtol(counter, NULL, 10);
520 	counter_value += delta;
521 	if (counter_value < 0) {
522 		counter_value = 0;
523 	}
524 	lseek(fd, 0, SEEK_SET);
525 	if (ftruncate(fd, 0) != 0) {
526 		pam_syslog(pamh, LOG_ERR, "Can't truncate counter file: %d",
527 		    errno);
528 		close(fd);
529 		return (-1);
530 	}
531 	snprintf(counter, sizeof (counter), "%ld", counter_value);
532 	remaining = strlen(counter);
533 	pos = counter;
534 	while (remaining > 0 && (ret = write(fd, pos, remaining)) > 0) {
535 		remaining -= ret;
536 		pos += ret;
537 	}
538 	close(fd);
539 	return (counter_value);
540 }
541 
542 __attribute__((visibility("default")))
543 PAM_EXTERN int
544 pam_sm_authenticate(pam_handle_t *pamh, int flags,
545     int argc, const char **argv)
546 {
547 	if (pw_fetch_lazy(pamh) == NULL) {
548 		return (PAM_AUTH_ERR);
549 	}
550 
551 	return (PAM_SUCCESS);
552 }
553 
554 __attribute__((visibility("default")))
555 PAM_EXTERN int
556 pam_sm_setcred(pam_handle_t *pamh, int flags,
557     int argc, const char **argv)
558 {
559 	return (PAM_SUCCESS);
560 }
561 
562 __attribute__((visibility("default")))
563 PAM_EXTERN int
564 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
565     int argc, const char **argv)
566 {
567 	if (geteuid() != 0) {
568 		pam_syslog(pamh, LOG_ERR,
569 		    "Cannot zfs_mount when not being root.");
570 		return (PAM_PERM_DENIED);
571 	}
572 	zfs_key_config_t config;
573 	if (zfs_key_config_load(pamh, &config, argc, argv) == -1) {
574 		return (PAM_SERVICE_ERR);
575 	}
576 	if (config.uid < 1000) {
577 		zfs_key_config_free(&config);
578 		return (PAM_SUCCESS);
579 	}
580 	{
581 		if (pam_zfs_init(pamh) != 0) {
582 			zfs_key_config_free(&config);
583 			return (PAM_SERVICE_ERR);
584 		}
585 		char *dataset = zfs_key_config_get_dataset(&config);
586 		if (!dataset) {
587 			pam_zfs_free();
588 			zfs_key_config_free(&config);
589 			return (PAM_SERVICE_ERR);
590 		}
591 		int key_loaded = is_key_loaded(pamh, dataset);
592 		if (key_loaded == -1) {
593 			free(dataset);
594 			pam_zfs_free();
595 			zfs_key_config_free(&config);
596 			return (PAM_SERVICE_ERR);
597 		}
598 		free(dataset);
599 		pam_zfs_free();
600 		if (! key_loaded) {
601 			pam_syslog(pamh, LOG_ERR,
602 			    "key not loaded, returning try_again");
603 			zfs_key_config_free(&config);
604 			return (PAM_PERM_DENIED);
605 		}
606 	}
607 
608 	if ((flags & PAM_UPDATE_AUTHTOK) != 0) {
609 		const pw_password_t *token = pw_get(pamh);
610 		if (token == NULL) {
611 			zfs_key_config_free(&config);
612 			return (PAM_SERVICE_ERR);
613 		}
614 		if (pam_zfs_init(pamh) != 0) {
615 			zfs_key_config_free(&config);
616 			return (PAM_SERVICE_ERR);
617 		}
618 		char *dataset = zfs_key_config_get_dataset(&config);
619 		if (!dataset) {
620 			pam_zfs_free();
621 			zfs_key_config_free(&config);
622 			return (PAM_SERVICE_ERR);
623 		}
624 		if (change_key(pamh, dataset, token->value) == -1) {
625 			free(dataset);
626 			pam_zfs_free();
627 			zfs_key_config_free(&config);
628 			return (PAM_SERVICE_ERR);
629 		}
630 		free(dataset);
631 		pam_zfs_free();
632 		zfs_key_config_free(&config);
633 		if (pw_clear(pamh) == -1) {
634 			return (PAM_SERVICE_ERR);
635 		}
636 	} else {
637 		zfs_key_config_free(&config);
638 	}
639 	return (PAM_SUCCESS);
640 }
641 
642 PAM_EXTERN int
643 pam_sm_open_session(pam_handle_t *pamh, int flags,
644     int argc, const char **argv)
645 {
646 	if (geteuid() != 0) {
647 		pam_syslog(pamh, LOG_ERR,
648 		    "Cannot zfs_mount when not being root.");
649 		return (PAM_SUCCESS);
650 	}
651 	zfs_key_config_t config;
652 	zfs_key_config_load(pamh, &config, argc, argv);
653 	if (config.uid < 1000) {
654 		zfs_key_config_free(&config);
655 		return (PAM_SUCCESS);
656 	}
657 
658 	int counter = zfs_key_config_modify_session_counter(pamh, &config, 1);
659 	if (counter != 1) {
660 		zfs_key_config_free(&config);
661 		return (PAM_SUCCESS);
662 	}
663 
664 	const pw_password_t *token = pw_get(pamh);
665 	if (token == NULL) {
666 		zfs_key_config_free(&config);
667 		return (PAM_SESSION_ERR);
668 	}
669 	if (pam_zfs_init(pamh) != 0) {
670 		zfs_key_config_free(&config);
671 		return (PAM_SERVICE_ERR);
672 	}
673 	char *dataset = zfs_key_config_get_dataset(&config);
674 	if (!dataset) {
675 		pam_zfs_free();
676 		zfs_key_config_free(&config);
677 		return (PAM_SERVICE_ERR);
678 	}
679 	if (decrypt_mount(pamh, dataset, token->value) == -1) {
680 		free(dataset);
681 		pam_zfs_free();
682 		zfs_key_config_free(&config);
683 		return (PAM_SERVICE_ERR);
684 	}
685 	free(dataset);
686 	pam_zfs_free();
687 	zfs_key_config_free(&config);
688 	if (pw_clear(pamh) == -1) {
689 		return (PAM_SERVICE_ERR);
690 	}
691 	return (PAM_SUCCESS);
692 
693 }
694 
695 __attribute__((visibility("default")))
696 PAM_EXTERN int
697 pam_sm_close_session(pam_handle_t *pamh, int flags,
698     int argc, const char **argv)
699 {
700 	if (geteuid() != 0) {
701 		pam_syslog(pamh, LOG_ERR,
702 		    "Cannot zfs_mount when not being root.");
703 		return (PAM_SUCCESS);
704 	}
705 	zfs_key_config_t config;
706 	zfs_key_config_load(pamh, &config, argc, argv);
707 	if (config.uid < 1000) {
708 		zfs_key_config_free(&config);
709 		return (PAM_SUCCESS);
710 	}
711 
712 	int counter = zfs_key_config_modify_session_counter(pamh, &config, -1);
713 	if (counter != 0) {
714 		zfs_key_config_free(&config);
715 		return (PAM_SUCCESS);
716 	}
717 
718 	if (config.unmount_and_unload) {
719 		if (pam_zfs_init(pamh) != 0) {
720 			zfs_key_config_free(&config);
721 			return (PAM_SERVICE_ERR);
722 		}
723 		char *dataset = zfs_key_config_get_dataset(&config);
724 		if (!dataset) {
725 			pam_zfs_free();
726 			zfs_key_config_free(&config);
727 			return (PAM_SESSION_ERR);
728 		}
729 		if (unmount_unload(pamh, dataset) == -1) {
730 			free(dataset);
731 			pam_zfs_free();
732 			zfs_key_config_free(&config);
733 			return (PAM_SESSION_ERR);
734 		}
735 		free(dataset);
736 		pam_zfs_free();
737 	}
738 
739 	zfs_key_config_free(&config);
740 	return (PAM_SUCCESS);
741 }
742