]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/user-record-util.c
Merge pull request #22508 from poettering/stat-ino-compare
[thirdparty/systemd.git] / src / home / user-record-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <sys/xattr.h>
4
5 #include "errno-util.h"
6 #include "home-util.h"
7 #include "id128-util.h"
8 #include "libcrypt-util.h"
9 #include "memory-util.h"
10 #include "recovery-key.h"
11 #include "mountpoint-util.h"
12 #include "path-util.h"
13 #include "stat-util.h"
14 #include "user-record-util.h"
15 #include "user-util.h"
16
17 int user_record_synthesize(
18 UserRecord *h,
19 const char *user_name,
20 const char *realm,
21 const char *image_path,
22 UserStorage storage,
23 uid_t uid,
24 gid_t gid) {
25
26 _cleanup_free_ char *hd = NULL, *un = NULL, *ip = NULL, *rr = NULL, *user_name_and_realm = NULL;
27 sd_id128_t mid;
28 int r;
29
30 assert(h);
31 assert(user_name);
32 assert(image_path);
33 assert(IN_SET(storage, USER_LUKS, USER_SUBVOLUME, USER_FSCRYPT, USER_DIRECTORY));
34 assert(uid_is_valid(uid));
35 assert(gid_is_valid(gid));
36
37 /* Fill in a home record from just a username and an image path. */
38
39 if (h->json)
40 return -EBUSY;
41
42 if (!suitable_user_name(user_name))
43 return -EINVAL;
44
45 if (realm) {
46 r = suitable_realm(realm);
47 if (r < 0)
48 return r;
49 if (r == 0)
50 return -EINVAL;
51 }
52
53 if (!suitable_image_path(image_path))
54 return -EINVAL;
55
56 r = sd_id128_get_machine(&mid);
57 if (r < 0)
58 return r;
59
60 un = strdup(user_name);
61 if (!un)
62 return -ENOMEM;
63
64 if (realm) {
65 rr = strdup(realm);
66 if (!rr)
67 return -ENOMEM;
68
69 user_name_and_realm = strjoin(user_name, "@", realm);
70 if (!user_name_and_realm)
71 return -ENOMEM;
72 }
73
74 ip = strdup(image_path);
75 if (!ip)
76 return -ENOMEM;
77
78 hd = path_join(get_home_root(), user_name);
79 if (!hd)
80 return -ENOMEM;
81
82 r = json_build(&h->json,
83 JSON_BUILD_OBJECT(
84 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
85 JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(realm)),
86 JSON_BUILD_PAIR("disposition", JSON_BUILD_CONST_STRING("regular")),
87 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
88 JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
89 JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path)),
90 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd)),
91 JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage))),
92 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
93 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid))))))));
94 if (r < 0)
95 return r;
96
97 free_and_replace(h->user_name, un);
98 free_and_replace(h->realm, rr);
99 free_and_replace(h->user_name_and_realm_auto, user_name_and_realm);
100 free_and_replace(h->image_path, ip);
101 free_and_replace(h->home_directory, hd);
102 h->storage = storage;
103 h->uid = uid;
104
105 h->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
106 return 0;
107 }
108
109 int group_record_synthesize(GroupRecord *g, UserRecord *h) {
110 _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL, *description = NULL;
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), 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), JSON_BUILD_OBJECT(
153 JSON_BUILD_PAIR("service", JSON_BUILD_CONST_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|USER_RECORD_PERMISSIVE);
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|USER_RECORD_PERMISSIVE, 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 _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
286 sd_id128_t mid;
287 int r;
288
289 assert(h);
290
291 if (!h->json)
292 return -EUNATCH;
293
294 r = sd_id128_get_machine(&mid);
295 if (r < 0)
296 return r;
297
298 if (image_path) {
299 ip = strdup(image_path);
300 if (!ip)
301 return -ENOMEM;
302 } else if (!h->image_path && storage >= 0) {
303 r = user_record_build_image_path(storage, user_record_user_name_and_realm(h), &ip_auto);
304 if (r < 0)
305 return r;
306 }
307
308 if (home_directory) {
309 hd = strdup(home_directory);
310 if (!hd)
311 return -ENOMEM;
312 }
313
314 if (file_system_type) {
315 fst = strdup(file_system_type);
316 if (!fst)
317 return -ENOMEM;
318 }
319
320 if (luks_cipher) {
321 lc = strdup(luks_cipher);
322 if (!lc)
323 return -ENOMEM;
324 }
325
326 if (luks_cipher_mode) {
327 lcm = strdup(luks_cipher_mode);
328 if (!lcm)
329 return -ENOMEM;
330 }
331
332 r = json_build(&new_binding_entry,
333 JSON_BUILD_OBJECT(
334 JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
335 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(partition_uuid))),
336 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(luks_uuid))),
337 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(fs_uuid))),
338 JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
339 JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
340 JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
341 JSON_BUILD_PAIR_CONDITION(!!file_system_type, "fileSystemType", JSON_BUILD_STRING(file_system_type)),
342 JSON_BUILD_PAIR_CONDITION(!!home_directory, "homeDirectory", JSON_BUILD_STRING(home_directory)),
343 JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid), "uid", JSON_BUILD_UNSIGNED(uid)),
344 JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid), "gid", JSON_BUILD_UNSIGNED(gid)),
345 JSON_BUILD_PAIR_CONDITION(storage >= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage)))));
346 if (r < 0)
347 return r;
348
349 binding = json_variant_ref(json_variant_by_key(h->json, "binding"));
350 if (binding) {
351 _cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
352
353 /* Merge the new entry with an old one, if that exists */
354 be = json_variant_ref(json_variant_by_key(binding, SD_ID128_TO_STRING(mid)));
355 if (be) {
356 r = json_variant_merge(&be, new_binding_entry);
357 if (r < 0)
358 return r;
359
360 json_variant_unref(new_binding_entry);
361 new_binding_entry = TAKE_PTR(be);
362 }
363 }
364
365 r = json_variant_set_field(&binding, SD_ID128_TO_STRING(mid), new_binding_entry);
366 if (r < 0)
367 return r;
368
369 r = json_variant_set_field(&h->json, "binding", binding);
370 if (r < 0)
371 return r;
372
373 if (storage >= 0)
374 h->storage = storage;
375
376 if (ip)
377 free_and_replace(h->image_path, ip);
378 if (ip_auto)
379 free_and_replace(h->image_path_auto, ip_auto);
380
381 if (!sd_id128_is_null(partition_uuid))
382 h->partition_uuid = partition_uuid;
383
384 if (!sd_id128_is_null(luks_uuid))
385 h->luks_uuid = luks_uuid;
386
387 if (!sd_id128_is_null(fs_uuid))
388 h->file_system_uuid = fs_uuid;
389
390 if (lc)
391 free_and_replace(h->luks_cipher, lc);
392 if (lcm)
393 free_and_replace(h->luks_cipher_mode, lcm);
394 if (luks_volume_key_size != UINT64_MAX)
395 h->luks_volume_key_size = luks_volume_key_size;
396
397 if (fst)
398 free_and_replace(h->file_system_type, fst);
399 if (hd)
400 free_and_replace(h->home_directory, hd);
401
402 if (uid_is_valid(uid))
403 h->uid = uid;
404 if (gid_is_valid(gid))
405 h->gid = gid;
406
407 h->mask |= USER_RECORD_BINDING;
408 return 1;
409 }
410
411 int user_record_test_home_directory(UserRecord *h) {
412 const char *hd;
413 int r;
414
415 assert(h);
416
417 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
418
419 hd = user_record_home_directory(h);
420 if (!hd)
421 return -ENXIO;
422
423 r = is_dir(hd, false);
424 if (r == -ENOENT)
425 return USER_TEST_ABSENT;
426 if (r < 0)
427 return r;
428 if (r == 0)
429 return -ENOTDIR;
430
431 r = path_is_mount_point(hd, NULL, 0);
432 if (r < 0)
433 return r;
434 if (r > 0)
435 return USER_TEST_MOUNTED;
436
437 /* If the image path and the home directory are identical, then it's OK if the directory is
438 * populated. */
439 if (IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) {
440 const char *ip;
441
442 ip = user_record_image_path(h);
443 if (ip && path_equal(ip, hd))
444 return USER_TEST_EXISTS;
445 }
446
447 /* Otherwise it's not OK */
448 r = dir_is_empty(hd);
449 if (r < 0)
450 return r;
451 if (r == 0)
452 return -EBUSY;
453
454 return USER_TEST_EXISTS;
455 }
456
457 int user_record_test_home_directory_and_warn(UserRecord *h) {
458 int r;
459
460 assert(h);
461
462 r = user_record_test_home_directory(h);
463 if (r == -ENXIO)
464 return log_error_errno(r, "User record lacks home directory, refusing.");
465 if (r == -ENOTDIR)
466 return log_error_errno(r, "Home directory %s is not a directory, refusing.", user_record_home_directory(h));
467 if (r == -EBUSY)
468 return log_error_errno(r, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h));
469 if (r < 0)
470 return log_error_errno(r, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h));
471
472 return r;
473 }
474
475 int user_record_test_image_path(UserRecord *h) {
476 const char *ip;
477 struct stat st;
478
479 assert(h);
480
481 if (user_record_storage(h) == USER_CIFS)
482 return USER_TEST_UNDEFINED;
483
484 ip = user_record_image_path(h);
485 if (!ip)
486 return -ENXIO;
487
488 if (stat(ip, &st) < 0) {
489 if (errno == ENOENT)
490 return USER_TEST_ABSENT;
491
492 return -errno;
493 }
494
495 switch (user_record_storage(h)) {
496
497 case USER_LUKS:
498 if (S_ISREG(st.st_mode)) {
499 ssize_t n;
500 char x[2];
501
502 n = getxattr(ip, "user.home-dirty", x, sizeof(x));
503 if (n < 0) {
504 if (errno != ENODATA)
505 log_debug_errno(errno, "Unable to read dirty xattr off image file, ignoring: %m");
506
507 } else if (n == 1 && x[0] == '1')
508 return USER_TEST_DIRTY;
509
510 return USER_TEST_EXISTS;
511 }
512
513 if (S_ISBLK(st.st_mode)) {
514 /* For block devices we can't really be sure if the device referenced actually is the
515 * fs we look for or some other file system (think: what does /dev/sdb1 refer
516 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
517 * case, except if the device path used is one of the paths that is based on a
518 * filesystem or partition UUID or label, because in those cases we can be sure we
519 * are referring to the right device. */
520
521 if (PATH_STARTSWITH_SET(ip,
522 "/dev/disk/by-uuid/",
523 "/dev/disk/by-partuuid/",
524 "/dev/disk/by-partlabel/",
525 "/dev/disk/by-label/"))
526 return USER_TEST_EXISTS;
527
528 return USER_TEST_MAYBE;
529 }
530
531 return -EBADFD;
532
533 case USER_CLASSIC:
534 case USER_DIRECTORY:
535 case USER_SUBVOLUME:
536 case USER_FSCRYPT:
537 if (S_ISDIR(st.st_mode))
538 return USER_TEST_EXISTS;
539
540 return -ENOTDIR;
541
542 default:
543 assert_not_reached();
544 }
545 }
546
547 int user_record_test_image_path_and_warn(UserRecord *h) {
548 int r;
549
550 assert(h);
551
552 r = user_record_test_image_path(h);
553 if (r == -ENXIO)
554 return log_error_errno(r, "User record lacks image path, refusing.");
555 if (r == -EBADFD)
556 return log_error_errno(r, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h));
557 if (r == -ENOTDIR)
558 return log_error_errno(r, "Image path %s is not a directory, refusing.", user_record_image_path(h));
559 if (r < 0)
560 return log_error_errno(r, "Failed to test whether image path %s exists: %m", user_record_image_path(h));
561
562 return r;
563 }
564
565 int user_record_test_password(UserRecord *h, UserRecord *secret) {
566 char **i;
567 int r;
568
569 assert(h);
570
571 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
572
573 if (strv_isempty(h->hashed_password))
574 return -ENXIO;
575
576 STRV_FOREACH(i, secret->password) {
577 r = test_password_many(h->hashed_password, *i);
578 if (r < 0)
579 return r;
580 if (r > 0)
581 return 0;
582 }
583
584 return -ENOKEY;
585 }
586
587 int user_record_test_recovery_key(UserRecord *h, UserRecord *secret) {
588 char **i;
589 int r;
590
591 assert(h);
592
593 /* Checks whether any of the specified passwords matches any of the hashed recovery keys of the entry */
594
595 if (h->n_recovery_key == 0)
596 return -ENXIO;
597
598 STRV_FOREACH(i, secret->password) {
599 for (size_t j = 0; j < h->n_recovery_key; j++) {
600 _cleanup_(erase_and_freep) char *mangled = NULL;
601 const char *p;
602
603 if (streq(h->recovery_key[j].type, "modhex64")) {
604 /* If this key is for a modhex64 recovery key, then try to normalize the
605 * passphrase to make things more robust: that way the password becomes case
606 * insensitive and the dashes become optional. */
607
608 r = normalize_recovery_key(*i, &mangled);
609 if (r == -EINVAL) /* Not a valid modhex64 passphrase, don't bother */
610 continue;
611 if (r < 0)
612 return r;
613
614 p = mangled;
615 } else
616 p = *i; /* Unknown recovery key types process as is */
617
618 r = test_password_one(h->recovery_key[j].hashed_password, p);
619 if (r < 0)
620 return r;
621 if (r > 0)
622 return 0;
623 }
624 }
625
626 return -ENOKEY;
627 }
628
629 int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
630 _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine = NULL, *midv = NULL, *midav = NULL, *ne = NULL;
631 _cleanup_free_ JsonVariant **array = NULL;
632 size_t idx = SIZE_MAX, n;
633 JsonVariant *per_machine;
634 sd_id128_t mid;
635 int r;
636
637 assert(h);
638
639 if (!h->json)
640 return -EUNATCH;
641
642 r = sd_id128_get_machine(&mid);
643 if (r < 0)
644 return r;
645
646 r = json_variant_new_string(&midv, SD_ID128_TO_STRING(mid));
647 if (r < 0)
648 return r;
649
650 r = json_variant_new_array(&midav, (JsonVariant*[]) { midv }, 1);
651 if (r < 0)
652 return r;
653
654 per_machine = json_variant_by_key(h->json, "perMachine");
655 if (per_machine) {
656 size_t i;
657
658 if (!json_variant_is_array(per_machine))
659 return -EINVAL;
660
661 n = json_variant_elements(per_machine);
662
663 array = new(JsonVariant*, n + 1);
664 if (!array)
665 return -ENOMEM;
666
667 for (i = 0; i < n; i++) {
668 JsonVariant *m;
669
670 array[i] = json_variant_by_index(per_machine, i);
671
672 if (!json_variant_is_object(array[i]))
673 return -EINVAL;
674
675 m = json_variant_by_key(array[i], "matchMachineId");
676 if (!m) {
677 /* No machineId field? Let's ignore this, but invalidate what we found so far */
678 idx = SIZE_MAX;
679 continue;
680 }
681
682 if (json_variant_equal(m, midv) ||
683 json_variant_equal(m, midav)) {
684 /* Matches exactly what we are looking for. Let's use this */
685 idx = i;
686 continue;
687 }
688
689 r = per_machine_id_match(m, JSON_PERMISSIVE);
690 if (r < 0)
691 return r;
692 if (r > 0)
693 /* Also matches what we are looking for, but with a broader match. In this
694 * case let's ignore this entry, and add a new specific one to the end. */
695 idx = SIZE_MAX;
696 }
697
698 if (idx == SIZE_MAX)
699 idx = n++; /* Nothing suitable found, place new entry at end */
700 else
701 ne = json_variant_ref(array[idx]);
702
703 } else {
704 array = new(JsonVariant*, 1);
705 if (!array)
706 return -ENOMEM;
707
708 idx = 0;
709 n = 1;
710 }
711
712 if (!ne) {
713 r = json_variant_set_field(&ne, "matchMachineId", midav);
714 if (r < 0)
715 return r;
716 }
717
718 r = json_variant_set_field_unsigned(&ne, "diskSize", disk_size);
719 if (r < 0)
720 return r;
721
722 assert(idx < n);
723 array[idx] = ne;
724
725 r = json_variant_new_array(&new_per_machine, array, n);
726 if (r < 0)
727 return r;
728
729 r = json_variant_set_field(&h->json, "perMachine", new_per_machine);
730 if (r < 0)
731 return r;
732
733 h->disk_size = disk_size;
734 h->mask |= USER_RECORD_PER_MACHINE;
735 return 0;
736 }
737
738 int user_record_update_last_changed(UserRecord *h, bool with_password) {
739 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
740 usec_t n;
741 int r;
742
743 assert(h);
744
745 if (!h->json)
746 return -EUNATCH;
747
748 n = now(CLOCK_REALTIME);
749
750 /* refuse downgrading */
751 if (h->last_change_usec != UINT64_MAX && h->last_change_usec >= n)
752 return -ECHRNG;
753 if (h->last_password_change_usec != UINT64_MAX && h->last_password_change_usec >= n)
754 return -ECHRNG;
755
756 v = json_variant_ref(h->json);
757
758 r = json_variant_set_field_unsigned(&v, "lastChangeUSec", n);
759 if (r < 0)
760 return r;
761
762 if (with_password) {
763 r = json_variant_set_field_unsigned(&v, "lastPasswordChangeUSec", n);
764 if (r < 0)
765 return r;
766
767 h->last_password_change_usec = n;
768 }
769
770 h->last_change_usec = n;
771
772 json_variant_unref(h->json);
773 h->json = TAKE_PTR(v);
774
775 h->mask |= USER_RECORD_REGULAR;
776 return 0;
777 }
778
779 int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
780 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
781 _cleanup_strv_free_ char **np = NULL;
782 char **i;
783 int r;
784
785 assert(h);
786 assert(secret);
787
788 /* Initializes the hashed password list from the specified plaintext passwords */
789
790 if (extend) {
791 np = strv_copy(h->hashed_password);
792 if (!np)
793 return -ENOMEM;
794
795 strv_uniq(np);
796 }
797
798 STRV_FOREACH(i, secret) {
799 _cleanup_(erase_and_freep) char *hashed = NULL;
800
801 r = hash_password(*i, &hashed);
802 if (r < 0)
803 return r;
804
805 r = strv_consume(&np, TAKE_PTR(hashed));
806 if (r < 0)
807 return r;
808 }
809
810 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
811
812 if (strv_isempty(np))
813 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
814 else {
815 _cleanup_(json_variant_unrefp) JsonVariant *new_array = NULL;
816
817 r = json_variant_new_array_strv(&new_array, np);
818 if (r < 0)
819 return r;
820
821 r = json_variant_set_field(&priv, "hashedPassword", new_array);
822 if (r < 0)
823 return r;
824 }
825
826 r = json_variant_set_field(&h->json, "privileged", priv);
827 if (r < 0)
828 return r;
829
830 strv_free_and_replace(h->hashed_password, np);
831
832 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
833 return 0;
834 }
835
836 int user_record_set_hashed_password(UserRecord *h, char **hashed_password) {
837 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
838 _cleanup_strv_free_ char **copy = NULL;
839 int r;
840
841 assert(h);
842
843 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
844
845 if (strv_isempty(hashed_password))
846 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
847 else {
848 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
849
850 copy = strv_copy(hashed_password);
851 if (!copy)
852 return -ENOMEM;
853
854 strv_uniq(copy);
855
856 r = json_variant_new_array_strv(&array, copy);
857 if (r < 0)
858 return r;
859
860 r = json_variant_set_field(&priv, "hashedPassword", array);
861 }
862 if (r < 0)
863 return r;
864
865 r = json_variant_set_field(&h->json, "privileged", priv);
866 if (r < 0)
867 return r;
868
869 strv_free_and_replace(h->hashed_password, copy);
870
871 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
872 return 0;
873 }
874
875 int user_record_set_password(UserRecord *h, char **password, bool prepend) {
876 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
877 _cleanup_(strv_free_erasep) char **e = NULL;
878 int r;
879
880 assert(h);
881
882 if (prepend) {
883 e = strv_copy(password);
884 if (!e)
885 return -ENOMEM;
886
887 r = strv_extend_strv(&e, h->password, true);
888 if (r < 0)
889 return r;
890
891 strv_uniq(e);
892
893 if (strv_equal(h->password, e))
894 return 0;
895
896 } else {
897 if (strv_equal(h->password, password))
898 return 0;
899
900 e = strv_copy(password);
901 if (!e)
902 return -ENOMEM;
903
904 strv_uniq(e);
905 }
906
907 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
908
909 if (strv_isempty(e))
910 r = json_variant_filter(&w, STRV_MAKE("password"));
911 else {
912 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
913
914 r = json_variant_new_array_strv(&l, e);
915 if (r < 0)
916 return r;
917
918 json_variant_sensitive(l);
919
920 r = json_variant_set_field(&w, "password", l);
921 }
922 if (r < 0)
923 return r;
924
925 json_variant_sensitive(w);
926
927 r = json_variant_set_field(&h->json, "secret", w);
928 if (r < 0)
929 return r;
930
931 strv_free_and_replace(h->password, e);
932
933 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
934 return 0;
935 }
936
937 int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) {
938 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
939 _cleanup_(strv_free_erasep) char **e = NULL;
940 int r;
941
942 assert(h);
943
944 if (prepend) {
945 e = strv_copy(pin);
946 if (!e)
947 return -ENOMEM;
948
949 r = strv_extend_strv(&e, h->token_pin, true);
950 if (r < 0)
951 return r;
952
953 strv_uniq(e);
954
955 if (strv_equal(h->token_pin, e))
956 return 0;
957
958 } else {
959 if (strv_equal(h->token_pin, pin))
960 return 0;
961
962 e = strv_copy(pin);
963 if (!e)
964 return -ENOMEM;
965
966 strv_uniq(e);
967 }
968
969 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
970
971 if (strv_isempty(e))
972 r = json_variant_filter(&w, STRV_MAKE("tokenPin"));
973 else {
974 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
975
976 r = json_variant_new_array_strv(&l, e);
977 if (r < 0)
978 return r;
979
980 json_variant_sensitive(l);
981
982 r = json_variant_set_field(&w, "tokenPin", l);
983 }
984 if (r < 0)
985 return r;
986
987 json_variant_sensitive(w);
988
989 r = json_variant_set_field(&h->json, "secret", w);
990 if (r < 0)
991 return r;
992
993 strv_free_and_replace(h->token_pin, e);
994
995 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
996 return 0;
997 }
998
999 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b) {
1000 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1001 int r;
1002
1003 assert(h);
1004
1005 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1006
1007 if (b < 0)
1008 r = json_variant_filter(&w, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
1009 else
1010 r = json_variant_set_field_boolean(&w, "pkcs11ProtectedAuthenticationPathPermitted", b);
1011 if (r < 0)
1012 return r;
1013
1014 if (json_variant_is_blank_object(w))
1015 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1016 else {
1017 json_variant_sensitive(w);
1018
1019 r = json_variant_set_field(&h->json, "secret", w);
1020 }
1021 if (r < 0)
1022 return r;
1023
1024 h->pkcs11_protected_authentication_path_permitted = b;
1025
1026 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1027 return 0;
1028 }
1029
1030 int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
1031 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1032 int r;
1033
1034 assert(h);
1035
1036 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1037
1038 if (b < 0)
1039 r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted"));
1040 else
1041 r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b);
1042 if (r < 0)
1043 return r;
1044
1045 if (json_variant_is_blank_object(w))
1046 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1047 else
1048 r = json_variant_set_field(&h->json, "secret", w);
1049 if (r < 0)
1050 return r;
1051
1052 h->fido2_user_presence_permitted = b;
1053
1054 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1055 return 0;
1056 }
1057
1058 int user_record_set_fido2_user_verification_permitted(UserRecord *h, int b) {
1059 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1060 int r;
1061
1062 assert(h);
1063
1064 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1065
1066 if (b < 0)
1067 r = json_variant_filter(&w, STRV_MAKE("fido2UserVerificationPermitted"));
1068 else
1069 r = json_variant_set_field_boolean(&w, "fido2UserVerificationPermitted", b);
1070 if (r < 0)
1071 return r;
1072
1073 if (json_variant_is_blank_object(w))
1074 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1075 else
1076 r = json_variant_set_field(&h->json, "secret", w);
1077 if (r < 0)
1078 return r;
1079
1080 h->fido2_user_verification_permitted = b;
1081
1082 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1083 return 0;
1084 }
1085
1086 static bool per_machine_entry_empty(JsonVariant *v) {
1087 const char *k;
1088 _unused_ JsonVariant *e;
1089
1090 JSON_VARIANT_OBJECT_FOREACH(k, e, v)
1091 if (!STR_IN_SET(k, "matchMachineId", "matchHostname"))
1092 return false;
1093
1094 return true;
1095 }
1096
1097 int user_record_set_password_change_now(UserRecord *h, int b) {
1098 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1099 JsonVariant *per_machine;
1100 int r;
1101
1102 assert(h);
1103
1104 w = json_variant_ref(h->json);
1105
1106 if (b < 0)
1107 r = json_variant_filter(&w, STRV_MAKE("passwordChangeNow"));
1108 else
1109 r = json_variant_set_field_boolean(&w, "passwordChangeNow", b);
1110 if (r < 0)
1111 return r;
1112
1113 /* Also drop the field from all perMachine entries */
1114 per_machine = json_variant_by_key(w, "perMachine");
1115 if (per_machine) {
1116 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1117 JsonVariant *e;
1118
1119 JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
1120 _cleanup_(json_variant_unrefp) JsonVariant *z = NULL;
1121
1122 if (!json_variant_is_object(e))
1123 return -EINVAL;
1124
1125 z = json_variant_ref(e);
1126
1127 r = json_variant_filter(&z, STRV_MAKE("passwordChangeNow"));
1128 if (r < 0)
1129 return r;
1130
1131 if (per_machine_entry_empty(z))
1132 continue;
1133
1134 r = json_variant_append_array(&array, z);
1135 if (r < 0)
1136 return r;
1137 }
1138
1139 if (json_variant_is_blank_array(array))
1140 r = json_variant_filter(&w, STRV_MAKE("perMachine"));
1141 else
1142 r = json_variant_set_field(&w, "perMachine", array);
1143 if (r < 0)
1144 return r;
1145
1146 SET_FLAG(h->mask, USER_RECORD_PER_MACHINE, !json_variant_is_blank_array(array));
1147 }
1148
1149 json_variant_unref(h->json);
1150 h->json = TAKE_PTR(w);
1151
1152 h->password_change_now = b;
1153
1154 return 0;
1155 }
1156
1157 int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
1158 int r;
1159
1160 assert(h);
1161
1162 /* Merges the secrets from 'secret' into 'h'. */
1163
1164 r = user_record_set_password(h, secret->password, true);
1165 if (r < 0)
1166 return r;
1167
1168 r = user_record_set_token_pin(h, secret->token_pin, true);
1169 if (r < 0)
1170 return r;
1171
1172 if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
1173 r = user_record_set_pkcs11_protected_authentication_path_permitted(
1174 h,
1175 secret->pkcs11_protected_authentication_path_permitted);
1176 if (r < 0)
1177 return r;
1178 }
1179
1180 if (secret->fido2_user_presence_permitted >= 0) {
1181 r = user_record_set_fido2_user_presence_permitted(
1182 h,
1183 secret->fido2_user_presence_permitted);
1184 if (r < 0)
1185 return r;
1186 }
1187
1188 if (secret->fido2_user_verification_permitted >= 0) {
1189 r = user_record_set_fido2_user_verification_permitted(
1190 h,
1191 secret->fido2_user_verification_permitted);
1192 if (r < 0)
1193 return r;
1194 }
1195
1196 return 0;
1197 }
1198
1199 int user_record_good_authentication(UserRecord *h) {
1200 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1201 uint64_t counter, usec;
1202 sd_id128_t mid;
1203 int r;
1204
1205 assert(h);
1206
1207 switch (h->good_authentication_counter) {
1208 case UINT64_MAX:
1209 counter = 1;
1210 break;
1211 case UINT64_MAX-1:
1212 counter = h->good_authentication_counter; /* saturate */
1213 break;
1214 default:
1215 counter = h->good_authentication_counter + 1;
1216 break;
1217 }
1218
1219 usec = now(CLOCK_REALTIME);
1220
1221 r = sd_id128_get_machine(&mid);
1222 if (r < 0)
1223 return r;
1224
1225 v = json_variant_ref(h->json);
1226 w = json_variant_ref(json_variant_by_key(v, "status"));
1227 z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
1228
1229 r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
1230 if (r < 0)
1231 return r;
1232
1233 r = json_variant_set_field_unsigned(&z, "lastGoodAuthenticationUSec", usec);
1234 if (r < 0)
1235 return r;
1236
1237 r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
1238 if (r < 0)
1239 return r;
1240
1241 r = json_variant_set_field(&v, "status", w);
1242 if (r < 0)
1243 return r;
1244
1245 json_variant_unref(h->json);
1246 h->json = TAKE_PTR(v);
1247
1248 h->good_authentication_counter = counter;
1249 h->last_good_authentication_usec = usec;
1250
1251 h->mask |= USER_RECORD_STATUS;
1252 return 0;
1253 }
1254
1255 int user_record_bad_authentication(UserRecord *h) {
1256 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1257 uint64_t counter, usec;
1258 sd_id128_t mid;
1259 int r;
1260
1261 assert(h);
1262
1263 switch (h->bad_authentication_counter) {
1264 case UINT64_MAX:
1265 counter = 1;
1266 break;
1267 case UINT64_MAX-1:
1268 counter = h->bad_authentication_counter; /* saturate */
1269 break;
1270 default:
1271 counter = h->bad_authentication_counter + 1;
1272 break;
1273 }
1274
1275 usec = now(CLOCK_REALTIME);
1276
1277 r = sd_id128_get_machine(&mid);
1278 if (r < 0)
1279 return r;
1280
1281 v = json_variant_ref(h->json);
1282 w = json_variant_ref(json_variant_by_key(v, "status"));
1283 z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
1284
1285 r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
1286 if (r < 0)
1287 return r;
1288
1289 r = json_variant_set_field_unsigned(&z, "lastBadAuthenticationUSec", usec);
1290 if (r < 0)
1291 return r;
1292
1293 r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
1294 if (r < 0)
1295 return r;
1296
1297 r = json_variant_set_field(&v, "status", w);
1298 if (r < 0)
1299 return r;
1300
1301 json_variant_unref(h->json);
1302 h->json = TAKE_PTR(v);
1303
1304 h->bad_authentication_counter = counter;
1305 h->last_bad_authentication_usec = usec;
1306
1307 h->mask |= USER_RECORD_STATUS;
1308 return 0;
1309 }
1310
1311 int user_record_ratelimit(UserRecord *h) {
1312 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1313 usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
1314 sd_id128_t mid;
1315 int r;
1316
1317 assert(h);
1318
1319 usec = now(CLOCK_REALTIME);
1320
1321 if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec) {
1322 /* Hmm, start-time is after the current time? If so, the RTC most likely doesn't work. */
1323 new_ratelimit_begin_usec = usec;
1324 new_ratelimit_count = 1;
1325 log_debug("Rate limit timestamp is in the future, assuming incorrect system clock, resetting limit.");
1326 } else if (h->ratelimit_begin_usec == UINT64_MAX ||
1327 usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
1328 /* Fresh start */
1329 new_ratelimit_begin_usec = usec;
1330 new_ratelimit_count = 1;
1331 } else if (h->ratelimit_count < user_record_ratelimit_burst(h)) {
1332 /* Count up */
1333 new_ratelimit_begin_usec = h->ratelimit_begin_usec;
1334 new_ratelimit_count = h->ratelimit_count + 1;
1335 } else
1336 /* Limit hit */
1337 return 0;
1338
1339 r = sd_id128_get_machine(&mid);
1340 if (r < 0)
1341 return r;
1342
1343 v = json_variant_ref(h->json);
1344 w = json_variant_ref(json_variant_by_key(v, "status"));
1345 z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
1346
1347 r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
1348 if (r < 0)
1349 return r;
1350
1351 r = json_variant_set_field_unsigned(&z, "rateLimitCount", new_ratelimit_count);
1352 if (r < 0)
1353 return r;
1354
1355 r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
1356 if (r < 0)
1357 return r;
1358
1359 r = json_variant_set_field(&v, "status", w);
1360 if (r < 0)
1361 return r;
1362
1363 json_variant_unref(h->json);
1364 h->json = TAKE_PTR(v);
1365
1366 h->ratelimit_begin_usec = new_ratelimit_begin_usec;
1367 h->ratelimit_count = new_ratelimit_count;
1368
1369 h->mask |= USER_RECORD_STATUS;
1370 return 1;
1371 }
1372
1373 int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
1374 assert(hr);
1375
1376 if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
1377 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
1378
1379 if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
1380 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
1381
1382 if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
1383 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
1384
1385 if (hr->service && !streq(hr->service, "io.systemd.Home"))
1386 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
1387
1388 return 0;
1389 }
1390
1391 bool user_record_shall_rebalance(UserRecord *h) {
1392 assert(h);
1393
1394 if (user_record_rebalance_weight(h) == REBALANCE_WEIGHT_OFF)
1395 return false;
1396
1397 if (user_record_storage(h) != USER_LUKS)
1398 return false;
1399
1400 if (!path_startswith(user_record_image_path(h), get_home_root())) /* This is the only pool we rebalance in */
1401 return false;
1402
1403 return true;
1404 }
1405
1406 int user_record_set_rebalance_weight(UserRecord *h, uint64_t weight) {
1407 _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine_array = NULL, *machine_id_variant = NULL,
1408 *machine_id_array = NULL, *per_machine_entry = NULL;
1409 _cleanup_free_ JsonVariant **array = NULL;
1410 size_t idx = SIZE_MAX, n;
1411 JsonVariant *per_machine;
1412 sd_id128_t mid;
1413 int r;
1414
1415 assert(h);
1416
1417 if (!h->json)
1418 return -EUNATCH;
1419
1420 r = sd_id128_get_machine(&mid);
1421 if (r < 0)
1422 return r;
1423
1424 r = json_variant_new_id128(&machine_id_variant, mid);
1425 if (r < 0)
1426 return r;
1427
1428 r = json_variant_new_array(&machine_id_array, (JsonVariant*[]) { machine_id_variant }, 1);
1429 if (r < 0)
1430 return r;
1431
1432 per_machine = json_variant_by_key(h->json, "perMachine");
1433 if (per_machine) {
1434 if (!json_variant_is_array(per_machine))
1435 return -EINVAL;
1436
1437 n = json_variant_elements(per_machine);
1438
1439 array = new(JsonVariant*, n + 1);
1440 if (!array)
1441 return -ENOMEM;
1442
1443 for (size_t i = 0; i < n; i++) {
1444 JsonVariant *m;
1445
1446 array[i] = json_variant_by_index(per_machine, i);
1447
1448 if (!json_variant_is_object(array[i]))
1449 return -EINVAL;
1450
1451 m = json_variant_by_key(array[i], "matchMachineId");
1452 if (!m) {
1453 /* No machineId field? Let's ignore this, but invalidate what we found so far */
1454 idx = SIZE_MAX;
1455 continue;
1456 }
1457
1458 if (json_variant_equal(m, machine_id_variant) ||
1459 json_variant_equal(m, machine_id_array)) {
1460 /* Matches exactly what we are looking for. Let's use this */
1461 idx = i;
1462 continue;
1463 }
1464
1465 r = per_machine_id_match(m, JSON_PERMISSIVE);
1466 if (r < 0)
1467 return r;
1468 if (r > 0)
1469 /* Also matches what we are looking for, but with a broader match. In this
1470 * case let's ignore this entry, and add a new specific one to the end. */
1471 idx = SIZE_MAX;
1472 }
1473
1474 if (idx == SIZE_MAX)
1475 idx = n++; /* Nothing suitable found, place new entry at end */
1476 else
1477 per_machine_entry = json_variant_ref(array[idx]);
1478
1479 } else {
1480 array = new(JsonVariant*, 1);
1481 if (!array)
1482 return -ENOMEM;
1483
1484 idx = 0;
1485 n = 1;
1486 }
1487
1488 if (!per_machine_entry) {
1489 r = json_variant_set_field(&per_machine_entry, "matchMachineId", machine_id_array);
1490 if (r < 0)
1491 return r;
1492 }
1493
1494 if (weight == REBALANCE_WEIGHT_UNSET)
1495 r = json_variant_set_field(&per_machine_entry, "rebalanceWeight", NULL); /* set explicitly to NULL (so that the perMachine setting we are setting here can override the global setting) */
1496 else
1497 r = json_variant_set_field_unsigned(&per_machine_entry, "rebalanceWeight", weight);
1498 if (r < 0)
1499 return r;
1500
1501 assert(idx < n);
1502 array[idx] = per_machine_entry;
1503
1504 r = json_variant_new_array(&new_per_machine_array, array, n);
1505 if (r < 0)
1506 return r;
1507
1508 r = json_variant_set_field(&h->json, "perMachine", new_per_machine_array);
1509 if (r < 0)
1510 return r;
1511
1512 h->rebalance_weight = weight;
1513 h->mask |= USER_RECORD_PER_MACHINE;
1514 return 0;
1515 }