]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/user-record-util.c
homed: support recovery keys
[thirdparty/systemd.git] / src / home / user-record-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "errno-util.h"
4 #include "home-util.h"
5 #include "id128-util.h"
6 #include "libcrypt-util.h"
7 #include "memory-util.h"
8 #include "modhex.h"
9 #include "mountpoint-util.h"
10 #include "path-util.h"
11 #include "stat-util.h"
12 #include "user-record-util.h"
13 #include "user-util.h"
14
15 int user_record_synthesize(
16 UserRecord *h,
17 const char *user_name,
18 const char *realm,
19 const char *image_path,
20 UserStorage storage,
21 uid_t uid,
22 gid_t gid) {
23
24 _cleanup_free_ char *hd = NULL, *un = NULL, *ip = NULL, *rr = NULL, *user_name_and_realm = NULL;
25 char smid[SD_ID128_STRING_MAX];
26 sd_id128_t mid;
27 int r;
28
29 assert(h);
30 assert(user_name);
31 assert(image_path);
32 assert(IN_SET(storage, USER_LUKS, USER_SUBVOLUME, USER_FSCRYPT, USER_DIRECTORY));
33 assert(uid_is_valid(uid));
34 assert(gid_is_valid(gid));
35
36 /* Fill in a home record from just a username and an image path. */
37
38 if (h->json)
39 return -EBUSY;
40
41 if (!suitable_user_name(user_name))
42 return -EINVAL;
43
44 if (realm) {
45 r = suitable_realm(realm);
46 if (r < 0)
47 return r;
48 if (r == 0)
49 return -EINVAL;
50 }
51
52 if (!suitable_image_path(image_path))
53 return -EINVAL;
54
55 r = sd_id128_get_machine(&mid);
56 if (r < 0)
57 return r;
58
59 un = strdup(user_name);
60 if (!un)
61 return -ENOMEM;
62
63 if (realm) {
64 rr = strdup(realm);
65 if (!rr)
66 return -ENOMEM;
67
68 user_name_and_realm = strjoin(user_name, "@", realm);
69 if (!user_name_and_realm)
70 return -ENOMEM;
71 }
72
73 ip = strdup(image_path);
74 if (!ip)
75 return -ENOMEM;
76
77 hd = path_join("/home/", user_name);
78 if (!hd)
79 return -ENOMEM;
80
81 r = json_build(&h->json,
82 JSON_BUILD_OBJECT(
83 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
84 JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(realm)),
85 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
86 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
87 JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
88 JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path)),
89 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd)),
90 JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage))),
91 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
92 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid))))))));
93 if (r < 0)
94 return r;
95
96 free_and_replace(h->user_name, un);
97 free_and_replace(h->realm, rr);
98 free_and_replace(h->user_name_and_realm_auto, user_name_and_realm);
99 free_and_replace(h->image_path, ip);
100 free_and_replace(h->home_directory, hd);
101 h->storage = storage;
102 h->uid = uid;
103
104 h->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
105 return 0;
106 }
107
108 int group_record_synthesize(GroupRecord *g, UserRecord *h) {
109 _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL, *description = NULL;
110 char smid[SD_ID128_STRING_MAX];
111 sd_id128_t mid;
112 int r;
113
114 assert(g);
115 assert(h);
116
117 if (g->json)
118 return -EBUSY;
119
120 r = sd_id128_get_machine(&mid);
121 if (r < 0)
122 return r;
123
124 un = strdup(h->user_name);
125 if (!un)
126 return -ENOMEM;
127
128 if (h->realm) {
129 rr = strdup(h->realm);
130 if (!rr)
131 return -ENOMEM;
132
133 group_name_and_realm = strjoin(un, "@", rr);
134 if (!group_name_and_realm)
135 return -ENOMEM;
136 }
137
138 description = strjoin("Primary Group of User ", un);
139 if (!description)
140 return -ENOMEM;
141
142 r = json_build(&g->json,
143 JSON_BUILD_OBJECT(
144 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(un)),
145 JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(rr)),
146 JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
147 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
148 JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
149 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h))))))),
150 JSON_BUILD_PAIR_CONDITION(h->disposition >= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h)))),
151 JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
152 JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
153 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
154 if (r < 0)
155 return r;
156
157 free_and_replace(g->group_name, un);
158 free_and_replace(g->realm, rr);
159 free_and_replace(g->group_name_and_realm_auto, group_name_and_realm);
160 g->gid = user_record_gid(h);
161 g->disposition = h->disposition;
162
163 g->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
164 return 0;
165 }
166
167 int user_record_reconcile(
168 UserRecord *host,
169 UserRecord *embedded,
170 UserReconcileMode mode,
171 UserRecord **ret) {
172
173 int r, result;
174
175 /* Reconciles the identity record stored on the host with the one embedded in a $HOME
176 * directory. Returns the following error codes:
177 *
178 * -EINVAL: one of the records not valid
179 * -REMCHG: identity records are not about the same user
180 * -ESTALE: embedded identity record is equally new or newer than supplied record
181 *
182 * Return the new record to use, which is either the embedded record updated with the host
183 * binding or the host record. In both cases the secret data is stripped. */
184
185 assert(host);
186 assert(embedded);
187
188 /* Make sure both records are initialized */
189 if (!host->json || !embedded->json)
190 return -EINVAL;
191
192 /* Ensure these records actually contain user data */
193 if (!(embedded->mask & host->mask & USER_RECORD_REGULAR))
194 return -EINVAL;
195
196 /* Make sure the user name and realm matches */
197 if (!user_record_compatible(host, embedded))
198 return -EREMCHG;
199
200 /* Embedded identities may not contain secrets or binding info*/
201 if ((embedded->mask & (USER_RECORD_SECRET|USER_RECORD_BINDING)) != 0)
202 return -EINVAL;
203
204 /* The embedded record checked out, let's now figure out which of the two identities we'll consider
205 * in effect from now on. We do this by checking the last change timestamp, and in doubt always let
206 * the embedded data win. */
207 if (host->last_change_usec != UINT64_MAX &&
208 (embedded->last_change_usec == UINT64_MAX || host->last_change_usec > embedded->last_change_usec))
209
210 /* The host version is definitely newer, either because it has a version at all and the
211 * embedded version doesn't or because it is numerically newer. */
212 result = USER_RECONCILE_HOST_WON;
213
214 else if (host->last_change_usec == embedded->last_change_usec) {
215
216 /* The nominal version number of the host and the embedded identity is the same. If so, let's
217 * verify that, and tell the caller if we are ignoring embedded data. */
218
219 r = user_record_masked_equal(host, embedded, USER_RECORD_REGULAR|USER_RECORD_PRIVILEGED|USER_RECORD_PER_MACHINE);
220 if (r < 0)
221 return r;
222 if (r > 0) {
223 if (mode == USER_RECONCILE_REQUIRE_NEWER)
224 return -ESTALE;
225
226 result = USER_RECONCILE_IDENTICAL;
227 } else
228 result = USER_RECONCILE_HOST_WON;
229 } else {
230 _cleanup_(json_variant_unrefp) JsonVariant *extended = NULL;
231 _cleanup_(user_record_unrefp) UserRecord *merged = NULL;
232 JsonVariant *e;
233
234 /* The embedded version is newer */
235
236 if (mode == USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL)
237 return -ESTALE;
238
239 /* Copy in the binding data */
240 extended = json_variant_ref(embedded->json);
241
242 e = json_variant_by_key(host->json, "binding");
243 if (e) {
244 r = json_variant_set_field(&extended, "binding", e);
245 if (r < 0)
246 return r;
247 }
248
249 merged = user_record_new();
250 if (!merged)
251 return -ENOMEM;
252
253 r = user_record_load(merged, extended, USER_RECORD_LOAD_MASK_SECRET);
254 if (r < 0)
255 return r;
256
257 *ret = TAKE_PTR(merged);
258 return USER_RECONCILE_EMBEDDED_WON; /* update */
259 }
260
261 /* Strip out secrets */
262 r = user_record_clone(host, USER_RECORD_LOAD_MASK_SECRET, ret);
263 if (r < 0)
264 return r;
265
266 return result;
267 }
268
269 int user_record_add_binding(
270 UserRecord *h,
271 UserStorage storage,
272 const char *image_path,
273 sd_id128_t partition_uuid,
274 sd_id128_t luks_uuid,
275 sd_id128_t fs_uuid,
276 const char *luks_cipher,
277 const char *luks_cipher_mode,
278 uint64_t luks_volume_key_size,
279 const char *file_system_type,
280 const char *home_directory,
281 uid_t uid,
282 gid_t gid) {
283
284 _cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
285 char smid[SD_ID128_STRING_MAX], partition_uuids[ID128_UUID_STRING_MAX], luks_uuids[ID128_UUID_STRING_MAX], fs_uuids[ID128_UUID_STRING_MAX];
286 _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
287 sd_id128_t mid;
288 int r;
289
290 assert(h);
291
292 if (!h->json)
293 return -EUNATCH;
294
295 r = sd_id128_get_machine(&mid);
296 if (r < 0)
297 return r;
298 sd_id128_to_string(mid, smid);
299
300 if (image_path) {
301 ip = strdup(image_path);
302 if (!ip)
303 return -ENOMEM;
304 } else if (!h->image_path && storage >= 0) {
305 r = user_record_build_image_path(storage, user_record_user_name_and_realm(h), &ip_auto);
306 if (r < 0)
307 return r;
308 }
309
310 if (home_directory) {
311 hd = strdup(home_directory);
312 if (!hd)
313 return -ENOMEM;
314 }
315
316 if (file_system_type) {
317 fst = strdup(file_system_type);
318 if (!fst)
319 return -ENOMEM;
320 }
321
322 if (luks_cipher) {
323 lc = strdup(luks_cipher);
324 if (!lc)
325 return -ENOMEM;
326 }
327
328 if (luks_cipher_mode) {
329 lcm = strdup(luks_cipher_mode);
330 if (!lcm)
331 return -ENOMEM;
332 }
333
334 r = json_build(&new_binding_entry,
335 JSON_BUILD_OBJECT(
336 JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
337 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid, partition_uuids))),
338 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid, luks_uuids))),
339 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid, fs_uuids))),
340 JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
341 JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
342 JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
343 JSON_BUILD_PAIR_CONDITION(!!file_system_type, "fileSystemType", JSON_BUILD_STRING(file_system_type)),
344 JSON_BUILD_PAIR_CONDITION(!!home_directory, "homeDirectory", JSON_BUILD_STRING(home_directory)),
345 JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid), "uid", JSON_BUILD_UNSIGNED(uid)),
346 JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid), "gid", JSON_BUILD_UNSIGNED(gid)),
347 JSON_BUILD_PAIR_CONDITION(storage >= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage)))));
348 if (r < 0)
349 return r;
350
351 binding = json_variant_ref(json_variant_by_key(h->json, "binding"));
352 if (binding) {
353 _cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
354
355 /* Merge the new entry with an old one, if that exists */
356 be = json_variant_ref(json_variant_by_key(binding, smid));
357 if (be) {
358 r = json_variant_merge(&be, new_binding_entry);
359 if (r < 0)
360 return r;
361
362 json_variant_unref(new_binding_entry);
363 new_binding_entry = TAKE_PTR(be);
364 }
365 }
366
367 r = json_variant_set_field(&binding, smid, new_binding_entry);
368 if (r < 0)
369 return r;
370
371 r = json_variant_set_field(&h->json, "binding", binding);
372 if (r < 0)
373 return r;
374
375 if (storage >= 0)
376 h->storage = storage;
377
378 if (ip)
379 free_and_replace(h->image_path, ip);
380 if (ip_auto)
381 free_and_replace(h->image_path_auto, ip_auto);
382
383 if (!sd_id128_is_null(partition_uuid))
384 h->partition_uuid = partition_uuid;
385
386 if (!sd_id128_is_null(luks_uuid))
387 h->luks_uuid = luks_uuid;
388
389 if (!sd_id128_is_null(fs_uuid))
390 h->file_system_uuid = fs_uuid;
391
392 if (lc)
393 free_and_replace(h->luks_cipher, lc);
394 if (lcm)
395 free_and_replace(h->luks_cipher_mode, lcm);
396 if (luks_volume_key_size != UINT64_MAX)
397 h->luks_volume_key_size = luks_volume_key_size;
398
399 if (fst)
400 free_and_replace(h->file_system_type, fst);
401 if (hd)
402 free_and_replace(h->home_directory, hd);
403
404 if (uid_is_valid(uid))
405 h->uid = uid;
406 if (gid_is_valid(gid))
407 h->gid = gid;
408
409 h->mask |= USER_RECORD_BINDING;
410 return 1;
411 }
412
413 int user_record_test_home_directory(UserRecord *h) {
414 const char *hd;
415 int r;
416
417 assert(h);
418
419 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
420
421 hd = user_record_home_directory(h);
422 if (!hd)
423 return -ENXIO;
424
425 r = is_dir(hd, false);
426 if (r == -ENOENT)
427 return USER_TEST_ABSENT;
428 if (r < 0)
429 return r;
430 if (r == 0)
431 return -ENOTDIR;
432
433 r = path_is_mount_point(hd, NULL, 0);
434 if (r < 0)
435 return r;
436 if (r > 0)
437 return USER_TEST_MOUNTED;
438
439 /* If the image path and the home directory are identical, then it's OK if the directory is
440 * populated. */
441 if (IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) {
442 const char *ip;
443
444 ip = user_record_image_path(h);
445 if (ip && path_equal(ip, hd))
446 return USER_TEST_EXISTS;
447 }
448
449 /* Otherwise it's not OK */
450 r = dir_is_empty(hd);
451 if (r < 0)
452 return r;
453 if (r == 0)
454 return -EBUSY;
455
456 return USER_TEST_EXISTS;
457 }
458
459 int user_record_test_home_directory_and_warn(UserRecord *h) {
460 int r;
461
462 assert(h);
463
464 r = user_record_test_home_directory(h);
465 if (r == -ENXIO)
466 return log_error_errno(r, "User record lacks home directory, refusing.");
467 if (r == -ENOTDIR)
468 return log_error_errno(r, "Home directory %s is not a directory, refusing.", user_record_home_directory(h));
469 if (r == -EBUSY)
470 return log_error_errno(r, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h));
471 if (r < 0)
472 return log_error_errno(r, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h));
473
474 return r;
475 }
476
477 int user_record_test_image_path(UserRecord *h) {
478 const char *ip;
479 struct stat st;
480
481 assert(h);
482
483 if (user_record_storage(h) == USER_CIFS)
484 return USER_TEST_UNDEFINED;
485
486 ip = user_record_image_path(h);
487 if (!ip)
488 return -ENXIO;
489
490 if (stat(ip, &st) < 0) {
491 if (errno == ENOENT)
492 return USER_TEST_ABSENT;
493
494 return -errno;
495 }
496
497 switch (user_record_storage(h)) {
498
499 case USER_LUKS:
500 if (S_ISREG(st.st_mode))
501 return USER_TEST_EXISTS;
502 if (S_ISBLK(st.st_mode)) {
503 /* For block devices we can't really be sure if the device referenced actually is the
504 * fs we look for or some other file system (think: what does /dev/sdb1 refer
505 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
506 * case, except if the device path used is one of the paths that is based on a
507 * filesystem or partition UUID or label, because in those cases we can be sure we
508 * are referring to the right device. */
509
510 if (PATH_STARTSWITH_SET(ip,
511 "/dev/disk/by-uuid/",
512 "/dev/disk/by-partuuid/",
513 "/dev/disk/by-partlabel/",
514 "/dev/disk/by-label/"))
515 return USER_TEST_EXISTS;
516
517 return USER_TEST_MAYBE;
518 }
519
520 return -EBADFD;
521
522 case USER_CLASSIC:
523 case USER_DIRECTORY:
524 case USER_SUBVOLUME:
525 case USER_FSCRYPT:
526 if (S_ISDIR(st.st_mode))
527 return USER_TEST_EXISTS;
528
529 return -ENOTDIR;
530
531 default:
532 assert_not_reached("Unexpected record type");
533 }
534 }
535
536 int user_record_test_image_path_and_warn(UserRecord *h) {
537 int r;
538
539 assert(h);
540
541 r = user_record_test_image_path(h);
542 if (r == -ENXIO)
543 return log_error_errno(r, "User record lacks image path, refusing.");
544 if (r == -EBADFD)
545 return log_error_errno(r, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h));
546 if (r == -ENOTDIR)
547 return log_error_errno(r, "Image path %s is not a directory, refusing.", user_record_image_path(h));
548 if (r < 0)
549 return log_error_errno(r, "Failed to test whether image path %s exists: %m", user_record_image_path(h));
550
551 return r;
552 }
553
554 int user_record_test_password(UserRecord *h, UserRecord *secret) {
555 char **i;
556 int r;
557
558 assert(h);
559
560 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
561
562 if (strv_isempty(h->hashed_password))
563 return -ENXIO;
564
565 STRV_FOREACH(i, secret->password) {
566 r = test_password_many(h->hashed_password, *i);
567 if (r < 0)
568 return r;
569 if (r > 0)
570 return 0;
571 }
572
573 return -ENOKEY;
574 }
575
576 int user_record_test_recovery_key(UserRecord *h, UserRecord *secret) {
577 char **i;
578 int r;
579
580 assert(h);
581
582 /* Checks whether any of the specified passwords matches any of the hashed recovery keys of the entry */
583
584 if (h->n_recovery_key == 0)
585 return -ENXIO;
586
587 STRV_FOREACH(i, secret->password) {
588 for (size_t j = 0; j < h->n_recovery_key; j++) {
589 _cleanup_(erase_and_freep) char *mangled = NULL;
590 const char *p;
591
592 if (streq(h->recovery_key[j].type, "modhex64")) {
593 /* If this key is for a modhex64 recovery key, then try to normalize the
594 * passphrase to make things more robust: that way the password becomes case
595 * insensitive and the dashes become optional. */
596
597 r = normalize_recovery_key(*i, &mangled);
598 if (r == -EINVAL) /* Not a valid modhex64 passphrase, don't bother */
599 continue;
600 if (r < 0)
601 return r;
602
603 p = mangled;
604 } else
605 p = *i; /* Unknown recovery key types process as is */
606
607 r = test_password_one(h->recovery_key[j].hashed_password, p);
608 if (r < 0)
609 return r;
610 if (r > 0)
611 return 0;
612 }
613 }
614
615 return -ENOKEY;
616 }
617
618 int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
619 _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine = NULL, *midv = NULL, *midav = NULL, *ne = NULL;
620 _cleanup_free_ JsonVariant **array = NULL;
621 char smid[SD_ID128_STRING_MAX];
622 size_t idx = SIZE_MAX, n;
623 JsonVariant *per_machine;
624 sd_id128_t mid;
625 int r;
626
627 assert(h);
628
629 if (!h->json)
630 return -EUNATCH;
631
632 if (disk_size < USER_DISK_SIZE_MIN || disk_size > USER_DISK_SIZE_MAX)
633 return -ERANGE;
634
635 r = sd_id128_get_machine(&mid);
636 if (r < 0)
637 return r;
638
639 sd_id128_to_string(mid, smid);
640
641 r = json_variant_new_string(&midv, smid);
642 if (r < 0)
643 return r;
644
645 r = json_variant_new_array(&midav, (JsonVariant*[]) { midv }, 1);
646 if (r < 0)
647 return r;
648
649 per_machine = json_variant_by_key(h->json, "perMachine");
650 if (per_machine) {
651 size_t i;
652
653 if (!json_variant_is_array(per_machine))
654 return -EINVAL;
655
656 n = json_variant_elements(per_machine);
657
658 array = new(JsonVariant*, n + 1);
659 if (!array)
660 return -ENOMEM;
661
662 for (i = 0; i < n; i++) {
663 JsonVariant *m;
664
665 array[i] = json_variant_by_index(per_machine, i);
666
667 if (!json_variant_is_object(array[i]))
668 return -EINVAL;
669
670 m = json_variant_by_key(array[i], "matchMachineId");
671 if (!m) {
672 /* No machineId field? Let's ignore this, but invalidate what we found so far */
673 idx = SIZE_MAX;
674 continue;
675 }
676
677 if (json_variant_equal(m, midv) ||
678 json_variant_equal(m, midav)) {
679 /* Matches exactly what we are looking for. Let's use this */
680 idx = i;
681 continue;
682 }
683
684 r = per_machine_id_match(m, JSON_PERMISSIVE);
685 if (r < 0)
686 return r;
687 if (r > 0)
688 /* Also matches what we are looking for, but with a broader match. In this
689 * case let's ignore this entry, and add a new specific one to the end. */
690 idx = SIZE_MAX;
691 }
692
693 if (idx == SIZE_MAX)
694 idx = n++; /* Nothing suitable found, place new entry at end */
695 else
696 ne = json_variant_ref(array[idx]);
697
698 } else {
699 array = new(JsonVariant*, 1);
700 if (!array)
701 return -ENOMEM;
702
703 idx = 0;
704 n = 1;
705 }
706
707 if (!ne) {
708 r = json_variant_set_field(&ne, "matchMachineId", midav);
709 if (r < 0)
710 return r;
711 }
712
713 r = json_variant_set_field_unsigned(&ne, "diskSize", disk_size);
714 if (r < 0)
715 return r;
716
717 assert(idx < n);
718 array[idx] = ne;
719
720 r = json_variant_new_array(&new_per_machine, array, n);
721 if (r < 0)
722 return r;
723
724 r = json_variant_set_field(&h->json, "perMachine", new_per_machine);
725 if (r < 0)
726 return r;
727
728 h->disk_size = disk_size;
729 h->mask |= USER_RECORD_PER_MACHINE;
730 return 0;
731 }
732
733 int user_record_update_last_changed(UserRecord *h, bool with_password) {
734 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
735 usec_t n;
736 int r;
737
738 assert(h);
739
740 if (!h->json)
741 return -EUNATCH;
742
743 n = now(CLOCK_REALTIME);
744
745 /* refuse downgrading */
746 if (h->last_change_usec != UINT64_MAX && h->last_change_usec >= n)
747 return -ECHRNG;
748 if (h->last_password_change_usec != UINT64_MAX && h->last_password_change_usec >= n)
749 return -ECHRNG;
750
751 v = json_variant_ref(h->json);
752
753 r = json_variant_set_field_unsigned(&v, "lastChangeUSec", n);
754 if (r < 0)
755 return r;
756
757 if (with_password) {
758 r = json_variant_set_field_unsigned(&v, "lastPasswordChangeUSec", n);
759 if (r < 0)
760 return r;
761
762 h->last_password_change_usec = n;
763 }
764
765 h->last_change_usec = n;
766
767 json_variant_unref(h->json);
768 h->json = TAKE_PTR(v);
769
770 h->mask |= USER_RECORD_REGULAR;
771 return 0;
772 }
773
774 int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
775 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
776 _cleanup_strv_free_ char **np = NULL;
777 char **i;
778 int r;
779
780 assert(h);
781 assert(secret);
782
783 /* Initializes the hashed password list from the specified plaintext passwords */
784
785 if (extend) {
786 np = strv_copy(h->hashed_password);
787 if (!np)
788 return -ENOMEM;
789
790 strv_uniq(np);
791 }
792
793 STRV_FOREACH(i, secret) {
794 _cleanup_free_ char *salt = NULL;
795 struct crypt_data cd = {};
796 char *k;
797
798 r = make_salt(&salt);
799 if (r < 0)
800 return r;
801
802 errno = 0;
803 k = crypt_r(*i, salt, &cd);
804 if (!k)
805 return errno_or_else(EINVAL);
806
807 r = strv_extend(&np, k);
808 if (r < 0)
809 return r;
810 }
811
812 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
813
814 if (strv_isempty(np))
815 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
816 else {
817 _cleanup_(json_variant_unrefp) JsonVariant *new_array = NULL;
818
819 r = json_variant_new_array_strv(&new_array, np);
820 if (r < 0)
821 return r;
822
823 r = json_variant_set_field(&priv, "hashedPassword", new_array);
824 if (r < 0)
825 return r;
826 }
827
828 r = json_variant_set_field(&h->json, "privileged", priv);
829 if (r < 0)
830 return r;
831
832 strv_free_and_replace(h->hashed_password, np);
833
834 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
835 return 0;
836 }
837
838 int user_record_set_hashed_password(UserRecord *h, char **hashed_password) {
839 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
840 _cleanup_strv_free_ char **copy = NULL;
841 int r;
842
843 assert(h);
844
845 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
846
847 if (strv_isempty(hashed_password))
848 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
849 else {
850 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
851
852 copy = strv_copy(hashed_password);
853 if (!copy)
854 return -ENOMEM;
855
856 strv_uniq(copy);
857
858 r = json_variant_new_array_strv(&array, copy);
859 if (r < 0)
860 return r;
861
862 r = json_variant_set_field(&priv, "hashedPassword", array);
863 }
864 if (r < 0)
865 return r;
866
867 r = json_variant_set_field(&h->json, "privileged", priv);
868 if (r < 0)
869 return r;
870
871 strv_free_and_replace(h->hashed_password, copy);
872
873 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
874 return 0;
875 }
876
877 int user_record_set_password(UserRecord *h, char **password, bool prepend) {
878 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
879 _cleanup_(strv_free_erasep) char **e = NULL;
880 int r;
881
882 assert(h);
883
884 if (prepend) {
885 e = strv_copy(password);
886 if (!e)
887 return -ENOMEM;
888
889 r = strv_extend_strv(&e, h->password, true);
890 if (r < 0)
891 return r;
892
893 strv_uniq(e);
894
895 if (strv_equal(h->password, e))
896 return 0;
897
898 } else {
899 if (strv_equal(h->password, password))
900 return 0;
901
902 e = strv_copy(password);
903 if (!e)
904 return -ENOMEM;
905
906 strv_uniq(e);
907 }
908
909 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
910
911 if (strv_isempty(e))
912 r = json_variant_filter(&w, STRV_MAKE("password"));
913 else {
914 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
915
916 r = json_variant_new_array_strv(&l, e);
917 if (r < 0)
918 return r;
919
920 json_variant_sensitive(l);
921
922 r = json_variant_set_field(&w, "password", l);
923 }
924 if (r < 0)
925 return r;
926
927 json_variant_sensitive(w);
928
929 r = json_variant_set_field(&h->json, "secret", w);
930 if (r < 0)
931 return r;
932
933 strv_free_and_replace(h->password, e);
934
935 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
936 return 0;
937 }
938
939 int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) {
940 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
941 _cleanup_(strv_free_erasep) char **e = NULL;
942 int r;
943
944 assert(h);
945
946 if (prepend) {
947 e = strv_copy(pin);
948 if (!e)
949 return -ENOMEM;
950
951 r = strv_extend_strv(&e, h->token_pin, true);
952 if (r < 0)
953 return r;
954
955 strv_uniq(e);
956
957 if (strv_equal(h->token_pin, e))
958 return 0;
959
960 } else {
961 if (strv_equal(h->token_pin, pin))
962 return 0;
963
964 e = strv_copy(pin);
965 if (!e)
966 return -ENOMEM;
967
968 strv_uniq(e);
969 }
970
971 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
972
973 if (strv_isempty(e))
974 r = json_variant_filter(&w, STRV_MAKE("tokenPin"));
975 else {
976 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
977
978 r = json_variant_new_array_strv(&l, e);
979 if (r < 0)
980 return r;
981
982 json_variant_sensitive(l);
983
984 r = json_variant_set_field(&w, "tokenPin", l);
985 }
986 if (r < 0)
987 return r;
988
989 json_variant_sensitive(w);
990
991 r = json_variant_set_field(&h->json, "secret", w);
992 if (r < 0)
993 return r;
994
995 strv_free_and_replace(h->token_pin, e);
996
997 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
998 return 0;
999 }
1000
1001 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b) {
1002 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1003 int r;
1004
1005 assert(h);
1006
1007 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1008
1009 if (b < 0)
1010 r = json_variant_filter(&w, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
1011 else
1012 r = json_variant_set_field_boolean(&w, "pkcs11ProtectedAuthenticationPathPermitted", b);
1013 if (r < 0)
1014 return r;
1015
1016 if (json_variant_is_blank_object(w))
1017 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1018 else {
1019 json_variant_sensitive(w);
1020
1021 r = json_variant_set_field(&h->json, "secret", w);
1022 }
1023 if (r < 0)
1024 return r;
1025
1026 h->pkcs11_protected_authentication_path_permitted = b;
1027
1028 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1029 return 0;
1030 }
1031
1032 int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
1033 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1034 int r;
1035
1036 assert(h);
1037
1038 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1039
1040 if (b < 0)
1041 r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted"));
1042 else
1043 r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b);
1044 if (r < 0)
1045 return r;
1046
1047 if (json_variant_is_blank_object(w))
1048 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1049 else
1050 r = json_variant_set_field(&h->json, "secret", w);
1051 if (r < 0)
1052 return r;
1053
1054 h->fido2_user_presence_permitted = b;
1055
1056 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1057 return 0;
1058 }
1059
1060 static bool per_machine_entry_empty(JsonVariant *v) {
1061 const char *k;
1062 _unused_ JsonVariant *e;
1063
1064 JSON_VARIANT_OBJECT_FOREACH(k, e, v)
1065 if (!STR_IN_SET(k, "matchMachineId", "matchHostname"))
1066 return false;
1067
1068 return true;
1069 }
1070
1071 int user_record_set_password_change_now(UserRecord *h, int b) {
1072 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1073 JsonVariant *per_machine;
1074 int r;
1075
1076 assert(h);
1077
1078 w = json_variant_ref(h->json);
1079
1080 if (b < 0)
1081 r = json_variant_filter(&w, STRV_MAKE("passwordChangeNow"));
1082 else
1083 r = json_variant_set_field_boolean(&w, "passwordChangeNow", b);
1084 if (r < 0)
1085 return r;
1086
1087 /* Also drop the field from all perMachine entries */
1088 per_machine = json_variant_by_key(w, "perMachine");
1089 if (per_machine) {
1090 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1091 JsonVariant *e;
1092
1093 JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
1094 _cleanup_(json_variant_unrefp) JsonVariant *z = NULL;
1095
1096 if (!json_variant_is_object(e))
1097 return -EINVAL;
1098
1099 z = json_variant_ref(e);
1100
1101 r = json_variant_filter(&z, STRV_MAKE("passwordChangeNow"));
1102 if (r < 0)
1103 return r;
1104
1105 if (per_machine_entry_empty(z))
1106 continue;
1107
1108 r = json_variant_append_array(&array, z);
1109 if (r < 0)
1110 return r;
1111 }
1112
1113 if (json_variant_is_blank_array(array))
1114 r = json_variant_filter(&w, STRV_MAKE("perMachine"));
1115 else
1116 r = json_variant_set_field(&w, "perMachine", array);
1117 if (r < 0)
1118 return r;
1119
1120 SET_FLAG(h->mask, USER_RECORD_PER_MACHINE, !json_variant_is_blank_array(array));
1121 }
1122
1123 json_variant_unref(h->json);
1124 h->json = TAKE_PTR(w);
1125
1126 h->password_change_now = b;
1127
1128 return 0;
1129 }
1130
1131 int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
1132 int r;
1133
1134 assert(h);
1135
1136 /* Merges the secrets from 'secret' into 'h'. */
1137
1138 r = user_record_set_password(h, secret->password, true);
1139 if (r < 0)
1140 return r;
1141
1142 r = user_record_set_token_pin(h, secret->token_pin, true);
1143 if (r < 0)
1144 return r;
1145
1146 if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
1147 r = user_record_set_pkcs11_protected_authentication_path_permitted(
1148 h,
1149 secret->pkcs11_protected_authentication_path_permitted);
1150 if (r < 0)
1151 return r;
1152 }
1153
1154 if (secret->fido2_user_presence_permitted >= 0) {
1155 r = user_record_set_fido2_user_presence_permitted(
1156 h,
1157 secret->fido2_user_presence_permitted);
1158 if (r < 0)
1159 return r;
1160 }
1161
1162 return 0;
1163 }
1164
1165 int user_record_good_authentication(UserRecord *h) {
1166 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1167 char buf[SD_ID128_STRING_MAX];
1168 uint64_t counter, usec;
1169 sd_id128_t mid;
1170 int r;
1171
1172 assert(h);
1173
1174 switch (h->good_authentication_counter) {
1175 case UINT64_MAX:
1176 counter = 1;
1177 break;
1178 case UINT64_MAX-1:
1179 counter = h->good_authentication_counter; /* saturate */
1180 break;
1181 default:
1182 counter = h->good_authentication_counter + 1;
1183 break;
1184 }
1185
1186 usec = now(CLOCK_REALTIME);
1187
1188 r = sd_id128_get_machine(&mid);
1189 if (r < 0)
1190 return r;
1191
1192 v = json_variant_ref(h->json);
1193 w = json_variant_ref(json_variant_by_key(v, "status"));
1194 z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
1195
1196 r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
1197 if (r < 0)
1198 return r;
1199
1200 r = json_variant_set_field_unsigned(&z, "lastGoodAuthenticationUSec", usec);
1201 if (r < 0)
1202 return r;
1203
1204 r = json_variant_set_field(&w, buf, z);
1205 if (r < 0)
1206 return r;
1207
1208 r = json_variant_set_field(&v, "status", w);
1209 if (r < 0)
1210 return r;
1211
1212 json_variant_unref(h->json);
1213 h->json = TAKE_PTR(v);
1214
1215 h->good_authentication_counter = counter;
1216 h->last_good_authentication_usec = usec;
1217
1218 h->mask |= USER_RECORD_STATUS;
1219 return 0;
1220 }
1221
1222 int user_record_bad_authentication(UserRecord *h) {
1223 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1224 char buf[SD_ID128_STRING_MAX];
1225 uint64_t counter, usec;
1226 sd_id128_t mid;
1227 int r;
1228
1229 assert(h);
1230
1231 switch (h->bad_authentication_counter) {
1232 case UINT64_MAX:
1233 counter = 1;
1234 break;
1235 case UINT64_MAX-1:
1236 counter = h->bad_authentication_counter; /* saturate */
1237 break;
1238 default:
1239 counter = h->bad_authentication_counter + 1;
1240 break;
1241 }
1242
1243 usec = now(CLOCK_REALTIME);
1244
1245 r = sd_id128_get_machine(&mid);
1246 if (r < 0)
1247 return r;
1248
1249 v = json_variant_ref(h->json);
1250 w = json_variant_ref(json_variant_by_key(v, "status"));
1251 z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
1252
1253 r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
1254 if (r < 0)
1255 return r;
1256
1257 r = json_variant_set_field_unsigned(&z, "lastBadAuthenticationUSec", usec);
1258 if (r < 0)
1259 return r;
1260
1261 r = json_variant_set_field(&w, buf, z);
1262 if (r < 0)
1263 return r;
1264
1265 r = json_variant_set_field(&v, "status", w);
1266 if (r < 0)
1267 return r;
1268
1269 json_variant_unref(h->json);
1270 h->json = TAKE_PTR(v);
1271
1272 h->bad_authentication_counter = counter;
1273 h->last_bad_authentication_usec = usec;
1274
1275 h->mask |= USER_RECORD_STATUS;
1276 return 0;
1277 }
1278
1279 int user_record_ratelimit(UserRecord *h) {
1280 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1281 usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
1282 char buf[SD_ID128_STRING_MAX];
1283 sd_id128_t mid;
1284 int r;
1285
1286 assert(h);
1287
1288 usec = now(CLOCK_REALTIME);
1289
1290 if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec)
1291 /* Hmm, time is running backwards? Say no! */
1292 return 0;
1293 else if (h->ratelimit_begin_usec == UINT64_MAX ||
1294 usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
1295 /* Fresh start */
1296 new_ratelimit_begin_usec = usec;
1297 new_ratelimit_count = 1;
1298 } else if (h->ratelimit_count < user_record_ratelimit_burst(h)) {
1299 /* Count up */
1300 new_ratelimit_begin_usec = h->ratelimit_begin_usec;
1301 new_ratelimit_count = h->ratelimit_count + 1;
1302 } else
1303 /* Limit hit */
1304 return 0;
1305
1306 r = sd_id128_get_machine(&mid);
1307 if (r < 0)
1308 return r;
1309
1310 v = json_variant_ref(h->json);
1311 w = json_variant_ref(json_variant_by_key(v, "status"));
1312 z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
1313
1314 r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
1315 if (r < 0)
1316 return r;
1317
1318 r = json_variant_set_field_unsigned(&z, "rateLimitCount", new_ratelimit_count);
1319 if (r < 0)
1320 return r;
1321
1322 r = json_variant_set_field(&w, buf, z);
1323 if (r < 0)
1324 return r;
1325
1326 r = json_variant_set_field(&v, "status", w);
1327 if (r < 0)
1328 return r;
1329
1330 json_variant_unref(h->json);
1331 h->json = TAKE_PTR(v);
1332
1333 h->ratelimit_begin_usec = new_ratelimit_begin_usec;
1334 h->ratelimit_count = new_ratelimit_count;
1335
1336 h->mask |= USER_RECORD_STATUS;
1337 return 1;
1338 }
1339
1340 int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
1341 assert(hr);
1342
1343 if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
1344 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
1345
1346 if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
1347 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
1348
1349 if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
1350 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
1351
1352 if (hr->service && !streq(hr->service, "io.systemd.Home"))
1353 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
1354
1355 return 0;
1356 }