1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "errno-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"
17 int user_record_synthesize(
19 const char *user_name
,
21 const char *image_path
,
26 _cleanup_free_
char *hd
= NULL
, *un
= NULL
, *ip
= NULL
, *rr
= NULL
, *user_name_and_realm
= NULL
;
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
));
37 /* Fill in a home record from just a username and an image path. */
42 if (!suitable_user_name(user_name
))
46 r
= suitable_realm(realm
);
53 if (!suitable_image_path(image_path
))
56 r
= sd_id128_get_machine(&mid
);
60 un
= strdup(user_name
);
69 user_name_and_realm
= strjoin(user_name
, "@", realm
);
70 if (!user_name_and_realm
)
74 ip
= strdup(image_path
);
78 hd
= path_join(get_home_root(), user_name
);
82 r
= json_build(&h
->json
,
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
))))))));
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
;
105 h
->mask
= USER_RECORD_REGULAR
|USER_RECORD_BINDING
;
109 int group_record_synthesize(GroupRecord
*g
, UserRecord
*h
) {
110 _cleanup_free_
char *un
= NULL
, *rr
= NULL
, *group_name_and_realm
= NULL
, *description
= NULL
;
120 r
= sd_id128_get_machine(&mid
);
124 un
= strdup(h
->user_name
);
129 rr
= strdup(h
->realm
);
133 group_name_and_realm
= strjoin(un
, "@", rr
);
134 if (!group_name_and_realm
)
138 description
= strjoin("Primary Group of User ", un
);
142 r
= json_build(&g
->json
,
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"))))))));
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
;
163 g
->mask
= USER_RECORD_REGULAR
|USER_RECORD_BINDING
;
167 int user_record_reconcile(
169 UserRecord
*embedded
,
170 UserReconcileMode mode
,
175 /* Reconciles the identity record stored on the host with the one embedded in a $HOME
176 * directory. Returns the following error codes:
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
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. */
188 /* Make sure both records are initialized */
189 if (!host
->json
|| !embedded
->json
)
192 /* Ensure these records actually contain user data */
193 if (!(embedded
->mask
& host
->mask
& USER_RECORD_REGULAR
))
196 /* Make sure the user name and realm matches */
197 if (!user_record_compatible(host
, embedded
))
200 /* Embedded identities may not contain secrets or binding info */
201 if ((embedded
->mask
& (USER_RECORD_SECRET
|USER_RECORD_BINDING
)) != 0)
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
))
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
;
214 else if (host
->last_change_usec
== embedded
->last_change_usec
) {
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. */
219 r
= user_record_masked_equal(host
, embedded
, USER_RECORD_REGULAR
|USER_RECORD_PRIVILEGED
|USER_RECORD_PER_MACHINE
);
223 if (mode
== USER_RECONCILE_REQUIRE_NEWER
)
226 result
= USER_RECONCILE_IDENTICAL
;
228 result
= USER_RECONCILE_HOST_WON
;
230 _cleanup_(json_variant_unrefp
) JsonVariant
*extended
= NULL
;
231 _cleanup_(user_record_unrefp
) UserRecord
*merged
= NULL
;
234 /* The embedded version is newer */
236 if (mode
== USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL
)
239 /* Copy in the binding data */
240 extended
= json_variant_ref(embedded
->json
);
242 e
= json_variant_by_key(host
->json
, "binding");
244 r
= json_variant_set_field(&extended
, "binding", e
);
249 merged
= user_record_new();
253 r
= user_record_load(merged
, extended
, USER_RECORD_LOAD_MASK_SECRET
|USER_RECORD_PERMISSIVE
);
257 *ret
= TAKE_PTR(merged
);
258 return USER_RECONCILE_EMBEDDED_WON
; /* update */
261 /* Strip out secrets */
262 r
= user_record_clone(host
, USER_RECORD_LOAD_MASK_SECRET
|USER_RECORD_PERMISSIVE
, ret
);
269 int user_record_add_binding(
272 const char *image_path
,
273 sd_id128_t partition_uuid
,
274 sd_id128_t luks_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
,
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
;
294 r
= sd_id128_get_machine(&mid
);
299 ip
= strdup(image_path
);
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
);
308 if (home_directory
) {
309 hd
= strdup(home_directory
);
314 if (file_system_type
) {
315 fst
= strdup(file_system_type
);
321 lc
= strdup(luks_cipher
);
326 if (luks_cipher_mode
) {
327 lcm
= strdup(luks_cipher_mode
);
332 r
= json_build(&new_binding_entry
,
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
)))));
349 binding
= json_variant_ref(json_variant_by_key(h
->json
, "binding"));
351 _cleanup_(json_variant_unrefp
) JsonVariant
*be
= NULL
;
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
)));
356 r
= json_variant_merge(&be
, new_binding_entry
);
360 json_variant_unref(new_binding_entry
);
361 new_binding_entry
= TAKE_PTR(be
);
365 r
= json_variant_set_field(&binding
, SD_ID128_TO_STRING(mid
), new_binding_entry
);
369 r
= json_variant_set_field(&h
->json
, "binding", binding
);
374 h
->storage
= storage
;
377 free_and_replace(h
->image_path
, ip
);
379 free_and_replace(h
->image_path_auto
, ip_auto
);
381 if (!sd_id128_is_null(partition_uuid
))
382 h
->partition_uuid
= partition_uuid
;
384 if (!sd_id128_is_null(luks_uuid
))
385 h
->luks_uuid
= luks_uuid
;
387 if (!sd_id128_is_null(fs_uuid
))
388 h
->file_system_uuid
= fs_uuid
;
391 free_and_replace(h
->luks_cipher
, lc
);
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
;
398 free_and_replace(h
->file_system_type
, fst
);
400 free_and_replace(h
->home_directory
, hd
);
402 if (uid_is_valid(uid
))
404 if (gid_is_valid(gid
))
407 h
->mask
|= USER_RECORD_BINDING
;
411 int user_record_test_home_directory(UserRecord
*h
) {
417 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
419 hd
= user_record_home_directory(h
);
423 r
= is_dir(hd
, false);
425 return USER_TEST_ABSENT
;
431 r
= path_is_mount_point(hd
, NULL
, 0);
435 return USER_TEST_MOUNTED
;
437 /* If the image path and the home directory are identical, then it's OK if the directory is
439 if (IN_SET(user_record_storage(h
), USER_CLASSIC
, USER_DIRECTORY
, USER_SUBVOLUME
, USER_FSCRYPT
)) {
442 ip
= user_record_image_path(h
);
443 if (ip
&& path_equal(ip
, hd
))
444 return USER_TEST_EXISTS
;
447 /* Otherwise it's not OK */
448 r
= dir_is_empty(hd
);
454 return USER_TEST_EXISTS
;
457 int user_record_test_home_directory_and_warn(UserRecord
*h
) {
462 r
= user_record_test_home_directory(h
);
464 return log_error_errno(r
, "User record lacks home directory, refusing.");
466 return log_error_errno(r
, "Home directory %s is not a directory, refusing.", user_record_home_directory(h
));
468 return log_error_errno(r
, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h
));
470 return log_error_errno(r
, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h
));
475 int user_record_test_image_path(UserRecord
*h
) {
481 if (user_record_storage(h
) == USER_CIFS
)
482 return USER_TEST_UNDEFINED
;
484 ip
= user_record_image_path(h
);
488 if (stat(ip
, &st
) < 0) {
490 return USER_TEST_ABSENT
;
495 switch (user_record_storage(h
)) {
498 if (S_ISREG(st
.st_mode
)) {
502 n
= getxattr(ip
, "user.home-dirty", x
, sizeof(x
));
504 if (errno
!= ENODATA
)
505 log_debug_errno(errno
, "Unable to read dirty xattr off image file, ignoring: %m");
507 } else if (n
== 1 && x
[0] == '1')
508 return USER_TEST_DIRTY
;
510 return USER_TEST_EXISTS
;
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. */
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
;
528 return USER_TEST_MAYBE
;
537 if (S_ISDIR(st
.st_mode
))
538 return USER_TEST_EXISTS
;
543 assert_not_reached();
547 int user_record_test_image_path_and_warn(UserRecord
*h
) {
552 r
= user_record_test_image_path(h
);
554 return log_error_errno(r
, "User record lacks image path, refusing.");
556 return log_error_errno(r
, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h
));
558 return log_error_errno(r
, "Image path %s is not a directory, refusing.", user_record_image_path(h
));
560 return log_error_errno(r
, "Failed to test whether image path %s exists: %m", user_record_image_path(h
));
565 int user_record_test_password(UserRecord
*h
, UserRecord
*secret
) {
571 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
573 if (strv_isempty(h
->hashed_password
))
576 STRV_FOREACH(i
, secret
->password
) {
577 r
= test_password_many(h
->hashed_password
, *i
);
587 int user_record_test_recovery_key(UserRecord
*h
, UserRecord
*secret
) {
593 /* Checks whether any of the specified passwords matches any of the hashed recovery keys of the entry */
595 if (h
->n_recovery_key
== 0)
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
;
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. */
608 r
= normalize_recovery_key(*i
, &mangled
);
609 if (r
== -EINVAL
) /* Not a valid modhex64 passphrase, don't bother */
616 p
= *i
; /* Unknown recovery key types process as is */
618 r
= test_password_one(h
->recovery_key
[j
].hashed_password
, p
);
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
;
642 r
= sd_id128_get_machine(&mid
);
646 r
= json_variant_new_string(&midv
, SD_ID128_TO_STRING(mid
));
650 r
= json_variant_new_array(&midav
, (JsonVariant
*[]) { midv
}, 1);
654 per_machine
= json_variant_by_key(h
->json
, "perMachine");
658 if (!json_variant_is_array(per_machine
))
661 n
= json_variant_elements(per_machine
);
663 array
= new(JsonVariant
*, n
+ 1);
667 for (i
= 0; i
< n
; i
++) {
670 array
[i
] = json_variant_by_index(per_machine
, i
);
672 if (!json_variant_is_object(array
[i
]))
675 m
= json_variant_by_key(array
[i
], "matchMachineId");
677 /* No machineId field? Let's ignore this, but invalidate what we found so far */
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 */
689 r
= per_machine_id_match(m
, JSON_PERMISSIVE
);
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. */
699 idx
= n
++; /* Nothing suitable found, place new entry at end */
701 ne
= json_variant_ref(array
[idx
]);
704 array
= new(JsonVariant
*, 1);
713 r
= json_variant_set_field(&ne
, "matchMachineId", midav
);
718 r
= json_variant_set_field_unsigned(&ne
, "diskSize", disk_size
);
725 r
= json_variant_new_array(&new_per_machine
, array
, n
);
729 r
= json_variant_set_field(&h
->json
, "perMachine", new_per_machine
);
733 h
->disk_size
= disk_size
;
734 h
->mask
|= USER_RECORD_PER_MACHINE
;
738 int user_record_update_last_changed(UserRecord
*h
, bool with_password
) {
739 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
748 n
= now(CLOCK_REALTIME
);
750 /* refuse downgrading */
751 if (h
->last_change_usec
!= UINT64_MAX
&& h
->last_change_usec
>= n
)
753 if (h
->last_password_change_usec
!= UINT64_MAX
&& h
->last_password_change_usec
>= n
)
756 v
= json_variant_ref(h
->json
);
758 r
= json_variant_set_field_unsigned(&v
, "lastChangeUSec", n
);
763 r
= json_variant_set_field_unsigned(&v
, "lastPasswordChangeUSec", n
);
767 h
->last_password_change_usec
= n
;
770 h
->last_change_usec
= n
;
772 json_variant_unref(h
->json
);
773 h
->json
= TAKE_PTR(v
);
775 h
->mask
|= USER_RECORD_REGULAR
;
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
;
788 /* Initializes the hashed password list from the specified plaintext passwords */
791 np
= strv_copy(h
->hashed_password
);
798 STRV_FOREACH(i
, secret
) {
799 _cleanup_(erase_and_freep
) char *hashed
= NULL
;
801 r
= hash_password(*i
, &hashed
);
805 r
= strv_consume(&np
, TAKE_PTR(hashed
));
810 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
812 if (strv_isempty(np
))
813 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
815 _cleanup_(json_variant_unrefp
) JsonVariant
*new_array
= NULL
;
817 r
= json_variant_new_array_strv(&new_array
, np
);
821 r
= json_variant_set_field(&priv
, "hashedPassword", new_array
);
826 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
830 strv_free_and_replace(h
->hashed_password
, np
);
832 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
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
;
843 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
845 if (strv_isempty(hashed_password
))
846 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
848 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
850 copy
= strv_copy(hashed_password
);
856 r
= json_variant_new_array_strv(&array
, copy
);
860 r
= json_variant_set_field(&priv
, "hashedPassword", array
);
865 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
869 strv_free_and_replace(h
->hashed_password
, copy
);
871 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
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
;
883 e
= strv_copy(password
);
887 r
= strv_extend_strv(&e
, h
->password
, true);
893 if (strv_equal(h
->password
, e
))
897 if (strv_equal(h
->password
, password
))
900 e
= strv_copy(password
);
907 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
910 r
= json_variant_filter(&w
, STRV_MAKE("password"));
912 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
914 r
= json_variant_new_array_strv(&l
, e
);
918 json_variant_sensitive(l
);
920 r
= json_variant_set_field(&w
, "password", l
);
925 json_variant_sensitive(w
);
927 r
= json_variant_set_field(&h
->json
, "secret", w
);
931 strv_free_and_replace(h
->password
, e
);
933 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
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
;
949 r
= strv_extend_strv(&e
, h
->token_pin
, true);
955 if (strv_equal(h
->token_pin
, e
))
959 if (strv_equal(h
->token_pin
, pin
))
969 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
972 r
= json_variant_filter(&w
, STRV_MAKE("tokenPin"));
974 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
976 r
= json_variant_new_array_strv(&l
, e
);
980 json_variant_sensitive(l
);
982 r
= json_variant_set_field(&w
, "tokenPin", l
);
987 json_variant_sensitive(w
);
989 r
= json_variant_set_field(&h
->json
, "secret", w
);
993 strv_free_and_replace(h
->token_pin
, e
);
995 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
999 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord
*h
, int b
) {
1000 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1005 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
1008 r
= json_variant_filter(&w
, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
1010 r
= json_variant_set_field_boolean(&w
, "pkcs11ProtectedAuthenticationPathPermitted", b
);
1014 if (json_variant_is_blank_object(w
))
1015 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
1017 json_variant_sensitive(w
);
1019 r
= json_variant_set_field(&h
->json
, "secret", w
);
1024 h
->pkcs11_protected_authentication_path_permitted
= b
;
1026 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1030 int user_record_set_fido2_user_presence_permitted(UserRecord
*h
, int b
) {
1031 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1036 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
1039 r
= json_variant_filter(&w
, STRV_MAKE("fido2UserPresencePermitted"));
1041 r
= json_variant_set_field_boolean(&w
, "fido2UserPresencePermitted", b
);
1045 if (json_variant_is_blank_object(w
))
1046 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
1048 r
= json_variant_set_field(&h
->json
, "secret", w
);
1052 h
->fido2_user_presence_permitted
= b
;
1054 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1058 int user_record_set_fido2_user_verification_permitted(UserRecord
*h
, int b
) {
1059 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1064 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
1067 r
= json_variant_filter(&w
, STRV_MAKE("fido2UserVerificationPermitted"));
1069 r
= json_variant_set_field_boolean(&w
, "fido2UserVerificationPermitted", b
);
1073 if (json_variant_is_blank_object(w
))
1074 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
1076 r
= json_variant_set_field(&h
->json
, "secret", w
);
1080 h
->fido2_user_verification_permitted
= b
;
1082 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1086 static bool per_machine_entry_empty(JsonVariant
*v
) {
1088 _unused_ JsonVariant
*e
;
1090 JSON_VARIANT_OBJECT_FOREACH(k
, e
, v
)
1091 if (!STR_IN_SET(k
, "matchMachineId", "matchHostname"))
1097 int user_record_set_password_change_now(UserRecord
*h
, int b
) {
1098 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1099 JsonVariant
*per_machine
;
1104 w
= json_variant_ref(h
->json
);
1107 r
= json_variant_filter(&w
, STRV_MAKE("passwordChangeNow"));
1109 r
= json_variant_set_field_boolean(&w
, "passwordChangeNow", b
);
1113 /* Also drop the field from all perMachine entries */
1114 per_machine
= json_variant_by_key(w
, "perMachine");
1116 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
1119 JSON_VARIANT_ARRAY_FOREACH(e
, per_machine
) {
1120 _cleanup_(json_variant_unrefp
) JsonVariant
*z
= NULL
;
1122 if (!json_variant_is_object(e
))
1125 z
= json_variant_ref(e
);
1127 r
= json_variant_filter(&z
, STRV_MAKE("passwordChangeNow"));
1131 if (per_machine_entry_empty(z
))
1134 r
= json_variant_append_array(&array
, z
);
1139 if (json_variant_is_blank_array(array
))
1140 r
= json_variant_filter(&w
, STRV_MAKE("perMachine"));
1142 r
= json_variant_set_field(&w
, "perMachine", array
);
1146 SET_FLAG(h
->mask
, USER_RECORD_PER_MACHINE
, !json_variant_is_blank_array(array
));
1149 json_variant_unref(h
->json
);
1150 h
->json
= TAKE_PTR(w
);
1152 h
->password_change_now
= b
;
1157 int user_record_merge_secret(UserRecord
*h
, UserRecord
*secret
) {
1162 /* Merges the secrets from 'secret' into 'h'. */
1164 r
= user_record_set_password(h
, secret
->password
, true);
1168 r
= user_record_set_token_pin(h
, secret
->token_pin
, true);
1172 if (secret
->pkcs11_protected_authentication_path_permitted
>= 0) {
1173 r
= user_record_set_pkcs11_protected_authentication_path_permitted(
1175 secret
->pkcs11_protected_authentication_path_permitted
);
1180 if (secret
->fido2_user_presence_permitted
>= 0) {
1181 r
= user_record_set_fido2_user_presence_permitted(
1183 secret
->fido2_user_presence_permitted
);
1188 if (secret
->fido2_user_verification_permitted
>= 0) {
1189 r
= user_record_set_fido2_user_verification_permitted(
1191 secret
->fido2_user_verification_permitted
);
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
;
1207 switch (h
->good_authentication_counter
) {
1212 counter
= h
->good_authentication_counter
; /* saturate */
1215 counter
= h
->good_authentication_counter
+ 1;
1219 usec
= now(CLOCK_REALTIME
);
1221 r
= sd_id128_get_machine(&mid
);
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
)));
1229 r
= json_variant_set_field_unsigned(&z
, "goodAuthenticationCounter", counter
);
1233 r
= json_variant_set_field_unsigned(&z
, "lastGoodAuthenticationUSec", usec
);
1237 r
= json_variant_set_field(&w
, SD_ID128_TO_STRING(mid
), z
);
1241 r
= json_variant_set_field(&v
, "status", w
);
1245 json_variant_unref(h
->json
);
1246 h
->json
= TAKE_PTR(v
);
1248 h
->good_authentication_counter
= counter
;
1249 h
->last_good_authentication_usec
= usec
;
1251 h
->mask
|= USER_RECORD_STATUS
;
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
;
1263 switch (h
->bad_authentication_counter
) {
1268 counter
= h
->bad_authentication_counter
; /* saturate */
1271 counter
= h
->bad_authentication_counter
+ 1;
1275 usec
= now(CLOCK_REALTIME
);
1277 r
= sd_id128_get_machine(&mid
);
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
)));
1285 r
= json_variant_set_field_unsigned(&z
, "badAuthenticationCounter", counter
);
1289 r
= json_variant_set_field_unsigned(&z
, "lastBadAuthenticationUSec", usec
);
1293 r
= json_variant_set_field(&w
, SD_ID128_TO_STRING(mid
), z
);
1297 r
= json_variant_set_field(&v
, "status", w
);
1301 json_variant_unref(h
->json
);
1302 h
->json
= TAKE_PTR(v
);
1304 h
->bad_authentication_counter
= counter
;
1305 h
->last_bad_authentication_usec
= usec
;
1307 h
->mask
|= USER_RECORD_STATUS
;
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
;
1319 usec
= now(CLOCK_REALTIME
);
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
) {
1329 new_ratelimit_begin_usec
= usec
;
1330 new_ratelimit_count
= 1;
1331 } else if (h
->ratelimit_count
< user_record_ratelimit_burst(h
)) {
1333 new_ratelimit_begin_usec
= h
->ratelimit_begin_usec
;
1334 new_ratelimit_count
= h
->ratelimit_count
+ 1;
1339 r
= sd_id128_get_machine(&mid
);
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
)));
1347 r
= json_variant_set_field_unsigned(&z
, "rateLimitBeginUSec", new_ratelimit_begin_usec
);
1351 r
= json_variant_set_field_unsigned(&z
, "rateLimitCount", new_ratelimit_count
);
1355 r
= json_variant_set_field(&w
, SD_ID128_TO_STRING(mid
), z
);
1359 r
= json_variant_set_field(&v
, "status", w
);
1363 json_variant_unref(h
->json
);
1364 h
->json
= TAKE_PTR(v
);
1366 h
->ratelimit_begin_usec
= new_ratelimit_begin_usec
;
1367 h
->ratelimit_count
= new_ratelimit_count
;
1369 h
->mask
|= USER_RECORD_STATUS
;
1373 int user_record_is_supported(UserRecord
*hr
, sd_bus_error
*error
) {
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.");
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.");
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.");
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.");
1391 bool user_record_shall_rebalance(UserRecord
*h
) {
1394 if (user_record_rebalance_weight(h
) == REBALANCE_WEIGHT_OFF
)
1397 if (user_record_storage(h
) != USER_LUKS
)
1400 if (!path_startswith(user_record_image_path(h
), get_home_root())) /* This is the only pool we rebalance in */
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
;
1420 r
= sd_id128_get_machine(&mid
);
1424 r
= json_variant_new_id128(&machine_id_variant
, mid
);
1428 r
= json_variant_new_array(&machine_id_array
, (JsonVariant
*[]) { machine_id_variant
}, 1);
1432 per_machine
= json_variant_by_key(h
->json
, "perMachine");
1434 if (!json_variant_is_array(per_machine
))
1437 n
= json_variant_elements(per_machine
);
1439 array
= new(JsonVariant
*, n
+ 1);
1443 for (size_t i
= 0; i
< n
; i
++) {
1446 array
[i
] = json_variant_by_index(per_machine
, i
);
1448 if (!json_variant_is_object(array
[i
]))
1451 m
= json_variant_by_key(array
[i
], "matchMachineId");
1453 /* No machineId field? Let's ignore this, but invalidate what we found so far */
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 */
1465 r
= per_machine_id_match(m
, JSON_PERMISSIVE
);
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. */
1474 if (idx
== SIZE_MAX
)
1475 idx
= n
++; /* Nothing suitable found, place new entry at end */
1477 per_machine_entry
= json_variant_ref(array
[idx
]);
1480 array
= new(JsonVariant
*, 1);
1488 if (!per_machine_entry
) {
1489 r
= json_variant_set_field(&per_machine_entry
, "matchMachineId", machine_id_array
);
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) */
1497 r
= json_variant_set_field_unsigned(&per_machine_entry
, "rebalanceWeight", weight
);
1502 array
[idx
] = per_machine_entry
;
1504 r
= json_variant_new_array(&new_per_machine_array
, array
, n
);
1508 r
= json_variant_set_field(&h
->json
, "perMachine", new_per_machine_array
);
1512 h
->rebalance_weight
= weight
;
1513 h
->mask
|= USER_RECORD_PER_MACHINE
;