]> git.proxmox.com Git - mirror_zfs.git/blob - contrib/pam_zfs_key/pam_zfs_key.c
PAM: use boolean_t for config flags
[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 (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, boolean_t noop)
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, noop, (uint8_t *)key->value,
387 WRAPPING_KEY_LEN);
388 pw_free(key);
389 if (ret && ret != EEXIST) {
390 pam_syslog(pamh, LOG_ERR, "load_key failed: %d", ret);
391 zfs_close(ds);
392 return (-1);
393 }
394 if (noop) {
395 goto out;
396 }
397 ret = zfs_mount(ds, NULL, 0);
398 if (ret) {
399 pam_syslog(pamh, LOG_ERR, "mount failed: %d", ret);
400 zfs_close(ds);
401 return (-1);
402 }
403 out:
404 zfs_close(ds);
405 return (0);
406 }
407
408 static int
409 unmount_unload(pam_handle_t *pamh, const char *ds_name)
410 {
411 zfs_handle_t *ds = zfs_open(g_zfs, ds_name, ZFS_TYPE_FILESYSTEM);
412 if (ds == NULL) {
413 pam_syslog(pamh, LOG_ERR, "dataset %s not found", ds_name);
414 return (-1);
415 }
416 int ret = zfs_unmount(ds, NULL, 0);
417 if (ret) {
418 pam_syslog(pamh, LOG_ERR, "zfs_unmount failed with: %d", ret);
419 zfs_close(ds);
420 return (-1);
421 }
422
423 ret = lzc_unload_key(ds_name);
424 if (ret) {
425 pam_syslog(pamh, LOG_ERR, "unload_key failed with: %d", ret);
426 zfs_close(ds);
427 return (-1);
428 }
429 zfs_close(ds);
430 return (0);
431 }
432
433 typedef struct {
434 char *homes_prefix;
435 char *runstatedir;
436 char *homedir;
437 char *dsname;
438 uid_t uid;
439 const char *username;
440 boolean_t unmount_and_unload;
441 } zfs_key_config_t;
442
443 static int
444 zfs_key_config_load(pam_handle_t *pamh, zfs_key_config_t *config,
445 int argc, const char **argv)
446 {
447 config->homes_prefix = strdup("rpool/home");
448 if (config->homes_prefix == NULL) {
449 pam_syslog(pamh, LOG_ERR, "strdup failure");
450 return (PAM_SERVICE_ERR);
451 }
452 config->runstatedir = strdup(RUNSTATEDIR "/pam_zfs_key");
453 if (config->runstatedir == NULL) {
454 pam_syslog(pamh, LOG_ERR, "strdup failure");
455 free(config->homes_prefix);
456 return (PAM_SERVICE_ERR);
457 }
458 const char *name;
459 if (pam_get_user(pamh, &name, NULL) != PAM_SUCCESS) {
460 pam_syslog(pamh, LOG_ERR,
461 "couldn't get username from PAM stack");
462 free(config->runstatedir);
463 free(config->homes_prefix);
464 return (PAM_SERVICE_ERR);
465 }
466 struct passwd *entry = getpwnam(name);
467 if (!entry) {
468 free(config->runstatedir);
469 free(config->homes_prefix);
470 return (PAM_USER_UNKNOWN);
471 }
472 config->uid = entry->pw_uid;
473 config->username = name;
474 config->unmount_and_unload = B_TRUE;
475 config->dsname = NULL;
476 config->homedir = NULL;
477 for (int c = 0; c < argc; c++) {
478 if (strncmp(argv[c], "homes=", 6) == 0) {
479 free(config->homes_prefix);
480 config->homes_prefix = strdup(argv[c] + 6);
481 } else if (strncmp(argv[c], "runstatedir=", 12) == 0) {
482 free(config->runstatedir);
483 config->runstatedir = strdup(argv[c] + 12);
484 } else if (strcmp(argv[c], "nounmount") == 0) {
485 config->unmount_and_unload = B_FALSE;
486 } else if (strcmp(argv[c], "prop_mountpoint") == 0) {
487 if (config->homedir == NULL)
488 config->homedir = strdup(entry->pw_dir);
489 }
490 }
491 return (PAM_SUCCESS);
492 }
493
494 static void
495 zfs_key_config_free(zfs_key_config_t *config)
496 {
497 free(config->homes_prefix);
498 free(config->runstatedir);
499 free(config->homedir);
500 free(config->dsname);
501 }
502
503 static int
504 find_dsname_by_prop_value(zfs_handle_t *zhp, void *data)
505 {
506 zfs_type_t type = zfs_get_type(zhp);
507 zfs_key_config_t *target = data;
508 char mountpoint[ZFS_MAXPROPLEN];
509
510 /* Skip any datasets whose type does not match */
511 if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
512 zfs_close(zhp);
513 return (0);
514 }
515
516 /* Skip any datasets whose mountpoint does not match */
517 (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
518 sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
519 if (strcmp(target->homedir, mountpoint) != 0) {
520 zfs_close(zhp);
521 return (0);
522 }
523
524 target->dsname = strdup(zfs_get_name(zhp));
525 zfs_close(zhp);
526 return (1);
527 }
528
529 static char *
530 zfs_key_config_get_dataset(zfs_key_config_t *config)
531 {
532 if (config->homedir != NULL &&
533 config->homes_prefix != NULL) {
534 zfs_handle_t *zhp = zfs_open(g_zfs, config->homes_prefix,
535 ZFS_TYPE_FILESYSTEM);
536 if (zhp == NULL) {
537 pam_syslog(NULL, LOG_ERR, "dataset %s not found",
538 config->homes_prefix);
539 return (NULL);
540 }
541
542 (void) zfs_iter_filesystems_v2(zhp, 0,
543 find_dsname_by_prop_value, config);
544 zfs_close(zhp);
545 char *dsname = config->dsname;
546 config->dsname = NULL;
547 return (dsname);
548 }
549
550 if (config->homes_prefix == NULL) {
551 return (NULL);
552 }
553
554 size_t len = ZFS_MAX_DATASET_NAME_LEN;
555 size_t total_len = strlen(config->homes_prefix) + 1
556 + strlen(config->username);
557 if (total_len > len) {
558 return (NULL);
559 }
560 char *ret = malloc(len + 1);
561 if (!ret) {
562 return (NULL);
563 }
564 ret[0] = 0;
565 (void) snprintf(ret, len + 1, "%s/%s", config->homes_prefix,
566 config->username);
567 return (ret);
568 }
569
570 static int
571 zfs_key_config_modify_session_counter(pam_handle_t *pamh,
572 zfs_key_config_t *config, int delta)
573 {
574 const char *runtime_path = config->runstatedir;
575 if (mkdir(runtime_path, S_IRWXU) != 0 && errno != EEXIST) {
576 pam_syslog(pamh, LOG_ERR, "Can't create runtime path: %d",
577 errno);
578 return (-1);
579 }
580 if (chown(runtime_path, 0, 0) != 0) {
581 pam_syslog(pamh, LOG_ERR, "Can't chown runtime path: %d",
582 errno);
583 return (-1);
584 }
585 if (chmod(runtime_path, S_IRWXU) != 0) {
586 pam_syslog(pamh, LOG_ERR, "Can't chmod runtime path: %d",
587 errno);
588 return (-1);
589 }
590
591 char *counter_path;
592 if (asprintf(&counter_path, "%s/%u", runtime_path, config->uid) == -1)
593 return (-1);
594
595 const int fd = open(counter_path,
596 O_RDWR | O_CLOEXEC | O_CREAT | O_NOFOLLOW,
597 S_IRUSR | S_IWUSR);
598 free(counter_path);
599 if (fd < 0) {
600 pam_syslog(pamh, LOG_ERR, "Can't open counter file: %d", errno);
601 return (-1);
602 }
603 if (flock(fd, LOCK_EX) != 0) {
604 pam_syslog(pamh, LOG_ERR, "Can't lock counter file: %d", errno);
605 close(fd);
606 return (-1);
607 }
608 char counter[20];
609 char *pos = counter;
610 int remaining = sizeof (counter) - 1;
611 int ret;
612 counter[sizeof (counter) - 1] = 0;
613 while (remaining > 0 && (ret = read(fd, pos, remaining)) > 0) {
614 remaining -= ret;
615 pos += ret;
616 }
617 *pos = 0;
618 long int counter_value = strtol(counter, NULL, 10);
619 counter_value += delta;
620 if (counter_value < 0) {
621 counter_value = 0;
622 }
623 lseek(fd, 0, SEEK_SET);
624 if (ftruncate(fd, 0) != 0) {
625 pam_syslog(pamh, LOG_ERR, "Can't truncate counter file: %d",
626 errno);
627 close(fd);
628 return (-1);
629 }
630 snprintf(counter, sizeof (counter), "%ld", counter_value);
631 remaining = strlen(counter);
632 pos = counter;
633 while (remaining > 0 && (ret = write(fd, pos, remaining)) > 0) {
634 remaining -= ret;
635 pos += ret;
636 }
637 close(fd);
638 return (counter_value);
639 }
640
641 __attribute__((visibility("default")))
642 PAM_EXTERN int
643 pam_sm_authenticate(pam_handle_t *pamh, int flags,
644 int argc, const char **argv)
645 {
646 (void) flags;
647
648 if (geteuid() != 0) {
649 pam_syslog(pamh, LOG_ERR,
650 "Cannot zfs_mount when not being root.");
651 return (PAM_SERVICE_ERR);
652 }
653 zfs_key_config_t config;
654 int config_err = zfs_key_config_load(pamh, &config, argc, argv);
655 if (config_err != PAM_SUCCESS) {
656 return (config_err);
657 }
658
659 const pw_password_t *token = pw_fetch_lazy(pamh);
660 if (token == NULL) {
661 zfs_key_config_free(&config);
662 return (PAM_AUTH_ERR);
663 }
664 if (pam_zfs_init(pamh) != 0) {
665 zfs_key_config_free(&config);
666 return (PAM_SERVICE_ERR);
667 }
668 char *dataset = zfs_key_config_get_dataset(&config);
669 if (!dataset) {
670 pam_zfs_free();
671 zfs_key_config_free(&config);
672 return (PAM_SERVICE_ERR);
673 }
674 if (decrypt_mount(pamh, dataset, token->value, B_TRUE) == -1) {
675 free(dataset);
676 pam_zfs_free();
677 zfs_key_config_free(&config);
678 return (PAM_AUTH_ERR);
679 }
680 free(dataset);
681 pam_zfs_free();
682 zfs_key_config_free(&config);
683 return (PAM_SUCCESS);
684 }
685
686 __attribute__((visibility("default")))
687 PAM_EXTERN int
688 pam_sm_setcred(pam_handle_t *pamh, int flags,
689 int argc, const char **argv)
690 {
691 (void) pamh, (void) flags, (void) argc, (void) argv;
692 return (PAM_SUCCESS);
693 }
694
695 __attribute__((visibility("default")))
696 PAM_EXTERN int
697 pam_sm_chauthtok(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_PERM_DENIED);
704 }
705 zfs_key_config_t config;
706 if (zfs_key_config_load(pamh, &config, argc, argv) != PAM_SUCCESS) {
707 return (PAM_SERVICE_ERR);
708 }
709 if (config.uid < 1000) {
710 zfs_key_config_free(&config);
711 return (PAM_SUCCESS);
712 }
713 {
714 if (pam_zfs_init(pamh) != 0) {
715 zfs_key_config_free(&config);
716 return (PAM_SERVICE_ERR);
717 }
718 char *dataset = zfs_key_config_get_dataset(&config);
719 if (!dataset) {
720 pam_zfs_free();
721 zfs_key_config_free(&config);
722 return (PAM_SERVICE_ERR);
723 }
724 int key_loaded = is_key_loaded(pamh, dataset);
725 if (key_loaded == -1) {
726 free(dataset);
727 pam_zfs_free();
728 zfs_key_config_free(&config);
729 return (PAM_SERVICE_ERR);
730 }
731 free(dataset);
732 pam_zfs_free();
733 if (! key_loaded) {
734 pam_syslog(pamh, LOG_ERR,
735 "key not loaded, returning try_again");
736 zfs_key_config_free(&config);
737 return (PAM_PERM_DENIED);
738 }
739 }
740
741 if ((flags & PAM_UPDATE_AUTHTOK) != 0) {
742 const pw_password_t *token = pw_get(pamh);
743 if (token == NULL) {
744 zfs_key_config_free(&config);
745 return (PAM_SERVICE_ERR);
746 }
747 if (pam_zfs_init(pamh) != 0) {
748 zfs_key_config_free(&config);
749 return (PAM_SERVICE_ERR);
750 }
751 char *dataset = zfs_key_config_get_dataset(&config);
752 if (!dataset) {
753 pam_zfs_free();
754 zfs_key_config_free(&config);
755 return (PAM_SERVICE_ERR);
756 }
757 if (change_key(pamh, dataset, token->value) == -1) {
758 free(dataset);
759 pam_zfs_free();
760 zfs_key_config_free(&config);
761 return (PAM_SERVICE_ERR);
762 }
763 free(dataset);
764 pam_zfs_free();
765 zfs_key_config_free(&config);
766 if (pw_clear(pamh) == -1) {
767 return (PAM_SERVICE_ERR);
768 }
769 } else {
770 zfs_key_config_free(&config);
771 }
772 return (PAM_SUCCESS);
773 }
774
775 PAM_EXTERN int
776 pam_sm_open_session(pam_handle_t *pamh, int flags,
777 int argc, const char **argv)
778 {
779 (void) flags;
780
781 if (geteuid() != 0) {
782 pam_syslog(pamh, LOG_ERR,
783 "Cannot zfs_mount when not being root.");
784 return (PAM_SUCCESS);
785 }
786 zfs_key_config_t config;
787 if (zfs_key_config_load(pamh, &config, argc, argv) != PAM_SUCCESS) {
788 return (PAM_SESSION_ERR);
789 }
790
791 if (config.uid < 1000) {
792 zfs_key_config_free(&config);
793 return (PAM_SUCCESS);
794 }
795
796 int counter = zfs_key_config_modify_session_counter(pamh, &config, 1);
797 if (counter != 1) {
798 zfs_key_config_free(&config);
799 return (PAM_SUCCESS);
800 }
801
802 const pw_password_t *token = pw_get(pamh);
803 if (token == NULL) {
804 zfs_key_config_free(&config);
805 return (PAM_SESSION_ERR);
806 }
807 if (pam_zfs_init(pamh) != 0) {
808 zfs_key_config_free(&config);
809 return (PAM_SERVICE_ERR);
810 }
811 char *dataset = zfs_key_config_get_dataset(&config);
812 if (!dataset) {
813 pam_zfs_free();
814 zfs_key_config_free(&config);
815 return (PAM_SERVICE_ERR);
816 }
817 if (decrypt_mount(pamh, dataset, token->value, B_FALSE) == -1) {
818 free(dataset);
819 pam_zfs_free();
820 zfs_key_config_free(&config);
821 return (PAM_SERVICE_ERR);
822 }
823 free(dataset);
824 pam_zfs_free();
825 zfs_key_config_free(&config);
826 if (pw_clear(pamh) == -1) {
827 return (PAM_SERVICE_ERR);
828 }
829 return (PAM_SUCCESS);
830
831 }
832
833 __attribute__((visibility("default")))
834 PAM_EXTERN int
835 pam_sm_close_session(pam_handle_t *pamh, int flags,
836 int argc, const char **argv)
837 {
838 (void) flags;
839
840 if (geteuid() != 0) {
841 pam_syslog(pamh, LOG_ERR,
842 "Cannot zfs_mount when not being root.");
843 return (PAM_SUCCESS);
844 }
845 zfs_key_config_t config;
846 if (zfs_key_config_load(pamh, &config, argc, argv) != PAM_SUCCESS) {
847 return (PAM_SESSION_ERR);
848 }
849 if (config.uid < 1000) {
850 zfs_key_config_free(&config);
851 return (PAM_SUCCESS);
852 }
853
854 int counter = zfs_key_config_modify_session_counter(pamh, &config, -1);
855 if (counter != 0) {
856 zfs_key_config_free(&config);
857 return (PAM_SUCCESS);
858 }
859
860 if (config.unmount_and_unload) {
861 if (pam_zfs_init(pamh) != 0) {
862 zfs_key_config_free(&config);
863 return (PAM_SERVICE_ERR);
864 }
865 char *dataset = zfs_key_config_get_dataset(&config);
866 if (!dataset) {
867 pam_zfs_free();
868 zfs_key_config_free(&config);
869 return (PAM_SESSION_ERR);
870 }
871 if (unmount_unload(pamh, dataset) == -1) {
872 free(dataset);
873 pam_zfs_free();
874 zfs_key_config_free(&config);
875 return (PAM_SESSION_ERR);
876 }
877 free(dataset);
878 pam_zfs_free();
879 }
880
881 zfs_key_config_free(&config);
882 return (PAM_SUCCESS);
883 }