1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "errno-util.h"
5 #include "id128-util.h"
6 #include "libcrypt-util.h"
7 #include "memory-util.h"
9 #include "mountpoint-util.h"
10 #include "path-util.h"
11 #include "stat-util.h"
12 #include "user-record-util.h"
13 #include "user-util.h"
15 int user_record_synthesize(
17 const char *user_name
,
19 const char *image_path
,
24 _cleanup_free_
char *hd
= NULL
, *un
= NULL
, *ip
= NULL
, *rr
= NULL
, *user_name_and_realm
= NULL
;
25 char smid
[SD_ID128_STRING_MAX
];
32 assert(IN_SET(storage
, USER_LUKS
, USER_SUBVOLUME
, USER_FSCRYPT
, USER_DIRECTORY
));
33 assert(uid_is_valid(uid
));
34 assert(gid_is_valid(gid
));
36 /* Fill in a home record from just a username and an image path. */
41 if (!suitable_user_name(user_name
))
45 r
= suitable_realm(realm
);
52 if (!suitable_image_path(image_path
))
55 r
= sd_id128_get_machine(&mid
);
59 un
= strdup(user_name
);
68 user_name_and_realm
= strjoin(user_name
, "@", realm
);
69 if (!user_name_and_realm
)
73 ip
= strdup(image_path
);
77 hd
= path_join("/home/", user_name
);
81 r
= json_build(&h
->json
,
83 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name
)),
84 JSON_BUILD_PAIR_CONDITION(!!rr
, "realm", JSON_BUILD_STRING(realm
)),
85 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
86 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
87 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
88 JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path
)),
89 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd
)),
90 JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage
))),
91 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
)),
92 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))))))));
96 free_and_replace(h
->user_name
, un
);
97 free_and_replace(h
->realm
, rr
);
98 free_and_replace(h
->user_name_and_realm_auto
, user_name_and_realm
);
99 free_and_replace(h
->image_path
, ip
);
100 free_and_replace(h
->home_directory
, hd
);
101 h
->storage
= storage
;
104 h
->mask
= USER_RECORD_REGULAR
|USER_RECORD_BINDING
;
108 int group_record_synthesize(GroupRecord
*g
, UserRecord
*h
) {
109 _cleanup_free_
char *un
= NULL
, *rr
= NULL
, *group_name_and_realm
= NULL
, *description
= NULL
;
110 char smid
[SD_ID128_STRING_MAX
];
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
, smid
), JSON_BUILD_OBJECT(
149 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h
))))))),
150 JSON_BUILD_PAIR_CONDITION(h
->disposition
>= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h
)))),
151 JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
152 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
153 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
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
);
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
, 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 char smid
[SD_ID128_STRING_MAX
], partition_uuids
[ID128_UUID_STRING_MAX
], luks_uuids
[ID128_UUID_STRING_MAX
], fs_uuids
[ID128_UUID_STRING_MAX
];
286 _cleanup_free_
char *ip
= NULL
, *hd
= NULL
, *ip_auto
= NULL
, *lc
= NULL
, *lcm
= NULL
, *fst
= NULL
;
295 r
= sd_id128_get_machine(&mid
);
298 sd_id128_to_string(mid
, smid
);
301 ip
= strdup(image_path
);
304 } else if (!h
->image_path
&& storage
>= 0) {
305 r
= user_record_build_image_path(storage
, user_record_user_name_and_realm(h
), &ip_auto
);
310 if (home_directory
) {
311 hd
= strdup(home_directory
);
316 if (file_system_type
) {
317 fst
= strdup(file_system_type
);
323 lc
= strdup(luks_cipher
);
328 if (luks_cipher_mode
) {
329 lcm
= strdup(luks_cipher_mode
);
334 r
= json_build(&new_binding_entry
,
336 JSON_BUILD_PAIR_CONDITION(!!image_path
, "imagePath", JSON_BUILD_STRING(image_path
)),
337 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid
), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid
, partition_uuids
))),
338 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid
), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid
, luks_uuids
))),
339 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid
), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid
, fs_uuids
))),
340 JSON_BUILD_PAIR_CONDITION(!!luks_cipher
, "luksCipher", JSON_BUILD_STRING(luks_cipher
)),
341 JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode
, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode
)),
342 JSON_BUILD_PAIR_CONDITION(luks_volume_key_size
!= UINT64_MAX
, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size
)),
343 JSON_BUILD_PAIR_CONDITION(!!file_system_type
, "fileSystemType", JSON_BUILD_STRING(file_system_type
)),
344 JSON_BUILD_PAIR_CONDITION(!!home_directory
, "homeDirectory", JSON_BUILD_STRING(home_directory
)),
345 JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid
), "uid", JSON_BUILD_UNSIGNED(uid
)),
346 JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid
), "gid", JSON_BUILD_UNSIGNED(gid
)),
347 JSON_BUILD_PAIR_CONDITION(storage
>= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage
)))));
351 binding
= json_variant_ref(json_variant_by_key(h
->json
, "binding"));
353 _cleanup_(json_variant_unrefp
) JsonVariant
*be
= NULL
;
355 /* Merge the new entry with an old one, if that exists */
356 be
= json_variant_ref(json_variant_by_key(binding
, smid
));
358 r
= json_variant_merge(&be
, new_binding_entry
);
362 json_variant_unref(new_binding_entry
);
363 new_binding_entry
= TAKE_PTR(be
);
367 r
= json_variant_set_field(&binding
, smid
, new_binding_entry
);
371 r
= json_variant_set_field(&h
->json
, "binding", binding
);
376 h
->storage
= storage
;
379 free_and_replace(h
->image_path
, ip
);
381 free_and_replace(h
->image_path_auto
, ip_auto
);
383 if (!sd_id128_is_null(partition_uuid
))
384 h
->partition_uuid
= partition_uuid
;
386 if (!sd_id128_is_null(luks_uuid
))
387 h
->luks_uuid
= luks_uuid
;
389 if (!sd_id128_is_null(fs_uuid
))
390 h
->file_system_uuid
= fs_uuid
;
393 free_and_replace(h
->luks_cipher
, lc
);
395 free_and_replace(h
->luks_cipher_mode
, lcm
);
396 if (luks_volume_key_size
!= UINT64_MAX
)
397 h
->luks_volume_key_size
= luks_volume_key_size
;
400 free_and_replace(h
->file_system_type
, fst
);
402 free_and_replace(h
->home_directory
, hd
);
404 if (uid_is_valid(uid
))
406 if (gid_is_valid(gid
))
409 h
->mask
|= USER_RECORD_BINDING
;
413 int user_record_test_home_directory(UserRecord
*h
) {
419 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
421 hd
= user_record_home_directory(h
);
425 r
= is_dir(hd
, false);
427 return USER_TEST_ABSENT
;
433 r
= path_is_mount_point(hd
, NULL
, 0);
437 return USER_TEST_MOUNTED
;
439 /* If the image path and the home directory are identical, then it's OK if the directory is
441 if (IN_SET(user_record_storage(h
), USER_CLASSIC
, USER_DIRECTORY
, USER_SUBVOLUME
, USER_FSCRYPT
)) {
444 ip
= user_record_image_path(h
);
445 if (ip
&& path_equal(ip
, hd
))
446 return USER_TEST_EXISTS
;
449 /* Otherwise it's not OK */
450 r
= dir_is_empty(hd
);
456 return USER_TEST_EXISTS
;
459 int user_record_test_home_directory_and_warn(UserRecord
*h
) {
464 r
= user_record_test_home_directory(h
);
466 return log_error_errno(r
, "User record lacks home directory, refusing.");
468 return log_error_errno(r
, "Home directory %s is not a directory, refusing.", user_record_home_directory(h
));
470 return log_error_errno(r
, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h
));
472 return log_error_errno(r
, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h
));
477 int user_record_test_image_path(UserRecord
*h
) {
483 if (user_record_storage(h
) == USER_CIFS
)
484 return USER_TEST_UNDEFINED
;
486 ip
= user_record_image_path(h
);
490 if (stat(ip
, &st
) < 0) {
492 return USER_TEST_ABSENT
;
497 switch (user_record_storage(h
)) {
500 if (S_ISREG(st
.st_mode
))
501 return USER_TEST_EXISTS
;
502 if (S_ISBLK(st
.st_mode
)) {
503 /* For block devices we can't really be sure if the device referenced actually is the
504 * fs we look for or some other file system (think: what does /dev/sdb1 refer
505 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
506 * case, except if the device path used is one of the paths that is based on a
507 * filesystem or partition UUID or label, because in those cases we can be sure we
508 * are referring to the right device. */
510 if (PATH_STARTSWITH_SET(ip
,
511 "/dev/disk/by-uuid/",
512 "/dev/disk/by-partuuid/",
513 "/dev/disk/by-partlabel/",
514 "/dev/disk/by-label/"))
515 return USER_TEST_EXISTS
;
517 return USER_TEST_MAYBE
;
526 if (S_ISDIR(st
.st_mode
))
527 return USER_TEST_EXISTS
;
532 assert_not_reached("Unexpected record type");
536 int user_record_test_image_path_and_warn(UserRecord
*h
) {
541 r
= user_record_test_image_path(h
);
543 return log_error_errno(r
, "User record lacks image path, refusing.");
545 return log_error_errno(r
, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h
));
547 return log_error_errno(r
, "Image path %s is not a directory, refusing.", user_record_image_path(h
));
549 return log_error_errno(r
, "Failed to test whether image path %s exists: %m", user_record_image_path(h
));
554 int user_record_test_password(UserRecord
*h
, UserRecord
*secret
) {
560 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
562 if (strv_isempty(h
->hashed_password
))
565 STRV_FOREACH(i
, secret
->password
) {
566 r
= test_password_many(h
->hashed_password
, *i
);
576 int user_record_test_recovery_key(UserRecord
*h
, UserRecord
*secret
) {
582 /* Checks whether any of the specified passwords matches any of the hashed recovery keys of the entry */
584 if (h
->n_recovery_key
== 0)
587 STRV_FOREACH(i
, secret
->password
) {
588 for (size_t j
= 0; j
< h
->n_recovery_key
; j
++) {
589 _cleanup_(erase_and_freep
) char *mangled
= NULL
;
592 if (streq(h
->recovery_key
[j
].type
, "modhex64")) {
593 /* If this key is for a modhex64 recovery key, then try to normalize the
594 * passphrase to make things more robust: that way the password becomes case
595 * insensitive and the dashes become optional. */
597 r
= normalize_recovery_key(*i
, &mangled
);
598 if (r
== -EINVAL
) /* Not a valid modhex64 passphrase, don't bother */
605 p
= *i
; /* Unknown recovery key types process as is */
607 r
= test_password_one(h
->recovery_key
[j
].hashed_password
, p
);
618 int user_record_set_disk_size(UserRecord
*h
, uint64_t disk_size
) {
619 _cleanup_(json_variant_unrefp
) JsonVariant
*new_per_machine
= NULL
, *midv
= NULL
, *midav
= NULL
, *ne
= NULL
;
620 _cleanup_free_ JsonVariant
**array
= NULL
;
621 char smid
[SD_ID128_STRING_MAX
];
622 size_t idx
= SIZE_MAX
, n
;
623 JsonVariant
*per_machine
;
632 if (disk_size
< USER_DISK_SIZE_MIN
|| disk_size
> USER_DISK_SIZE_MAX
)
635 r
= sd_id128_get_machine(&mid
);
639 sd_id128_to_string(mid
, smid
);
641 r
= json_variant_new_string(&midv
, smid
);
645 r
= json_variant_new_array(&midav
, (JsonVariant
*[]) { midv
}, 1);
649 per_machine
= json_variant_by_key(h
->json
, "perMachine");
653 if (!json_variant_is_array(per_machine
))
656 n
= json_variant_elements(per_machine
);
658 array
= new(JsonVariant
*, n
+ 1);
662 for (i
= 0; i
< n
; i
++) {
665 array
[i
] = json_variant_by_index(per_machine
, i
);
667 if (!json_variant_is_object(array
[i
]))
670 m
= json_variant_by_key(array
[i
], "matchMachineId");
672 /* No machineId field? Let's ignore this, but invalidate what we found so far */
677 if (json_variant_equal(m
, midv
) ||
678 json_variant_equal(m
, midav
)) {
679 /* Matches exactly what we are looking for. Let's use this */
684 r
= per_machine_id_match(m
, JSON_PERMISSIVE
);
688 /* Also matches what we are looking for, but with a broader match. In this
689 * case let's ignore this entry, and add a new specific one to the end. */
694 idx
= n
++; /* Nothing suitable found, place new entry at end */
696 ne
= json_variant_ref(array
[idx
]);
699 array
= new(JsonVariant
*, 1);
708 r
= json_variant_set_field(&ne
, "matchMachineId", midav
);
713 r
= json_variant_set_field_unsigned(&ne
, "diskSize", disk_size
);
720 r
= json_variant_new_array(&new_per_machine
, array
, n
);
724 r
= json_variant_set_field(&h
->json
, "perMachine", new_per_machine
);
728 h
->disk_size
= disk_size
;
729 h
->mask
|= USER_RECORD_PER_MACHINE
;
733 int user_record_update_last_changed(UserRecord
*h
, bool with_password
) {
734 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
743 n
= now(CLOCK_REALTIME
);
745 /* refuse downgrading */
746 if (h
->last_change_usec
!= UINT64_MAX
&& h
->last_change_usec
>= n
)
748 if (h
->last_password_change_usec
!= UINT64_MAX
&& h
->last_password_change_usec
>= n
)
751 v
= json_variant_ref(h
->json
);
753 r
= json_variant_set_field_unsigned(&v
, "lastChangeUSec", n
);
758 r
= json_variant_set_field_unsigned(&v
, "lastPasswordChangeUSec", n
);
762 h
->last_password_change_usec
= n
;
765 h
->last_change_usec
= n
;
767 json_variant_unref(h
->json
);
768 h
->json
= TAKE_PTR(v
);
770 h
->mask
|= USER_RECORD_REGULAR
;
774 int user_record_make_hashed_password(UserRecord
*h
, char **secret
, bool extend
) {
775 _cleanup_(json_variant_unrefp
) JsonVariant
*priv
= NULL
;
776 _cleanup_strv_free_
char **np
= NULL
;
783 /* Initializes the hashed password list from the specified plaintext passwords */
786 np
= strv_copy(h
->hashed_password
);
793 STRV_FOREACH(i
, secret
) {
794 _cleanup_free_
char *salt
= NULL
;
795 struct crypt_data cd
= {};
798 r
= make_salt(&salt
);
803 k
= crypt_r(*i
, salt
, &cd
);
805 return errno_or_else(EINVAL
);
807 r
= strv_extend(&np
, k
);
812 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
814 if (strv_isempty(np
))
815 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
817 _cleanup_(json_variant_unrefp
) JsonVariant
*new_array
= NULL
;
819 r
= json_variant_new_array_strv(&new_array
, np
);
823 r
= json_variant_set_field(&priv
, "hashedPassword", new_array
);
828 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
832 strv_free_and_replace(h
->hashed_password
, np
);
834 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
838 int user_record_set_hashed_password(UserRecord
*h
, char **hashed_password
) {
839 _cleanup_(json_variant_unrefp
) JsonVariant
*priv
= NULL
;
840 _cleanup_strv_free_
char **copy
= NULL
;
845 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
847 if (strv_isempty(hashed_password
))
848 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
850 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
852 copy
= strv_copy(hashed_password
);
858 r
= json_variant_new_array_strv(&array
, copy
);
862 r
= json_variant_set_field(&priv
, "hashedPassword", array
);
867 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
871 strv_free_and_replace(h
->hashed_password
, copy
);
873 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
877 int user_record_set_password(UserRecord
*h
, char **password
, bool prepend
) {
878 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
879 _cleanup_(strv_free_erasep
) char **e
= NULL
;
885 e
= strv_copy(password
);
889 r
= strv_extend_strv(&e
, h
->password
, true);
895 if (strv_equal(h
->password
, e
))
899 if (strv_equal(h
->password
, password
))
902 e
= strv_copy(password
);
909 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
912 r
= json_variant_filter(&w
, STRV_MAKE("password"));
914 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
916 r
= json_variant_new_array_strv(&l
, e
);
920 json_variant_sensitive(l
);
922 r
= json_variant_set_field(&w
, "password", l
);
927 json_variant_sensitive(w
);
929 r
= json_variant_set_field(&h
->json
, "secret", w
);
933 strv_free_and_replace(h
->password
, e
);
935 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
939 int user_record_set_token_pin(UserRecord
*h
, char **pin
, bool prepend
) {
940 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
941 _cleanup_(strv_free_erasep
) char **e
= NULL
;
951 r
= strv_extend_strv(&e
, h
->token_pin
, true);
957 if (strv_equal(h
->token_pin
, e
))
961 if (strv_equal(h
->token_pin
, pin
))
971 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
974 r
= json_variant_filter(&w
, STRV_MAKE("tokenPin"));
976 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
978 r
= json_variant_new_array_strv(&l
, e
);
982 json_variant_sensitive(l
);
984 r
= json_variant_set_field(&w
, "tokenPin", l
);
989 json_variant_sensitive(w
);
991 r
= json_variant_set_field(&h
->json
, "secret", w
);
995 strv_free_and_replace(h
->token_pin
, e
);
997 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1001 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord
*h
, int b
) {
1002 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1007 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
1010 r
= json_variant_filter(&w
, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
1012 r
= json_variant_set_field_boolean(&w
, "pkcs11ProtectedAuthenticationPathPermitted", b
);
1016 if (json_variant_is_blank_object(w
))
1017 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
1019 json_variant_sensitive(w
);
1021 r
= json_variant_set_field(&h
->json
, "secret", w
);
1026 h
->pkcs11_protected_authentication_path_permitted
= b
;
1028 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1032 int user_record_set_fido2_user_presence_permitted(UserRecord
*h
, int b
) {
1033 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1038 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
1041 r
= json_variant_filter(&w
, STRV_MAKE("fido2UserPresencePermitted"));
1043 r
= json_variant_set_field_boolean(&w
, "fido2UserPresencePermitted", b
);
1047 if (json_variant_is_blank_object(w
))
1048 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
1050 r
= json_variant_set_field(&h
->json
, "secret", w
);
1054 h
->fido2_user_presence_permitted
= b
;
1056 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1060 static bool per_machine_entry_empty(JsonVariant
*v
) {
1062 _unused_ JsonVariant
*e
;
1064 JSON_VARIANT_OBJECT_FOREACH(k
, e
, v
)
1065 if (!STR_IN_SET(k
, "matchMachineId", "matchHostname"))
1071 int user_record_set_password_change_now(UserRecord
*h
, int b
) {
1072 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1073 JsonVariant
*per_machine
;
1078 w
= json_variant_ref(h
->json
);
1081 r
= json_variant_filter(&w
, STRV_MAKE("passwordChangeNow"));
1083 r
= json_variant_set_field_boolean(&w
, "passwordChangeNow", b
);
1087 /* Also drop the field from all perMachine entries */
1088 per_machine
= json_variant_by_key(w
, "perMachine");
1090 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
1093 JSON_VARIANT_ARRAY_FOREACH(e
, per_machine
) {
1094 _cleanup_(json_variant_unrefp
) JsonVariant
*z
= NULL
;
1096 if (!json_variant_is_object(e
))
1099 z
= json_variant_ref(e
);
1101 r
= json_variant_filter(&z
, STRV_MAKE("passwordChangeNow"));
1105 if (per_machine_entry_empty(z
))
1108 r
= json_variant_append_array(&array
, z
);
1113 if (json_variant_is_blank_array(array
))
1114 r
= json_variant_filter(&w
, STRV_MAKE("perMachine"));
1116 r
= json_variant_set_field(&w
, "perMachine", array
);
1120 SET_FLAG(h
->mask
, USER_RECORD_PER_MACHINE
, !json_variant_is_blank_array(array
));
1123 json_variant_unref(h
->json
);
1124 h
->json
= TAKE_PTR(w
);
1126 h
->password_change_now
= b
;
1131 int user_record_merge_secret(UserRecord
*h
, UserRecord
*secret
) {
1136 /* Merges the secrets from 'secret' into 'h'. */
1138 r
= user_record_set_password(h
, secret
->password
, true);
1142 r
= user_record_set_token_pin(h
, secret
->token_pin
, true);
1146 if (secret
->pkcs11_protected_authentication_path_permitted
>= 0) {
1147 r
= user_record_set_pkcs11_protected_authentication_path_permitted(
1149 secret
->pkcs11_protected_authentication_path_permitted
);
1154 if (secret
->fido2_user_presence_permitted
>= 0) {
1155 r
= user_record_set_fido2_user_presence_permitted(
1157 secret
->fido2_user_presence_permitted
);
1165 int user_record_good_authentication(UserRecord
*h
) {
1166 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1167 char buf
[SD_ID128_STRING_MAX
];
1168 uint64_t counter
, usec
;
1174 switch (h
->good_authentication_counter
) {
1179 counter
= h
->good_authentication_counter
; /* saturate */
1182 counter
= h
->good_authentication_counter
+ 1;
1186 usec
= now(CLOCK_REALTIME
);
1188 r
= sd_id128_get_machine(&mid
);
1192 v
= json_variant_ref(h
->json
);
1193 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1194 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1196 r
= json_variant_set_field_unsigned(&z
, "goodAuthenticationCounter", counter
);
1200 r
= json_variant_set_field_unsigned(&z
, "lastGoodAuthenticationUSec", usec
);
1204 r
= json_variant_set_field(&w
, buf
, z
);
1208 r
= json_variant_set_field(&v
, "status", w
);
1212 json_variant_unref(h
->json
);
1213 h
->json
= TAKE_PTR(v
);
1215 h
->good_authentication_counter
= counter
;
1216 h
->last_good_authentication_usec
= usec
;
1218 h
->mask
|= USER_RECORD_STATUS
;
1222 int user_record_bad_authentication(UserRecord
*h
) {
1223 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1224 char buf
[SD_ID128_STRING_MAX
];
1225 uint64_t counter
, usec
;
1231 switch (h
->bad_authentication_counter
) {
1236 counter
= h
->bad_authentication_counter
; /* saturate */
1239 counter
= h
->bad_authentication_counter
+ 1;
1243 usec
= now(CLOCK_REALTIME
);
1245 r
= sd_id128_get_machine(&mid
);
1249 v
= json_variant_ref(h
->json
);
1250 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1251 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1253 r
= json_variant_set_field_unsigned(&z
, "badAuthenticationCounter", counter
);
1257 r
= json_variant_set_field_unsigned(&z
, "lastBadAuthenticationUSec", usec
);
1261 r
= json_variant_set_field(&w
, buf
, z
);
1265 r
= json_variant_set_field(&v
, "status", w
);
1269 json_variant_unref(h
->json
);
1270 h
->json
= TAKE_PTR(v
);
1272 h
->bad_authentication_counter
= counter
;
1273 h
->last_bad_authentication_usec
= usec
;
1275 h
->mask
|= USER_RECORD_STATUS
;
1279 int user_record_ratelimit(UserRecord
*h
) {
1280 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1281 usec_t usec
, new_ratelimit_begin_usec
, new_ratelimit_count
;
1282 char buf
[SD_ID128_STRING_MAX
];
1288 usec
= now(CLOCK_REALTIME
);
1290 if (h
->ratelimit_begin_usec
!= UINT64_MAX
&& h
->ratelimit_begin_usec
> usec
)
1291 /* Hmm, time is running backwards? Say no! */
1293 else if (h
->ratelimit_begin_usec
== UINT64_MAX
||
1294 usec_add(h
->ratelimit_begin_usec
, user_record_ratelimit_interval_usec(h
)) <= usec
) {
1296 new_ratelimit_begin_usec
= usec
;
1297 new_ratelimit_count
= 1;
1298 } else if (h
->ratelimit_count
< user_record_ratelimit_burst(h
)) {
1300 new_ratelimit_begin_usec
= h
->ratelimit_begin_usec
;
1301 new_ratelimit_count
= h
->ratelimit_count
+ 1;
1306 r
= sd_id128_get_machine(&mid
);
1310 v
= json_variant_ref(h
->json
);
1311 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1312 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1314 r
= json_variant_set_field_unsigned(&z
, "rateLimitBeginUSec", new_ratelimit_begin_usec
);
1318 r
= json_variant_set_field_unsigned(&z
, "rateLimitCount", new_ratelimit_count
);
1322 r
= json_variant_set_field(&w
, buf
, z
);
1326 r
= json_variant_set_field(&v
, "status", w
);
1330 json_variant_unref(h
->json
);
1331 h
->json
= TAKE_PTR(v
);
1333 h
->ratelimit_begin_usec
= new_ratelimit_begin_usec
;
1334 h
->ratelimit_count
= new_ratelimit_count
;
1336 h
->mask
|= USER_RECORD_STATUS
;
1340 int user_record_is_supported(UserRecord
*hr
, sd_bus_error
*error
) {
1343 if (hr
->disposition
>= 0 && hr
->disposition
!= USER_REGULAR
)
1344 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Cannot manage anything but regular users.");
1346 if (hr
->storage
>= 0 && !IN_SET(hr
->storage
, USER_LUKS
, USER_DIRECTORY
, USER_SUBVOLUME
, USER_FSCRYPT
, USER_CIFS
))
1347 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User record has storage type this service cannot manage.");
1349 if (gid_is_valid(hr
->gid
) && hr
->uid
!= (uid_t
) hr
->gid
)
1350 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User record has to have matching UID/GID fields.");
1352 if (hr
->service
&& !streq(hr
->service
, "io.systemd.Home"))
1353 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Not accepted with service not matching io.systemd.Home.");