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