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
pam_syslog(pam_handle_t * pamh,int loglevel,const char * fmt,...)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 *
alloc_pw_size(size_t len)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 *
alloc_pw_string(const char * source)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
pw_free(pw_password_t * pw)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 *
pw_fetch(pam_handle_t * pamh)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 *
pw_fetch_lazy(pam_handle_t * pamh)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 *
pw_get(pam_handle_t * pamh)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
pw_clear(pam_handle_t * pamh)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
destroy_pw(pam_handle_t * pamh,void * data,int errcode)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
pam_zfs_init(pam_handle_t * pamh)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
pam_zfs_free(void)200 pam_zfs_free(void)
201 {
202 	libzfs_fini(g_zfs);
203 }
204 
205 static pw_password_t *
prepare_passphrase(pam_handle_t * pamh,zfs_handle_t * ds,const char * passphrase,nvlist_t * nvlist)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
is_key_loaded(pam_handle_t * pamh,const char * ds_name)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
change_key(pam_handle_t * pamh,const char * ds_name,const char * passphrase)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
decrypt_mount(pam_handle_t * pamh,const char * ds_name,const char * passphrase)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
unmount_unload(pam_handle_t * pamh,const char * ds_name)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 	char *homedir;
390 	char *dsname;
391 	uid_t uid;
392 	const char *username;
393 	int unmount_and_unload;
394 } zfs_key_config_t;
395 
396 static int
zfs_key_config_load(pam_handle_t * pamh,zfs_key_config_t * config,int argc,const char ** argv)397 zfs_key_config_load(pam_handle_t *pamh, zfs_key_config_t *config,
398     int argc, const char **argv)
399 {
400 	config->homes_prefix = strdup("rpool/home");
401 	if (config->homes_prefix == NULL) {
402 		pam_syslog(pamh, LOG_ERR, "strdup failure");
403 		return (-1);
404 	}
405 	config->runstatedir = strdup(RUNSTATEDIR "/pam_zfs_key");
406 	if (config->runstatedir == NULL) {
407 		pam_syslog(pamh, LOG_ERR, "strdup failure");
408 		free(config->homes_prefix);
409 		return (-1);
410 	}
411 	const char *name;
412 	if (pam_get_user(pamh, &name, NULL) != PAM_SUCCESS) {
413 		pam_syslog(pamh, LOG_ERR,
414 		    "couldn't get username from PAM stack");
415 		free(config->runstatedir);
416 		free(config->homes_prefix);
417 		return (-1);
418 	}
419 	struct passwd *entry = getpwnam(name);
420 	if (!entry) {
421 		free(config->runstatedir);
422 		free(config->homes_prefix);
423 		return (-1);
424 	}
425 	config->uid = entry->pw_uid;
426 	config->username = name;
427 	config->unmount_and_unload = 1;
428 	config->dsname = NULL;
429 	config->homedir = NULL;
430 	for (int c = 0; c < argc; c++) {
431 		if (strncmp(argv[c], "homes=", 6) == 0) {
432 			free(config->homes_prefix);
433 			config->homes_prefix = strdup(argv[c] + 6);
434 		} else if (strncmp(argv[c], "runstatedir=", 12) == 0) {
435 			free(config->runstatedir);
436 			config->runstatedir = strdup(argv[c] + 12);
437 		} else if (strcmp(argv[c], "nounmount") == 0) {
438 			config->unmount_and_unload = 0;
439 		} else if (strcmp(argv[c], "prop_mountpoint") == 0) {
440 			config->homedir = strdup(entry->pw_dir);
441 		}
442 	}
443 	return (0);
444 }
445 
446 static void
zfs_key_config_free(zfs_key_config_t * config)447 zfs_key_config_free(zfs_key_config_t *config)
448 {
449 	free(config->homes_prefix);
450 	free(config->runstatedir);
451 	free(config->homedir);
452 	free(config->dsname);
453 }
454 
455 static int
find_dsname_by_prop_value(zfs_handle_t * zhp,void * data)456 find_dsname_by_prop_value(zfs_handle_t *zhp, void *data)
457 {
458 	zfs_type_t type = zfs_get_type(zhp);
459 	zfs_key_config_t *target = data;
460 	char mountpoint[ZFS_MAXPROPLEN];
461 
462 	/* Skip any datasets whose type does not match */
463 	if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
464 		zfs_close(zhp);
465 		return (0);
466 	}
467 
468 	/* Skip any datasets whose mountpoint does not match */
469 	(void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
470 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
471 	if (strcmp(target->homedir, mountpoint) != 0) {
472 		zfs_close(zhp);
473 		return (0);
474 	}
475 
476 	target->dsname = strdup(zfs_get_name(zhp));
477 	zfs_close(zhp);
478 	return (1);
479 }
480 
481 static char *
zfs_key_config_get_dataset(zfs_key_config_t * config)482 zfs_key_config_get_dataset(zfs_key_config_t *config)
483 {
484 	if (config->homedir != NULL &&
485 	    config->homes_prefix != NULL) {
486 		zfs_handle_t *zhp = zfs_open(g_zfs, config->homes_prefix,
487 		    ZFS_TYPE_FILESYSTEM);
488 		if (zhp == NULL) {
489 			pam_syslog(NULL, LOG_ERR, "dataset %s not found",
490 			    config->homes_prefix);
491 			zfs_close(zhp);
492 			return (NULL);
493 		}
494 
495 		(void) zfs_iter_filesystems(zhp, find_dsname_by_prop_value,
496 		    config);
497 		zfs_close(zhp);
498 		char *dsname = config->dsname;
499 		config->dsname = NULL;
500 		return (dsname);
501 	}
502 
503 	size_t len = ZFS_MAX_DATASET_NAME_LEN;
504 	size_t total_len = strlen(config->homes_prefix) + 1
505 	    + strlen(config->username);
506 	if (total_len > len) {
507 		return (NULL);
508 	}
509 	char *ret = malloc(len + 1);
510 	if (!ret) {
511 		return (NULL);
512 	}
513 	ret[0] = 0;
514 	strcat(ret, config->homes_prefix);
515 	strcat(ret, "/");
516 	strcat(ret, config->username);
517 	return (ret);
518 }
519 
520 static int
zfs_key_config_modify_session_counter(pam_handle_t * pamh,zfs_key_config_t * config,int delta)521 zfs_key_config_modify_session_counter(pam_handle_t *pamh,
522     zfs_key_config_t *config, int delta)
523 {
524 	const char *runtime_path = config->runstatedir;
525 	if (mkdir(runtime_path, S_IRWXU) != 0 && errno != EEXIST) {
526 		pam_syslog(pamh, LOG_ERR, "Can't create runtime path: %d",
527 		    errno);
528 		return (-1);
529 	}
530 	if (chown(runtime_path, 0, 0) != 0) {
531 		pam_syslog(pamh, LOG_ERR, "Can't chown runtime path: %d",
532 		    errno);
533 		return (-1);
534 	}
535 	if (chmod(runtime_path, S_IRWXU) != 0) {
536 		pam_syslog(pamh, LOG_ERR, "Can't chmod runtime path: %d",
537 		    errno);
538 		return (-1);
539 	}
540 	size_t runtime_path_len = strlen(runtime_path);
541 	size_t counter_path_len = runtime_path_len + 1 + 10;
542 	char *counter_path = malloc(counter_path_len + 1);
543 	if (!counter_path) {
544 		return (-1);
545 	}
546 	counter_path[0] = 0;
547 	strcat(counter_path, runtime_path);
548 	snprintf(counter_path + runtime_path_len, counter_path_len, "/%d",
549 	    config->uid);
550 	const int fd = open(counter_path,
551 	    O_RDWR | O_CLOEXEC | O_CREAT | O_NOFOLLOW,
552 	    S_IRUSR | S_IWUSR);
553 	free(counter_path);
554 	if (fd < 0) {
555 		pam_syslog(pamh, LOG_ERR, "Can't open counter file: %d", errno);
556 		return (-1);
557 	}
558 	if (flock(fd, LOCK_EX) != 0) {
559 		pam_syslog(pamh, LOG_ERR, "Can't lock counter file: %d", errno);
560 		close(fd);
561 		return (-1);
562 	}
563 	char counter[20];
564 	char *pos = counter;
565 	int remaining = sizeof (counter) - 1;
566 	int ret;
567 	counter[sizeof (counter) - 1] = 0;
568 	while (remaining > 0 && (ret = read(fd, pos, remaining)) > 0) {
569 		remaining -= ret;
570 		pos += ret;
571 	}
572 	*pos = 0;
573 	long int counter_value = strtol(counter, NULL, 10);
574 	counter_value += delta;
575 	if (counter_value < 0) {
576 		counter_value = 0;
577 	}
578 	lseek(fd, 0, SEEK_SET);
579 	if (ftruncate(fd, 0) != 0) {
580 		pam_syslog(pamh, LOG_ERR, "Can't truncate counter file: %d",
581 		    errno);
582 		close(fd);
583 		return (-1);
584 	}
585 	snprintf(counter, sizeof (counter), "%ld", counter_value);
586 	remaining = strlen(counter);
587 	pos = counter;
588 	while (remaining > 0 && (ret = write(fd, pos, remaining)) > 0) {
589 		remaining -= ret;
590 		pos += ret;
591 	}
592 	close(fd);
593 	return (counter_value);
594 }
595 
596 __attribute__((visibility("default")))
597 PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)598 pam_sm_authenticate(pam_handle_t *pamh, int flags,
599     int argc, const char **argv)
600 {
601 	if (pw_fetch_lazy(pamh) == NULL) {
602 		return (PAM_AUTH_ERR);
603 	}
604 
605 	return (PAM_SUCCESS);
606 }
607 
608 __attribute__((visibility("default")))
609 PAM_EXTERN int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)610 pam_sm_setcred(pam_handle_t *pamh, int flags,
611     int argc, const char **argv)
612 {
613 	return (PAM_SUCCESS);
614 }
615 
616 __attribute__((visibility("default")))
617 PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)618 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
619     int argc, const char **argv)
620 {
621 	if (geteuid() != 0) {
622 		pam_syslog(pamh, LOG_ERR,
623 		    "Cannot zfs_mount when not being root.");
624 		return (PAM_PERM_DENIED);
625 	}
626 	zfs_key_config_t config;
627 	if (zfs_key_config_load(pamh, &config, argc, argv) == -1) {
628 		return (PAM_SERVICE_ERR);
629 	}
630 	if (config.uid < 1000) {
631 		zfs_key_config_free(&config);
632 		return (PAM_SUCCESS);
633 	}
634 	{
635 		if (pam_zfs_init(pamh) != 0) {
636 			zfs_key_config_free(&config);
637 			return (PAM_SERVICE_ERR);
638 		}
639 		char *dataset = zfs_key_config_get_dataset(&config);
640 		if (!dataset) {
641 			pam_zfs_free();
642 			zfs_key_config_free(&config);
643 			return (PAM_SERVICE_ERR);
644 		}
645 		int key_loaded = is_key_loaded(pamh, dataset);
646 		if (key_loaded == -1) {
647 			free(dataset);
648 			pam_zfs_free();
649 			zfs_key_config_free(&config);
650 			return (PAM_SERVICE_ERR);
651 		}
652 		free(dataset);
653 		pam_zfs_free();
654 		if (! key_loaded) {
655 			pam_syslog(pamh, LOG_ERR,
656 			    "key not loaded, returning try_again");
657 			zfs_key_config_free(&config);
658 			return (PAM_PERM_DENIED);
659 		}
660 	}
661 
662 	if ((flags & PAM_UPDATE_AUTHTOK) != 0) {
663 		const pw_password_t *token = pw_get(pamh);
664 		if (token == NULL) {
665 			zfs_key_config_free(&config);
666 			return (PAM_SERVICE_ERR);
667 		}
668 		if (pam_zfs_init(pamh) != 0) {
669 			zfs_key_config_free(&config);
670 			return (PAM_SERVICE_ERR);
671 		}
672 		char *dataset = zfs_key_config_get_dataset(&config);
673 		if (!dataset) {
674 			pam_zfs_free();
675 			zfs_key_config_free(&config);
676 			return (PAM_SERVICE_ERR);
677 		}
678 		if (change_key(pamh, dataset, token->value) == -1) {
679 			free(dataset);
680 			pam_zfs_free();
681 			zfs_key_config_free(&config);
682 			return (PAM_SERVICE_ERR);
683 		}
684 		free(dataset);
685 		pam_zfs_free();
686 		zfs_key_config_free(&config);
687 		if (pw_clear(pamh) == -1) {
688 			return (PAM_SERVICE_ERR);
689 		}
690 	} else {
691 		zfs_key_config_free(&config);
692 	}
693 	return (PAM_SUCCESS);
694 }
695 
696 PAM_EXTERN int
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)697 pam_sm_open_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 != 1) {
714 		zfs_key_config_free(&config);
715 		return (PAM_SUCCESS);
716 	}
717 
718 	const pw_password_t *token = pw_get(pamh);
719 	if (token == NULL) {
720 		zfs_key_config_free(&config);
721 		return (PAM_SESSION_ERR);
722 	}
723 	if (pam_zfs_init(pamh) != 0) {
724 		zfs_key_config_free(&config);
725 		return (PAM_SERVICE_ERR);
726 	}
727 	char *dataset = zfs_key_config_get_dataset(&config);
728 	if (!dataset) {
729 		pam_zfs_free();
730 		zfs_key_config_free(&config);
731 		return (PAM_SERVICE_ERR);
732 	}
733 	if (decrypt_mount(pamh, dataset, token->value) == -1) {
734 		free(dataset);
735 		pam_zfs_free();
736 		zfs_key_config_free(&config);
737 		return (PAM_SERVICE_ERR);
738 	}
739 	free(dataset);
740 	pam_zfs_free();
741 	zfs_key_config_free(&config);
742 	if (pw_clear(pamh) == -1) {
743 		return (PAM_SERVICE_ERR);
744 	}
745 	return (PAM_SUCCESS);
746 
747 }
748 
749 __attribute__((visibility("default")))
750 PAM_EXTERN int
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)751 pam_sm_close_session(pam_handle_t *pamh, int flags,
752     int argc, const char **argv)
753 {
754 	if (geteuid() != 0) {
755 		pam_syslog(pamh, LOG_ERR,
756 		    "Cannot zfs_mount when not being root.");
757 		return (PAM_SUCCESS);
758 	}
759 	zfs_key_config_t config;
760 	zfs_key_config_load(pamh, &config, argc, argv);
761 	if (config.uid < 1000) {
762 		zfs_key_config_free(&config);
763 		return (PAM_SUCCESS);
764 	}
765 
766 	int counter = zfs_key_config_modify_session_counter(pamh, &config, -1);
767 	if (counter != 0) {
768 		zfs_key_config_free(&config);
769 		return (PAM_SUCCESS);
770 	}
771 
772 	if (config.unmount_and_unload) {
773 		if (pam_zfs_init(pamh) != 0) {
774 			zfs_key_config_free(&config);
775 			return (PAM_SERVICE_ERR);
776 		}
777 		char *dataset = zfs_key_config_get_dataset(&config);
778 		if (!dataset) {
779 			pam_zfs_free();
780 			zfs_key_config_free(&config);
781 			return (PAM_SESSION_ERR);
782 		}
783 		if (unmount_unload(pamh, dataset) == -1) {
784 			free(dataset);
785 			pam_zfs_free();
786 			zfs_key_config_free(&config);
787 			return (PAM_SESSION_ERR);
788 		}
789 		free(dataset);
790 		pam_zfs_free();
791 	}
792 
793 	zfs_key_config_free(&config);
794 	return (PAM_SUCCESS);
795 }
796