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