]> git.proxmox.com Git - mirror_zfs.git/blame - contrib/pam_zfs_key/pam_zfs_key.c
PAM: add 'recursive_homes' flag to use with 'prop_mountpoint'
[mirror_zfs.git] / contrib / pam_zfs_key / pam_zfs_key.c
CommitLineData
221e6704
FD
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>
7cc5cb80 44#define MAP_FLAGS MAP_PRIVATE | MAP_ANONYMOUS
221e6704
FD
45#elif defined(__FreeBSD__)
46#include <security/pam_appl.h>
47static void
48pam_syslog(pam_handle_t *pamh, int loglevel, const char *fmt, ...)
49{
46c7a802 50 (void) pamh;
221e6704
FD
51 va_list args;
52 va_start(args, fmt);
53 vsyslog(loglevel, fmt, args);
54 va_end(args);
55}
7cc5cb80 56#define MAP_FLAGS MAP_PRIVATE | MAP_ANON | MAP_NOCORE
221e6704
FD
57#endif
58
59#include <string.h>
50292e25 60#include <unistd.h>
221e6704
FD
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
69static const char PASSWORD_VAR_NAME[] = "pam_zfs_key_authtok";
70
71static libzfs_handle_t *g_zfs;
72
73static void destroy_pw(pam_handle_t *pamh, void *data, int errcode);
74
50292e25
AF
75typedef int (*mlock_func_t) (const void *, size_t);
76
221e6704
FD
77typedef struct {
78 size_t len;
79 char *value;
80} pw_password_t;
81
50292e25 82/*
7cc5cb80
AF
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).
50292e25
AF
86 */
87static int
88try_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
221e6704
FD
107static pw_password_t *
108alloc_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;
1b610ae4 115 /*
7cc5cb80
AF
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.
1b610ae4 123 */
7cc5cb80
AF
124 pw->value = mmap(NULL, pw->len, PROT_READ | PROT_WRITE, MAP_FLAGS,
125 -1, 0);
126
127 if (pw->value == MAP_FAILED) {
221e6704
FD
128 free(pw);
129 return (NULL);
130 }
50292e25 131 if (try_lock(mlock, pw->value, pw->len) != 0) {
7cc5cb80 132 (void) munmap(pw->value, pw->len);
50292e25 133 free(pw);
7cc5cb80 134 return (NULL);
50292e25 135 }
221e6704
FD
136 return (pw);
137}
138
139static pw_password_t *
140alloc_pw_string(const char *source)
141{
7cc5cb80
AF
142 size_t len = strlen(source) + 1;
143 pw_password_t *pw = alloc_pw_size(len);
144
221e6704
FD
145 if (!pw) {
146 return (NULL);
147 }
221e6704
FD
148 memcpy(pw->value, source, pw->len);
149 return (pw);
150}
151
152static void
153pw_free(pw_password_t *pw)
154{
861166b0 155 memset(pw->value, 0, pw->len);
50292e25 156 if (try_lock(munlock, pw->value, pw->len) == 0) {
7cc5cb80 157 (void) munmap(pw->value, pw->len);
50292e25 158 }
221e6704
FD
159 free(pw);
160}
161
162static pw_password_t *
163pw_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
179static const pw_password_t *
180pw_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
195static const pw_password_t *
196pw_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
209static int
210pw_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
220static void
221destroy_pw(pam_handle_t *pamh, void *data, int errcode)
222{
1f63c645
AZ
223 (void) pamh, (void) errcode;
224
221e6704
FD
225 if (data != NULL) {
226 pw_free((pw_password_t *)data);
227 }
228}
229
230static int
231pam_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
242static void
243pam_zfs_free(void)
244{
245 libzfs_fini(g_zfs);
246}
247
248static pw_password_t *
249prepare_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
311static int
312is_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
324static int
325change_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
372static int
373decrypt_mount(pam_handle_t *pamh, const char *ds_name,
ae0d0f0e 374 const char *passphrase, boolean_t noop)
221e6704
FD
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 }
ae0d0f0e 386 int ret = lzc_load_key(ds_name, noop, (uint8_t *)key->value,
221e6704
FD
387 WRAPPING_KEY_LEN);
388 pw_free(key);
c47b7086 389 if (ret && ret != EEXIST) {
221e6704
FD
390 pam_syslog(pamh, LOG_ERR, "load_key failed: %d", ret);
391 zfs_close(ds);
392 return (-1);
393 }
ae0d0f0e
VP
394 if (noop) {
395 goto out;
396 }
221e6704
FD
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 }
ae0d0f0e 403out:
221e6704
FD
404 zfs_close(ds);
405 return (0);
406}
407
408static int
409unmount_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
433typedef struct {
434 char *homes_prefix;
435 char *runstatedir;
dc6d39a8
CW
436 char *homedir;
437 char *dsname;
221e6704
FD
438 uid_t uid;
439 const char *username;
bd4962b5 440 boolean_t unmount_and_unload;
850bccd3 441 boolean_t recursive_homes;
221e6704
FD
442} zfs_key_config_t;
443
444static int
445zfs_key_config_load(pam_handle_t *pamh, zfs_key_config_t *config,
446 int argc, const char **argv)
447{
448 config->homes_prefix = strdup("rpool/home");
449 if (config->homes_prefix == NULL) {
450 pam_syslog(pamh, LOG_ERR, "strdup failure");
ae0d0f0e 451 return (PAM_SERVICE_ERR);
221e6704
FD
452 }
453 config->runstatedir = strdup(RUNSTATEDIR "/pam_zfs_key");
454 if (config->runstatedir == NULL) {
455 pam_syslog(pamh, LOG_ERR, "strdup failure");
456 free(config->homes_prefix);
ae0d0f0e 457 return (PAM_SERVICE_ERR);
221e6704
FD
458 }
459 const char *name;
460 if (pam_get_user(pamh, &name, NULL) != PAM_SUCCESS) {
461 pam_syslog(pamh, LOG_ERR,
462 "couldn't get username from PAM stack");
463 free(config->runstatedir);
464 free(config->homes_prefix);
ae0d0f0e 465 return (PAM_SERVICE_ERR);
221e6704
FD
466 }
467 struct passwd *entry = getpwnam(name);
468 if (!entry) {
469 free(config->runstatedir);
470 free(config->homes_prefix);
ae0d0f0e 471 return (PAM_USER_UNKNOWN);
221e6704
FD
472 }
473 config->uid = entry->pw_uid;
474 config->username = name;
bd4962b5 475 config->unmount_and_unload = B_TRUE;
850bccd3 476 config->recursive_homes = B_FALSE;
dc6d39a8
CW
477 config->dsname = NULL;
478 config->homedir = NULL;
221e6704
FD
479 for (int c = 0; c < argc; c++) {
480 if (strncmp(argv[c], "homes=", 6) == 0) {
481 free(config->homes_prefix);
482 config->homes_prefix = strdup(argv[c] + 6);
483 } else if (strncmp(argv[c], "runstatedir=", 12) == 0) {
484 free(config->runstatedir);
485 config->runstatedir = strdup(argv[c] + 12);
486 } else if (strcmp(argv[c], "nounmount") == 0) {
bd4962b5 487 config->unmount_and_unload = B_FALSE;
850bccd3
VP
488 } else if (strcmp(argv[c], "recursive_homes") == 0) {
489 config->recursive_homes = B_TRUE;
dc6d39a8 490 } else if (strcmp(argv[c], "prop_mountpoint") == 0) {
f7bda2de
RY
491 if (config->homedir == NULL)
492 config->homedir = strdup(entry->pw_dir);
221e6704
FD
493 }
494 }
ae0d0f0e 495 return (PAM_SUCCESS);
221e6704
FD
496}
497
498static void
499zfs_key_config_free(zfs_key_config_t *config)
500{
501 free(config->homes_prefix);
dc6d39a8
CW
502 free(config->runstatedir);
503 free(config->homedir);
504 free(config->dsname);
505}
506
507static int
508find_dsname_by_prop_value(zfs_handle_t *zhp, void *data)
509{
510 zfs_type_t type = zfs_get_type(zhp);
511 zfs_key_config_t *target = data;
512 char mountpoint[ZFS_MAXPROPLEN];
513
514 /* Skip any datasets whose type does not match */
515 if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
516 zfs_close(zhp);
517 return (0);
518 }
519
520 /* Skip any datasets whose mountpoint does not match */
521 (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
522 sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
523 if (strcmp(target->homedir, mountpoint) != 0) {
850bccd3
VP
524 if (target->recursive_homes) {
525 (void) zfs_iter_filesystems_v2(zhp, 0,
526 find_dsname_by_prop_value, target);
527 }
dc6d39a8 528 zfs_close(zhp);
850bccd3 529 return (target->dsname != NULL);
dc6d39a8
CW
530 }
531
532 target->dsname = strdup(zfs_get_name(zhp));
533 zfs_close(zhp);
534 return (1);
221e6704
FD
535}
536
537static char *
538zfs_key_config_get_dataset(zfs_key_config_t *config)
539{
dc6d39a8
CW
540 if (config->homedir != NULL &&
541 config->homes_prefix != NULL) {
850bccd3
VP
542 if (strcmp(config->homes_prefix, "*") == 0) {
543 (void) zfs_iter_root(g_zfs,
544 find_dsname_by_prop_value, config);
545 } else {
546 zfs_handle_t *zhp = zfs_open(g_zfs,
547 config->homes_prefix, ZFS_TYPE_FILESYSTEM);
548 if (zhp == NULL) {
549 pam_syslog(NULL, LOG_ERR,
550 "dataset %s not found",
551 config->homes_prefix);
552 return (NULL);
553 }
dc6d39a8 554
850bccd3
VP
555 (void) zfs_iter_filesystems_v2(zhp, 0,
556 find_dsname_by_prop_value, config);
557 zfs_close(zhp);
558 }
dc6d39a8
CW
559 char *dsname = config->dsname;
560 config->dsname = NULL;
561 return (dsname);
562 }
563
4df8ccc8
RY
564 if (config->homes_prefix == NULL) {
565 return (NULL);
566 }
567
221e6704
FD
568 size_t len = ZFS_MAX_DATASET_NAME_LEN;
569 size_t total_len = strlen(config->homes_prefix) + 1
570 + strlen(config->username);
571 if (total_len > len) {
572 return (NULL);
573 }
574 char *ret = malloc(len + 1);
575 if (!ret) {
576 return (NULL);
577 }
578 ret[0] = 0;
a51288aa
RY
579 (void) snprintf(ret, len + 1, "%s/%s", config->homes_prefix,
580 config->username);
221e6704
FD
581 return (ret);
582}
583
584static int
585zfs_key_config_modify_session_counter(pam_handle_t *pamh,
586 zfs_key_config_t *config, int delta)
587{
588 const char *runtime_path = config->runstatedir;
589 if (mkdir(runtime_path, S_IRWXU) != 0 && errno != EEXIST) {
590 pam_syslog(pamh, LOG_ERR, "Can't create runtime path: %d",
591 errno);
592 return (-1);
593 }
594 if (chown(runtime_path, 0, 0) != 0) {
595 pam_syslog(pamh, LOG_ERR, "Can't chown runtime path: %d",
596 errno);
597 return (-1);
598 }
599 if (chmod(runtime_path, S_IRWXU) != 0) {
600 pam_syslog(pamh, LOG_ERR, "Can't chmod runtime path: %d",
601 errno);
602 return (-1);
603 }
d3db900a
TH
604
605 char *counter_path;
606 if (asprintf(&counter_path, "%s/%u", runtime_path, config->uid) == -1)
221e6704 607 return (-1);
d3db900a 608
221e6704
FD
609 const int fd = open(counter_path,
610 O_RDWR | O_CLOEXEC | O_CREAT | O_NOFOLLOW,
611 S_IRUSR | S_IWUSR);
612 free(counter_path);
613 if (fd < 0) {
614 pam_syslog(pamh, LOG_ERR, "Can't open counter file: %d", errno);
615 return (-1);
616 }
617 if (flock(fd, LOCK_EX) != 0) {
618 pam_syslog(pamh, LOG_ERR, "Can't lock counter file: %d", errno);
619 close(fd);
620 return (-1);
621 }
622 char counter[20];
623 char *pos = counter;
624 int remaining = sizeof (counter) - 1;
625 int ret;
626 counter[sizeof (counter) - 1] = 0;
627 while (remaining > 0 && (ret = read(fd, pos, remaining)) > 0) {
628 remaining -= ret;
629 pos += ret;
630 }
631 *pos = 0;
632 long int counter_value = strtol(counter, NULL, 10);
633 counter_value += delta;
634 if (counter_value < 0) {
635 counter_value = 0;
636 }
637 lseek(fd, 0, SEEK_SET);
638 if (ftruncate(fd, 0) != 0) {
639 pam_syslog(pamh, LOG_ERR, "Can't truncate counter file: %d",
640 errno);
641 close(fd);
642 return (-1);
643 }
644 snprintf(counter, sizeof (counter), "%ld", counter_value);
645 remaining = strlen(counter);
646 pos = counter;
647 while (remaining > 0 && (ret = write(fd, pos, remaining)) > 0) {
648 remaining -= ret;
649 pos += ret;
650 }
651 close(fd);
652 return (counter_value);
653}
654
655__attribute__((visibility("default")))
656PAM_EXTERN int
657pam_sm_authenticate(pam_handle_t *pamh, int flags,
658 int argc, const char **argv)
659{
ae0d0f0e 660 (void) flags;
1f63c645 661
ae0d0f0e
VP
662 if (geteuid() != 0) {
663 pam_syslog(pamh, LOG_ERR,
664 "Cannot zfs_mount when not being root.");
665 return (PAM_SERVICE_ERR);
666 }
667 zfs_key_config_t config;
668 int config_err = zfs_key_config_load(pamh, &config, argc, argv);
669 if (config_err != PAM_SUCCESS) {
670 return (config_err);
221e6704
FD
671 }
672
ae0d0f0e
VP
673 const pw_password_t *token = pw_fetch_lazy(pamh);
674 if (token == NULL) {
675 zfs_key_config_free(&config);
676 return (PAM_AUTH_ERR);
677 }
678 if (pam_zfs_init(pamh) != 0) {
679 zfs_key_config_free(&config);
680 return (PAM_SERVICE_ERR);
681 }
682 char *dataset = zfs_key_config_get_dataset(&config);
683 if (!dataset) {
684 pam_zfs_free();
685 zfs_key_config_free(&config);
686 return (PAM_SERVICE_ERR);
687 }
688 if (decrypt_mount(pamh, dataset, token->value, B_TRUE) == -1) {
689 free(dataset);
690 pam_zfs_free();
691 zfs_key_config_free(&config);
692 return (PAM_AUTH_ERR);
693 }
694 free(dataset);
695 pam_zfs_free();
696 zfs_key_config_free(&config);
221e6704
FD
697 return (PAM_SUCCESS);
698}
699
700__attribute__((visibility("default")))
701PAM_EXTERN int
702pam_sm_setcred(pam_handle_t *pamh, int flags,
703 int argc, const char **argv)
704{
1f63c645 705 (void) pamh, (void) flags, (void) argc, (void) argv;
221e6704
FD
706 return (PAM_SUCCESS);
707}
708
709__attribute__((visibility("default")))
710PAM_EXTERN int
711pam_sm_chauthtok(pam_handle_t *pamh, int flags,
712 int argc, const char **argv)
713{
714 if (geteuid() != 0) {
715 pam_syslog(pamh, LOG_ERR,
716 "Cannot zfs_mount when not being root.");
717 return (PAM_PERM_DENIED);
718 }
719 zfs_key_config_t config;
ae0d0f0e 720 if (zfs_key_config_load(pamh, &config, argc, argv) != PAM_SUCCESS) {
221e6704
FD
721 return (PAM_SERVICE_ERR);
722 }
723 if (config.uid < 1000) {
724 zfs_key_config_free(&config);
725 return (PAM_SUCCESS);
726 }
727 {
728 if (pam_zfs_init(pamh) != 0) {
729 zfs_key_config_free(&config);
730 return (PAM_SERVICE_ERR);
731 }
732 char *dataset = zfs_key_config_get_dataset(&config);
733 if (!dataset) {
734 pam_zfs_free();
735 zfs_key_config_free(&config);
736 return (PAM_SERVICE_ERR);
737 }
738 int key_loaded = is_key_loaded(pamh, dataset);
739 if (key_loaded == -1) {
740 free(dataset);
741 pam_zfs_free();
742 zfs_key_config_free(&config);
743 return (PAM_SERVICE_ERR);
744 }
745 free(dataset);
746 pam_zfs_free();
747 if (! key_loaded) {
748 pam_syslog(pamh, LOG_ERR,
749 "key not loaded, returning try_again");
750 zfs_key_config_free(&config);
751 return (PAM_PERM_DENIED);
752 }
753 }
754
755 if ((flags & PAM_UPDATE_AUTHTOK) != 0) {
756 const pw_password_t *token = pw_get(pamh);
757 if (token == NULL) {
758 zfs_key_config_free(&config);
759 return (PAM_SERVICE_ERR);
760 }
761 if (pam_zfs_init(pamh) != 0) {
762 zfs_key_config_free(&config);
763 return (PAM_SERVICE_ERR);
764 }
765 char *dataset = zfs_key_config_get_dataset(&config);
766 if (!dataset) {
767 pam_zfs_free();
768 zfs_key_config_free(&config);
769 return (PAM_SERVICE_ERR);
770 }
771 if (change_key(pamh, dataset, token->value) == -1) {
772 free(dataset);
773 pam_zfs_free();
774 zfs_key_config_free(&config);
775 return (PAM_SERVICE_ERR);
776 }
777 free(dataset);
778 pam_zfs_free();
779 zfs_key_config_free(&config);
780 if (pw_clear(pamh) == -1) {
781 return (PAM_SERVICE_ERR);
782 }
783 } else {
784 zfs_key_config_free(&config);
785 }
786 return (PAM_SUCCESS);
787}
788
789PAM_EXTERN int
790pam_sm_open_session(pam_handle_t *pamh, int flags,
791 int argc, const char **argv)
792{
1f63c645
AZ
793 (void) flags;
794
221e6704
FD
795 if (geteuid() != 0) {
796 pam_syslog(pamh, LOG_ERR,
797 "Cannot zfs_mount when not being root.");
798 return (PAM_SUCCESS);
799 }
800 zfs_key_config_t config;
ae0d0f0e 801 if (zfs_key_config_load(pamh, &config, argc, argv) != PAM_SUCCESS) {
2ba240f3
RY
802 return (PAM_SESSION_ERR);
803 }
804
221e6704
FD
805 if (config.uid < 1000) {
806 zfs_key_config_free(&config);
807 return (PAM_SUCCESS);
808 }
809
810 int counter = zfs_key_config_modify_session_counter(pamh, &config, 1);
811 if (counter != 1) {
812 zfs_key_config_free(&config);
813 return (PAM_SUCCESS);
814 }
815
816 const pw_password_t *token = pw_get(pamh);
817 if (token == NULL) {
818 zfs_key_config_free(&config);
819 return (PAM_SESSION_ERR);
820 }
821 if (pam_zfs_init(pamh) != 0) {
822 zfs_key_config_free(&config);
823 return (PAM_SERVICE_ERR);
824 }
825 char *dataset = zfs_key_config_get_dataset(&config);
826 if (!dataset) {
827 pam_zfs_free();
828 zfs_key_config_free(&config);
829 return (PAM_SERVICE_ERR);
830 }
ae0d0f0e 831 if (decrypt_mount(pamh, dataset, token->value, B_FALSE) == -1) {
221e6704
FD
832 free(dataset);
833 pam_zfs_free();
834 zfs_key_config_free(&config);
835 return (PAM_SERVICE_ERR);
836 }
837 free(dataset);
838 pam_zfs_free();
839 zfs_key_config_free(&config);
840 if (pw_clear(pamh) == -1) {
841 return (PAM_SERVICE_ERR);
842 }
843 return (PAM_SUCCESS);
844
845}
846
847__attribute__((visibility("default")))
848PAM_EXTERN int
849pam_sm_close_session(pam_handle_t *pamh, int flags,
850 int argc, const char **argv)
851{
1f63c645
AZ
852 (void) flags;
853
221e6704
FD
854 if (geteuid() != 0) {
855 pam_syslog(pamh, LOG_ERR,
856 "Cannot zfs_mount when not being root.");
857 return (PAM_SUCCESS);
858 }
859 zfs_key_config_t config;
ae0d0f0e 860 if (zfs_key_config_load(pamh, &config, argc, argv) != PAM_SUCCESS) {
9a49c6b7
RY
861 return (PAM_SESSION_ERR);
862 }
221e6704
FD
863 if (config.uid < 1000) {
864 zfs_key_config_free(&config);
865 return (PAM_SUCCESS);
866 }
867
868 int counter = zfs_key_config_modify_session_counter(pamh, &config, -1);
869 if (counter != 0) {
870 zfs_key_config_free(&config);
871 return (PAM_SUCCESS);
872 }
873
874 if (config.unmount_and_unload) {
875 if (pam_zfs_init(pamh) != 0) {
876 zfs_key_config_free(&config);
877 return (PAM_SERVICE_ERR);
878 }
879 char *dataset = zfs_key_config_get_dataset(&config);
880 if (!dataset) {
881 pam_zfs_free();
882 zfs_key_config_free(&config);
883 return (PAM_SESSION_ERR);
884 }
885 if (unmount_unload(pamh, dataset) == -1) {
886 free(dataset);
887 pam_zfs_free();
888 zfs_key_config_free(&config);
889 return (PAM_SESSION_ERR);
890 }
891 free(dataset);
892 pam_zfs_free();
893 }
894
895 zfs_key_config_free(&config);
896 return (PAM_SUCCESS);
897}