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