]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/user-record-util.c
tree-wide: port everything over to new sd-id128 compund literal bliss
[thirdparty/systemd.git] / src / home / user-record-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
70a5db58 2
9be99f81
LP
3#include <sys/xattr.h>
4
70a5db58
LP
5#include "errno-util.h"
6#include "home-util.h"
7#include "id128-util.h"
8#include "libcrypt-util.h"
aecbc87d 9#include "memory-util.h"
73d874ba 10#include "recovery-key.h"
70a5db58
LP
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
17int 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;
70a5db58
LP
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("/home/", 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_STRING("regular")),
87 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
85b55869 88 JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
70a5db58
LP
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
109int group_record_synthesize(GroupRecord *g, UserRecord *h) {
0a388dfc 110 _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL, *description = NULL;
70a5db58
LP
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
0a388dfc
LP
138 description = strjoin("Primary Group of User ", un);
139 if (!description)
140 return -ENOMEM;
141
70a5db58
LP
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)),
0a388dfc 146 JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
70a5db58 147 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
85b55869 148 JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
70a5db58
LP
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(
85b55869 152 JSON_BUILD_PAIR(SD_ID128_TO_STRING(mid), JSON_BUILD_OBJECT(
70a5db58
LP
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
167int 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 *
37b22b3b 182 * Return the new record to use, which is either the embedded record updated with the host
70a5db58
LP
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
7802194a 200 /* Embedded identities may not contain secrets or binding info */
70a5db58
LP
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
bfc0cc1a 253 r = user_record_load(merged, extended, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_PERMISSIVE);
70a5db58
LP
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 */
bfc0cc1a 262 r = user_record_clone(host, USER_RECORD_LOAD_MASK_SECRET|USER_RECORD_PERMISSIVE, ret);
70a5db58
LP
263 if (r < 0)
264 return r;
265
266 return result;
267}
268
269int 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;
0d5e5234 285 _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
70a5db58
LP
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;
70a5db58
LP
297
298 if (image_path) {
299 ip = strdup(image_path);
300 if (!ip)
301 return -ENOMEM;
0d5e5234
LP
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;
70a5db58
LP
306 }
307
308 if (home_directory) {
309 hd = strdup(home_directory);
310 if (!hd)
311 return -ENOMEM;
312 }
313
0d5e5234
LP
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
70a5db58
LP
332 r = json_build(&new_binding_entry,
333 JSON_BUILD_OBJECT(
334 JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
85b55869
LP
335 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(partition_uuid))),
336 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(luks_uuid))),
337 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(fs_uuid))),
70a5db58
LP
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 */
85b55869 354 be = json_variant_ref(json_variant_by_key(binding, SD_ID128_TO_STRING(mid)));
70a5db58
LP
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
85b55869 365 r = json_variant_set_field(&binding, SD_ID128_TO_STRING(mid), new_binding_entry);
70a5db58
LP
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);
0d5e5234
LP
378 if (ip_auto)
379 free_and_replace(h->image_path_auto, ip_auto);
70a5db58
LP
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
0d5e5234
LP
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);
70a5db58
LP
399 if (hd)
400 free_and_replace(h->home_directory, hd);
401
402 if (uid_is_valid(uid))
403 h->uid = uid;
0d5e5234
LP
404 if (gid_is_valid(gid))
405 h->gid = gid;
70a5db58
LP
406
407 h->mask |= USER_RECORD_BINDING;
408 return 1;
409}
410
411int 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
457int 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
475int 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:
9be99f81
LP
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
70a5db58 510 return USER_TEST_EXISTS;
9be99f81
LP
511 }
512
70a5db58
LP
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
162392b7 516 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
70a5db58
LP
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:
04499a70 543 assert_not_reached();
70a5db58
LP
544 }
545}
546
547int 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
87d7893c 565int user_record_test_password(UserRecord *h, UserRecord *secret) {
70a5db58
LP
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
aecbc87d
LP
587int 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
70a5db58
LP
629int 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;
70a5db58
LP
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 if (disk_size < USER_DISK_SIZE_MIN || disk_size > USER_DISK_SIZE_MAX)
643 return -ERANGE;
644
645 r = sd_id128_get_machine(&mid);
646 if (r < 0)
647 return r;
648
85b55869 649 r = json_variant_new_string(&midv, SD_ID128_TO_STRING(mid));
70a5db58
LP
650 if (r < 0)
651 return r;
652
653 r = json_variant_new_array(&midav, (JsonVariant*[]) { midv }, 1);
654 if (r < 0)
655 return r;
656
657 per_machine = json_variant_by_key(h->json, "perMachine");
658 if (per_machine) {
659 size_t i;
660
661 if (!json_variant_is_array(per_machine))
662 return -EINVAL;
663
664 n = json_variant_elements(per_machine);
665
666 array = new(JsonVariant*, n + 1);
667 if (!array)
668 return -ENOMEM;
669
670 for (i = 0; i < n; i++) {
671 JsonVariant *m;
672
673 array[i] = json_variant_by_index(per_machine, i);
674
675 if (!json_variant_is_object(array[i]))
676 return -EINVAL;
677
678 m = json_variant_by_key(array[i], "matchMachineId");
679 if (!m) {
680 /* No machineId field? Let's ignore this, but invalidate what we found so far */
681 idx = SIZE_MAX;
682 continue;
683 }
684
685 if (json_variant_equal(m, midv) ||
686 json_variant_equal(m, midav)) {
687 /* Matches exactly what we are looking for. Let's use this */
688 idx = i;
689 continue;
690 }
691
692 r = per_machine_id_match(m, JSON_PERMISSIVE);
693 if (r < 0)
694 return r;
695 if (r > 0)
696 /* Also matches what we are looking for, but with a broader match. In this
697 * case let's ignore this entry, and add a new specific one to the end. */
698 idx = SIZE_MAX;
699 }
700
701 if (idx == SIZE_MAX)
702 idx = n++; /* Nothing suitable found, place new entry at end */
703 else
704 ne = json_variant_ref(array[idx]);
705
706 } else {
707 array = new(JsonVariant*, 1);
708 if (!array)
709 return -ENOMEM;
710
711 idx = 0;
712 n = 1;
713 }
714
715 if (!ne) {
716 r = json_variant_set_field(&ne, "matchMachineId", midav);
717 if (r < 0)
718 return r;
719 }
720
721 r = json_variant_set_field_unsigned(&ne, "diskSize", disk_size);
722 if (r < 0)
723 return r;
724
725 assert(idx < n);
726 array[idx] = ne;
727
728 r = json_variant_new_array(&new_per_machine, array, n);
729 if (r < 0)
730 return r;
731
732 r = json_variant_set_field(&h->json, "perMachine", new_per_machine);
733 if (r < 0)
734 return r;
735
736 h->disk_size = disk_size;
737 h->mask |= USER_RECORD_PER_MACHINE;
738 return 0;
739}
740
741int user_record_update_last_changed(UserRecord *h, bool with_password) {
742 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
743 usec_t n;
744 int r;
745
746 assert(h);
747
748 if (!h->json)
749 return -EUNATCH;
750
751 n = now(CLOCK_REALTIME);
752
753 /* refuse downgrading */
754 if (h->last_change_usec != UINT64_MAX && h->last_change_usec >= n)
755 return -ECHRNG;
756 if (h->last_password_change_usec != UINT64_MAX && h->last_password_change_usec >= n)
757 return -ECHRNG;
758
759 v = json_variant_ref(h->json);
760
761 r = json_variant_set_field_unsigned(&v, "lastChangeUSec", n);
762 if (r < 0)
763 return r;
764
765 if (with_password) {
766 r = json_variant_set_field_unsigned(&v, "lastPasswordChangeUSec", n);
767 if (r < 0)
768 return r;
769
770 h->last_password_change_usec = n;
771 }
772
773 h->last_change_usec = n;
774
775 json_variant_unref(h->json);
776 h->json = TAKE_PTR(v);
777
778 h->mask |= USER_RECORD_REGULAR;
779 return 0;
780}
781
782int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
783 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
784 _cleanup_strv_free_ char **np = NULL;
785 char **i;
786 int r;
787
788 assert(h);
789 assert(secret);
790
791 /* Initializes the hashed password list from the specified plaintext passwords */
792
793 if (extend) {
794 np = strv_copy(h->hashed_password);
795 if (!np)
796 return -ENOMEM;
797
798 strv_uniq(np);
799 }
800
801 STRV_FOREACH(i, secret) {
0e98d17e 802 _cleanup_(erase_and_freep) char *hashed = NULL;
70a5db58 803
0e98d17e 804 r = hash_password(*i, &hashed);
70a5db58
LP
805 if (r < 0)
806 return r;
807
0e98d17e 808 r = strv_consume(&np, TAKE_PTR(hashed));
70a5db58
LP
809 if (r < 0)
810 return r;
811 }
812
813 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
814
815 if (strv_isempty(np))
816 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
817 else {
818 _cleanup_(json_variant_unrefp) JsonVariant *new_array = NULL;
819
820 r = json_variant_new_array_strv(&new_array, np);
821 if (r < 0)
822 return r;
823
824 r = json_variant_set_field(&priv, "hashedPassword", new_array);
02cec156
YW
825 if (r < 0)
826 return r;
70a5db58
LP
827 }
828
829 r = json_variant_set_field(&h->json, "privileged", priv);
830 if (r < 0)
831 return r;
832
833 strv_free_and_replace(h->hashed_password, np);
834
835 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
836 return 0;
837}
838
839int user_record_set_hashed_password(UserRecord *h, char **hashed_password) {
840 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
841 _cleanup_strv_free_ char **copy = NULL;
842 int r;
843
844 assert(h);
845
846 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
847
848 if (strv_isempty(hashed_password))
849 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
850 else {
851 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
852
853 copy = strv_copy(hashed_password);
854 if (!copy)
855 return -ENOMEM;
856
857 strv_uniq(copy);
858
859 r = json_variant_new_array_strv(&array, copy);
860 if (r < 0)
861 return r;
862
863 r = json_variant_set_field(&priv, "hashedPassword", array);
864 }
865 if (r < 0)
866 return r;
867
868 r = json_variant_set_field(&h->json, "privileged", priv);
869 if (r < 0)
870 return r;
871
872 strv_free_and_replace(h->hashed_password, copy);
873
874 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
875 return 0;
876}
877
878int user_record_set_password(UserRecord *h, char **password, bool prepend) {
879 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
880 _cleanup_(strv_free_erasep) char **e = NULL;
881 int r;
882
883 assert(h);
884
885 if (prepend) {
886 e = strv_copy(password);
887 if (!e)
888 return -ENOMEM;
889
890 r = strv_extend_strv(&e, h->password, true);
891 if (r < 0)
892 return r;
893
894 strv_uniq(e);
895
896 if (strv_equal(h->password, e))
897 return 0;
898
899 } else {
900 if (strv_equal(h->password, password))
901 return 0;
902
903 e = strv_copy(password);
904 if (!e)
905 return -ENOMEM;
906
907 strv_uniq(e);
908 }
909
910 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
911
912 if (strv_isempty(e))
913 r = json_variant_filter(&w, STRV_MAKE("password"));
914 else {
915 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
916
917 r = json_variant_new_array_strv(&l, e);
918 if (r < 0)
919 return r;
920
921 json_variant_sensitive(l);
922
923 r = json_variant_set_field(&w, "password", l);
924 }
925 if (r < 0)
926 return r;
927
560a3e5d
LP
928 json_variant_sensitive(w);
929
70a5db58
LP
930 r = json_variant_set_field(&h->json, "secret", w);
931 if (r < 0)
932 return r;
933
934 strv_free_and_replace(h->password, e);
935
936 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
937 return 0;
938}
939
c0bde0d2 940int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) {
70a5db58
LP
941 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
942 _cleanup_(strv_free_erasep) char **e = NULL;
943 int r;
944
945 assert(h);
946
947 if (prepend) {
948 e = strv_copy(pin);
949 if (!e)
950 return -ENOMEM;
951
c0bde0d2 952 r = strv_extend_strv(&e, h->token_pin, true);
70a5db58
LP
953 if (r < 0)
954 return r;
955
956 strv_uniq(e);
957
c0bde0d2 958 if (strv_equal(h->token_pin, e))
70a5db58
LP
959 return 0;
960
961 } else {
c0bde0d2 962 if (strv_equal(h->token_pin, pin))
70a5db58
LP
963 return 0;
964
965 e = strv_copy(pin);
966 if (!e)
967 return -ENOMEM;
968
969 strv_uniq(e);
970 }
971
972 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
973
974 if (strv_isempty(e))
c0bde0d2 975 r = json_variant_filter(&w, STRV_MAKE("tokenPin"));
70a5db58
LP
976 else {
977 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
978
979 r = json_variant_new_array_strv(&l, e);
980 if (r < 0)
981 return r;
982
983 json_variant_sensitive(l);
984
c0bde0d2 985 r = json_variant_set_field(&w, "tokenPin", l);
70a5db58
LP
986 }
987 if (r < 0)
988 return r;
989
560a3e5d
LP
990 json_variant_sensitive(w);
991
70a5db58
LP
992 r = json_variant_set_field(&h->json, "secret", w);
993 if (r < 0)
994 return r;
995
c0bde0d2 996 strv_free_and_replace(h->token_pin, e);
70a5db58
LP
997
998 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
999 return 0;
1000}
1001
1002int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b) {
1003 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1004 int r;
1005
1006 assert(h);
1007
1008 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1009
1010 if (b < 0)
1011 r = json_variant_filter(&w, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
1012 else
1013 r = json_variant_set_field_boolean(&w, "pkcs11ProtectedAuthenticationPathPermitted", b);
1014 if (r < 0)
1015 return r;
1016
1017 if (json_variant_is_blank_object(w))
1018 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
560a3e5d
LP
1019 else {
1020 json_variant_sensitive(w);
1021
70a5db58 1022 r = json_variant_set_field(&h->json, "secret", w);
560a3e5d 1023 }
70a5db58
LP
1024 if (r < 0)
1025 return r;
1026
1027 h->pkcs11_protected_authentication_path_permitted = b;
1028
1029 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1030 return 0;
1031}
1032
7b78db28
LP
1033int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
1034 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1035 int r;
1036
1037 assert(h);
1038
1039 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1040
1041 if (b < 0)
1042 r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted"));
1043 else
1044 r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b);
1045 if (r < 0)
1046 return r;
1047
1048 if (json_variant_is_blank_object(w))
1049 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1050 else
1051 r = json_variant_set_field(&h->json, "secret", w);
1052 if (r < 0)
1053 return r;
1054
1055 h->fido2_user_presence_permitted = b;
1056
1057 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1058 return 0;
1059}
1060
17e7561a
LP
1061int user_record_set_fido2_user_verification_permitted(UserRecord *h, int b) {
1062 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1063 int r;
1064
1065 assert(h);
1066
1067 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
1068
1069 if (b < 0)
1070 r = json_variant_filter(&w, STRV_MAKE("fido2UserVerificationPermitted"));
1071 else
1072 r = json_variant_set_field_boolean(&w, "fido2UserVerificationPermitted", b);
1073 if (r < 0)
1074 return r;
1075
1076 if (json_variant_is_blank_object(w))
1077 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1078 else
1079 r = json_variant_set_field(&h->json, "secret", w);
1080 if (r < 0)
1081 return r;
1082
1083 h->fido2_user_verification_permitted = b;
1084
1085 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1086 return 0;
1087}
1088
70a5db58
LP
1089static bool per_machine_entry_empty(JsonVariant *v) {
1090 const char *k;
1091 _unused_ JsonVariant *e;
1092
1093 JSON_VARIANT_OBJECT_FOREACH(k, e, v)
1094 if (!STR_IN_SET(k, "matchMachineId", "matchHostname"))
1095 return false;
1096
1097 return true;
1098}
1099
1100int user_record_set_password_change_now(UserRecord *h, int b) {
1101 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1102 JsonVariant *per_machine;
1103 int r;
1104
1105 assert(h);
1106
1107 w = json_variant_ref(h->json);
1108
1109 if (b < 0)
1110 r = json_variant_filter(&w, STRV_MAKE("passwordChangeNow"));
1111 else
1112 r = json_variant_set_field_boolean(&w, "passwordChangeNow", b);
1113 if (r < 0)
1114 return r;
1115
1116 /* Also drop the field from all perMachine entries */
1117 per_machine = json_variant_by_key(w, "perMachine");
1118 if (per_machine) {
1119 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1120 JsonVariant *e;
1121
1122 JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
1123 _cleanup_(json_variant_unrefp) JsonVariant *z = NULL;
1124
1125 if (!json_variant_is_object(e))
1126 return -EINVAL;
1127
1128 z = json_variant_ref(e);
1129
1130 r = json_variant_filter(&z, STRV_MAKE("passwordChangeNow"));
1131 if (r < 0)
1132 return r;
1133
1134 if (per_machine_entry_empty(z))
1135 continue;
1136
1137 r = json_variant_append_array(&array, z);
1138 if (r < 0)
1139 return r;
1140 }
1141
1142 if (json_variant_is_blank_array(array))
1143 r = json_variant_filter(&w, STRV_MAKE("perMachine"));
1144 else
1145 r = json_variant_set_field(&w, "perMachine", array);
1146 if (r < 0)
1147 return r;
1148
1149 SET_FLAG(h->mask, USER_RECORD_PER_MACHINE, !json_variant_is_blank_array(array));
1150 }
1151
1152 json_variant_unref(h->json);
1153 h->json = TAKE_PTR(w);
1154
1155 h->password_change_now = b;
1156
1157 return 0;
1158}
1159
1160int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
1161 int r;
1162
1163 assert(h);
1164
1165 /* Merges the secrets from 'secret' into 'h'. */
1166
1167 r = user_record_set_password(h, secret->password, true);
1168 if (r < 0)
1169 return r;
1170
c0bde0d2 1171 r = user_record_set_token_pin(h, secret->token_pin, true);
70a5db58
LP
1172 if (r < 0)
1173 return r;
1174
1175 if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
7b78db28
LP
1176 r = user_record_set_pkcs11_protected_authentication_path_permitted(
1177 h,
1178 secret->pkcs11_protected_authentication_path_permitted);
1179 if (r < 0)
1180 return r;
1181 }
1182
1183 if (secret->fido2_user_presence_permitted >= 0) {
1184 r = user_record_set_fido2_user_presence_permitted(
1185 h,
1186 secret->fido2_user_presence_permitted);
70a5db58
LP
1187 if (r < 0)
1188 return r;
1189 }
1190
17e7561a
LP
1191 if (secret->fido2_user_verification_permitted >= 0) {
1192 r = user_record_set_fido2_user_verification_permitted(
1193 h,
1194 secret->fido2_user_verification_permitted);
1195 if (r < 0)
1196 return r;
1197 }
1198
70a5db58
LP
1199 return 0;
1200}
1201
1202int user_record_good_authentication(UserRecord *h) {
1203 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
70a5db58
LP
1204 uint64_t counter, usec;
1205 sd_id128_t mid;
1206 int r;
1207
1208 assert(h);
1209
1210 switch (h->good_authentication_counter) {
1211 case UINT64_MAX:
1212 counter = 1;
1213 break;
1214 case UINT64_MAX-1:
1215 counter = h->good_authentication_counter; /* saturate */
1216 break;
1217 default:
1218 counter = h->good_authentication_counter + 1;
1219 break;
1220 }
1221
1222 usec = now(CLOCK_REALTIME);
1223
1224 r = sd_id128_get_machine(&mid);
1225 if (r < 0)
1226 return r;
1227
1228 v = json_variant_ref(h->json);
1229 w = json_variant_ref(json_variant_by_key(v, "status"));
85b55869 1230 z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
70a5db58
LP
1231
1232 r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
1233 if (r < 0)
1234 return r;
1235
1236 r = json_variant_set_field_unsigned(&z, "lastGoodAuthenticationUSec", usec);
1237 if (r < 0)
1238 return r;
1239
85b55869 1240 r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
70a5db58
LP
1241 if (r < 0)
1242 return r;
1243
1244 r = json_variant_set_field(&v, "status", w);
1245 if (r < 0)
1246 return r;
1247
1248 json_variant_unref(h->json);
1249 h->json = TAKE_PTR(v);
1250
1251 h->good_authentication_counter = counter;
1252 h->last_good_authentication_usec = usec;
1253
1254 h->mask |= USER_RECORD_STATUS;
1255 return 0;
1256}
1257
1258int user_record_bad_authentication(UserRecord *h) {
1259 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
70a5db58
LP
1260 uint64_t counter, usec;
1261 sd_id128_t mid;
1262 int r;
1263
1264 assert(h);
1265
1266 switch (h->bad_authentication_counter) {
1267 case UINT64_MAX:
1268 counter = 1;
1269 break;
1270 case UINT64_MAX-1:
1271 counter = h->bad_authentication_counter; /* saturate */
1272 break;
1273 default:
1274 counter = h->bad_authentication_counter + 1;
1275 break;
1276 }
1277
1278 usec = now(CLOCK_REALTIME);
1279
1280 r = sd_id128_get_machine(&mid);
1281 if (r < 0)
1282 return r;
1283
1284 v = json_variant_ref(h->json);
1285 w = json_variant_ref(json_variant_by_key(v, "status"));
85b55869 1286 z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
70a5db58
LP
1287
1288 r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
1289 if (r < 0)
1290 return r;
1291
1292 r = json_variant_set_field_unsigned(&z, "lastBadAuthenticationUSec", usec);
1293 if (r < 0)
1294 return r;
1295
85b55869 1296 r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
70a5db58
LP
1297 if (r < 0)
1298 return r;
1299
1300 r = json_variant_set_field(&v, "status", w);
1301 if (r < 0)
1302 return r;
1303
1304 json_variant_unref(h->json);
1305 h->json = TAKE_PTR(v);
1306
1307 h->bad_authentication_counter = counter;
1308 h->last_bad_authentication_usec = usec;
1309
1310 h->mask |= USER_RECORD_STATUS;
1311 return 0;
1312}
1313
1314int user_record_ratelimit(UserRecord *h) {
1315 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1316 usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
70a5db58
LP
1317 sd_id128_t mid;
1318 int r;
1319
1320 assert(h);
1321
1322 usec = now(CLOCK_REALTIME);
1323
61a29a02
LP
1324 if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec) {
1325 /* Hmm, start-time is after the current time? If so, the RTC most likely doesn't work. */
1326 new_ratelimit_begin_usec = usec;
1327 new_ratelimit_count = 1;
1328 log_debug("Rate limit timestamp is in the future, assuming incorrect system clock, resetting limit.");
1329 } else if (h->ratelimit_begin_usec == UINT64_MAX ||
70a5db58
LP
1330 usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
1331 /* Fresh start */
1332 new_ratelimit_begin_usec = usec;
1333 new_ratelimit_count = 1;
1334 } else if (h->ratelimit_count < user_record_ratelimit_burst(h)) {
1335 /* Count up */
1336 new_ratelimit_begin_usec = h->ratelimit_begin_usec;
1337 new_ratelimit_count = h->ratelimit_count + 1;
1338 } else
1339 /* Limit hit */
1340 return 0;
1341
1342 r = sd_id128_get_machine(&mid);
1343 if (r < 0)
1344 return r;
1345
1346 v = json_variant_ref(h->json);
1347 w = json_variant_ref(json_variant_by_key(v, "status"));
85b55869 1348 z = json_variant_ref(json_variant_by_key(w, SD_ID128_TO_STRING(mid)));
70a5db58
LP
1349
1350 r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
1351 if (r < 0)
1352 return r;
1353
1354 r = json_variant_set_field_unsigned(&z, "rateLimitCount", new_ratelimit_count);
1355 if (r < 0)
1356 return r;
1357
85b55869 1358 r = json_variant_set_field(&w, SD_ID128_TO_STRING(mid), z);
70a5db58
LP
1359 if (r < 0)
1360 return r;
1361
1362 r = json_variant_set_field(&v, "status", w);
1363 if (r < 0)
1364 return r;
1365
1366 json_variant_unref(h->json);
1367 h->json = TAKE_PTR(v);
1368
1369 h->ratelimit_begin_usec = new_ratelimit_begin_usec;
1370 h->ratelimit_count = new_ratelimit_count;
1371
1372 h->mask |= USER_RECORD_STATUS;
1373 return 1;
1374}
1375
1376int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
1377 assert(hr);
1378
1379 if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
1b09b81c 1380 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
70a5db58
LP
1381
1382 if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
1b09b81c 1383 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
70a5db58
LP
1384
1385 if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
1b09b81c 1386 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
70a5db58
LP
1387
1388 if (hr->service && !streq(hr->service, "io.systemd.Home"))
1b09b81c 1389 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
70a5db58
LP
1390
1391 return 0;
1392}