1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "errno-util.h"
5 #include "id128-util.h"
6 #include "libcrypt-util.h"
7 #include "mountpoint-util.h"
10 #include "user-record-util.h"
11 #include "user-util.h"
13 int user_record_synthesize(
15 const char *user_name
,
17 const char *image_path
,
22 _cleanup_free_
char *hd
= NULL
, *un
= NULL
, *ip
= NULL
, *rr
= NULL
, *user_name_and_realm
= NULL
;
23 char smid
[SD_ID128_STRING_MAX
];
30 assert(IN_SET(storage
, USER_LUKS
, USER_SUBVOLUME
, USER_FSCRYPT
, USER_DIRECTORY
));
31 assert(uid_is_valid(uid
));
32 assert(gid_is_valid(gid
));
34 /* Fill in a home record from just a username and an image path. */
39 if (!suitable_user_name(user_name
))
43 r
= suitable_realm(realm
);
50 if (!suitable_image_path(image_path
))
53 r
= sd_id128_get_machine(&mid
);
57 un
= strdup(user_name
);
66 user_name_and_realm
= strjoin(user_name
, "@", realm
);
67 if (!user_name_and_realm
)
71 ip
= strdup(image_path
);
75 hd
= path_join("/home/", user_name
);
79 r
= json_build(&h
->json
,
81 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name
)),
82 JSON_BUILD_PAIR_CONDITION(!!rr
, "realm", JSON_BUILD_STRING(realm
)),
83 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
84 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
85 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
86 JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path
)),
87 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd
)),
88 JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage
))),
89 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
)),
90 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))))))));
94 free_and_replace(h
->user_name
, un
);
95 free_and_replace(h
->realm
, rr
);
96 free_and_replace(h
->user_name_and_realm_auto
, user_name_and_realm
);
97 free_and_replace(h
->image_path
, ip
);
98 free_and_replace(h
->home_directory
, hd
);
102 h
->mask
= USER_RECORD_REGULAR
|USER_RECORD_BINDING
;
106 int group_record_synthesize(GroupRecord
*g
, UserRecord
*h
) {
107 _cleanup_free_
char *un
= NULL
, *rr
= NULL
, *group_name_and_realm
= NULL
;
108 char smid
[SD_ID128_STRING_MAX
];
118 r
= sd_id128_get_machine(&mid
);
122 un
= strdup(h
->user_name
);
127 rr
= strdup(h
->realm
);
131 group_name_and_realm
= strjoin(un
, "@", rr
);
132 if (!group_name_and_realm
)
136 r
= json_build(&g
->json
,
138 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(un
)),
139 JSON_BUILD_PAIR_CONDITION(!!rr
, "realm", JSON_BUILD_STRING(rr
)),
140 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
141 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
142 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h
))))))),
143 JSON_BUILD_PAIR_CONDITION(h
->disposition
>= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h
)))),
144 JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
145 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
146 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
150 free_and_replace(g
->group_name
, un
);
151 free_and_replace(g
->realm
, rr
);
152 free_and_replace(g
->group_name_and_realm_auto
, group_name_and_realm
);
153 g
->gid
= user_record_gid(h
);
154 g
->disposition
= h
->disposition
;
156 g
->mask
= USER_RECORD_REGULAR
|USER_RECORD_BINDING
;
160 int user_record_reconcile(
162 UserRecord
*embedded
,
163 UserReconcileMode mode
,
168 /* Reconciles the identity record stored on the host with the one embedded in a $HOME
169 * directory. Returns the following error codes:
171 * -EINVAL: one of the records not valid
172 * -REMCHG: identity records are not about the same user
173 * -ESTALE: embedded identity record is equally new or newer than supplied record
175 * Return the new record to use, which is either the embedded record updated with the host
176 * binding or the host record. In both cases the secret data is stripped. */
181 /* Make sure both records are initialized */
182 if (!host
->json
|| !embedded
->json
)
185 /* Ensure these records actually contain user data */
186 if (!(embedded
->mask
& host
->mask
& USER_RECORD_REGULAR
))
189 /* Make sure the user name and realm matches */
190 if (!user_record_compatible(host
, embedded
))
193 /* Embedded identities may not contain secrets or binding info*/
194 if ((embedded
->mask
& (USER_RECORD_SECRET
|USER_RECORD_BINDING
)) != 0)
197 /* The embedded record checked out, let's now figure out which of the two identities we'll consider
198 * in effect from now on. We do this by checking the last change timestamp, and in doubt always let
199 * the embedded data win. */
200 if (host
->last_change_usec
!= UINT64_MAX
&&
201 (embedded
->last_change_usec
== UINT64_MAX
|| host
->last_change_usec
> embedded
->last_change_usec
))
203 /* The host version is definitely newer, either because it has a version at all and the
204 * embedded version doesn't or because it is numerically newer. */
205 result
= USER_RECONCILE_HOST_WON
;
207 else if (host
->last_change_usec
== embedded
->last_change_usec
) {
209 /* The nominal version number of the host and the embedded identity is the same. If so, let's
210 * verify that, and tell the caller if we are ignoring embedded data. */
212 r
= user_record_masked_equal(host
, embedded
, USER_RECORD_REGULAR
|USER_RECORD_PRIVILEGED
|USER_RECORD_PER_MACHINE
);
216 if (mode
== USER_RECONCILE_REQUIRE_NEWER
)
219 result
= USER_RECONCILE_IDENTICAL
;
221 result
= USER_RECONCILE_HOST_WON
;
223 _cleanup_(json_variant_unrefp
) JsonVariant
*extended
= NULL
;
224 _cleanup_(user_record_unrefp
) UserRecord
*merged
= NULL
;
227 /* The embedded version is newer */
229 if (mode
== USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL
)
232 /* Copy in the binding data */
233 extended
= json_variant_ref(embedded
->json
);
235 e
= json_variant_by_key(host
->json
, "binding");
237 r
= json_variant_set_field(&extended
, "binding", e
);
242 merged
= user_record_new();
246 r
= user_record_load(merged
, extended
, USER_RECORD_LOAD_MASK_SECRET
);
250 *ret
= TAKE_PTR(merged
);
251 return USER_RECONCILE_EMBEDDED_WON
; /* update */
254 /* Strip out secrets */
255 r
= user_record_clone(host
, USER_RECORD_LOAD_MASK_SECRET
, ret
);
262 int user_record_add_binding(
265 const char *image_path
,
266 sd_id128_t partition_uuid
,
267 sd_id128_t luks_uuid
,
269 const char *luks_cipher
,
270 const char *luks_cipher_mode
,
271 uint64_t luks_volume_key_size
,
272 const char *file_system_type
,
273 const char *home_directory
,
277 _cleanup_(json_variant_unrefp
) JsonVariant
*new_binding_entry
= NULL
, *binding
= NULL
;
278 char smid
[SD_ID128_STRING_MAX
], partition_uuids
[37], luks_uuids
[37], fs_uuids
[37];
279 _cleanup_free_
char *ip
= NULL
, *hd
= NULL
, *ip_auto
= NULL
, *lc
= NULL
, *lcm
= NULL
, *fst
= NULL
;
288 r
= sd_id128_get_machine(&mid
);
291 sd_id128_to_string(mid
, smid
);
294 ip
= strdup(image_path
);
297 } else if (!h
->image_path
&& storage
>= 0) {
298 r
= user_record_build_image_path(storage
, user_record_user_name_and_realm(h
), &ip_auto
);
303 if (home_directory
) {
304 hd
= strdup(home_directory
);
309 if (file_system_type
) {
310 fst
= strdup(file_system_type
);
316 lc
= strdup(luks_cipher
);
321 if (luks_cipher_mode
) {
322 lcm
= strdup(luks_cipher_mode
);
327 r
= json_build(&new_binding_entry
,
329 JSON_BUILD_PAIR_CONDITION(!!image_path
, "imagePath", JSON_BUILD_STRING(image_path
)),
330 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid
), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid
, partition_uuids
))),
331 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid
), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid
, luks_uuids
))),
332 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid
), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid
, fs_uuids
))),
333 JSON_BUILD_PAIR_CONDITION(!!luks_cipher
, "luksCipher", JSON_BUILD_STRING(luks_cipher
)),
334 JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode
, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode
)),
335 JSON_BUILD_PAIR_CONDITION(luks_volume_key_size
!= UINT64_MAX
, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size
)),
336 JSON_BUILD_PAIR_CONDITION(!!file_system_type
, "fileSystemType", JSON_BUILD_STRING(file_system_type
)),
337 JSON_BUILD_PAIR_CONDITION(!!home_directory
, "homeDirectory", JSON_BUILD_STRING(home_directory
)),
338 JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid
), "uid", JSON_BUILD_UNSIGNED(uid
)),
339 JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid
), "gid", JSON_BUILD_UNSIGNED(gid
)),
340 JSON_BUILD_PAIR_CONDITION(storage
>= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage
)))));
344 binding
= json_variant_ref(json_variant_by_key(h
->json
, "binding"));
346 _cleanup_(json_variant_unrefp
) JsonVariant
*be
= NULL
;
348 /* Merge the new entry with an old one, if that exists */
349 be
= json_variant_ref(json_variant_by_key(binding
, smid
));
351 r
= json_variant_merge(&be
, new_binding_entry
);
355 json_variant_unref(new_binding_entry
);
356 new_binding_entry
= TAKE_PTR(be
);
360 r
= json_variant_set_field(&binding
, smid
, new_binding_entry
);
364 r
= json_variant_set_field(&h
->json
, "binding", binding
);
369 h
->storage
= storage
;
372 free_and_replace(h
->image_path
, ip
);
374 free_and_replace(h
->image_path_auto
, ip_auto
);
376 if (!sd_id128_is_null(partition_uuid
))
377 h
->partition_uuid
= partition_uuid
;
379 if (!sd_id128_is_null(luks_uuid
))
380 h
->luks_uuid
= luks_uuid
;
382 if (!sd_id128_is_null(fs_uuid
))
383 h
->file_system_uuid
= fs_uuid
;
386 free_and_replace(h
->luks_cipher
, lc
);
388 free_and_replace(h
->luks_cipher_mode
, lcm
);
389 if (luks_volume_key_size
!= UINT64_MAX
)
390 h
->luks_volume_key_size
= luks_volume_key_size
;
393 free_and_replace(h
->file_system_type
, fst
);
395 free_and_replace(h
->home_directory
, hd
);
397 if (uid_is_valid(uid
))
399 if (gid_is_valid(gid
))
402 h
->mask
|= USER_RECORD_BINDING
;
406 int user_record_test_home_directory(UserRecord
*h
) {
412 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
414 hd
= user_record_home_directory(h
);
418 r
= is_dir(hd
, false);
420 return USER_TEST_ABSENT
;
426 r
= path_is_mount_point(hd
, NULL
, 0);
430 return USER_TEST_MOUNTED
;
432 /* If the image path and the home directory are identical, then it's OK if the directory is
434 if (IN_SET(user_record_storage(h
), USER_CLASSIC
, USER_DIRECTORY
, USER_SUBVOLUME
, USER_FSCRYPT
)) {
437 ip
= user_record_image_path(h
);
438 if (ip
&& path_equal(ip
, hd
))
439 return USER_TEST_EXISTS
;
442 /* Otherwise it's not OK */
443 r
= dir_is_empty(hd
);
449 return USER_TEST_EXISTS
;
452 int user_record_test_home_directory_and_warn(UserRecord
*h
) {
457 r
= user_record_test_home_directory(h
);
459 return log_error_errno(r
, "User record lacks home directory, refusing.");
461 return log_error_errno(r
, "Home directory %s is not a directory, refusing.", user_record_home_directory(h
));
463 return log_error_errno(r
, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h
));
465 return log_error_errno(r
, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h
));
470 int user_record_test_image_path(UserRecord
*h
) {
476 if (user_record_storage(h
) == USER_CIFS
)
477 return USER_TEST_UNDEFINED
;
479 ip
= user_record_image_path(h
);
483 if (stat(ip
, &st
) < 0) {
485 return USER_TEST_ABSENT
;
490 switch (user_record_storage(h
)) {
493 if (S_ISREG(st
.st_mode
))
494 return USER_TEST_EXISTS
;
495 if (S_ISBLK(st
.st_mode
)) {
496 /* For block devices we can't really be sure if the device referenced actually is the
497 * fs we look for or some other file system (think: what does /dev/sdb1 refer
498 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
499 * case, except if the device path used is one of the paths that is based on a
500 * filesystem or partition UUID or label, because in those cases we can be sure we
501 * are referring to the right device. */
503 if (PATH_STARTSWITH_SET(ip
,
504 "/dev/disk/by-uuid/",
505 "/dev/disk/by-partuuid/",
506 "/dev/disk/by-partlabel/",
507 "/dev/disk/by-label/"))
508 return USER_TEST_EXISTS
;
510 return USER_TEST_MAYBE
;
519 if (S_ISDIR(st
.st_mode
))
520 return USER_TEST_EXISTS
;
525 assert_not_reached("Unexpected record type");
529 int user_record_test_image_path_and_warn(UserRecord
*h
) {
534 r
= user_record_test_image_path(h
);
536 return log_error_errno(r
, "User record lacks image path, refusing.");
538 return log_error_errno(r
, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h
));
540 return log_error_errno(r
, "Image path %s is not a directory, refusing.", user_record_image_path(h
));
542 return log_error_errno(r
, "Failed to test whether image path %s exists: %m", user_record_image_path(h
));
547 int user_record_test_secret(UserRecord
*h
, UserRecord
*secret
) {
553 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
555 if (strv_isempty(h
->hashed_password
))
558 STRV_FOREACH(i
, secret
->password
) {
559 r
= test_password_many(h
->hashed_password
, *i
);
569 int user_record_set_disk_size(UserRecord
*h
, uint64_t disk_size
) {
570 _cleanup_(json_variant_unrefp
) JsonVariant
*new_per_machine
= NULL
, *midv
= NULL
, *midav
= NULL
, *ne
= NULL
;
571 _cleanup_free_ JsonVariant
**array
= NULL
;
572 char smid
[SD_ID128_STRING_MAX
];
573 size_t idx
= SIZE_MAX
, n
;
574 JsonVariant
*per_machine
;
583 if (disk_size
< USER_DISK_SIZE_MIN
|| disk_size
> USER_DISK_SIZE_MAX
)
586 r
= sd_id128_get_machine(&mid
);
590 sd_id128_to_string(mid
, smid
);
592 r
= json_variant_new_string(&midv
, smid
);
596 r
= json_variant_new_array(&midav
, (JsonVariant
*[]) { midv
}, 1);
600 per_machine
= json_variant_by_key(h
->json
, "perMachine");
604 if (!json_variant_is_array(per_machine
))
607 n
= json_variant_elements(per_machine
);
609 array
= new(JsonVariant
*, n
+ 1);
613 for (i
= 0; i
< n
; i
++) {
616 array
[i
] = json_variant_by_index(per_machine
, i
);
618 if (!json_variant_is_object(array
[i
]))
621 m
= json_variant_by_key(array
[i
], "matchMachineId");
623 /* No machineId field? Let's ignore this, but invalidate what we found so far */
628 if (json_variant_equal(m
, midv
) ||
629 json_variant_equal(m
, midav
)) {
630 /* Matches exactly what we are looking for. Let's use this */
635 r
= per_machine_id_match(m
, JSON_PERMISSIVE
);
639 /* Also matches what we are looking for, but with a broader match. In this
640 * case let's ignore this entry, and add a new specific one to the end. */
645 idx
= n
++; /* Nothing suitable found, place new entry at end */
647 ne
= json_variant_ref(array
[idx
]);
650 array
= new(JsonVariant
*, 1);
659 r
= json_variant_set_field(&ne
, "matchMachineId", midav
);
664 r
= json_variant_set_field_unsigned(&ne
, "diskSize", disk_size
);
671 r
= json_variant_new_array(&new_per_machine
, array
, n
);
675 r
= json_variant_set_field(&h
->json
, "perMachine", new_per_machine
);
679 h
->disk_size
= disk_size
;
680 h
->mask
|= USER_RECORD_PER_MACHINE
;
684 int user_record_update_last_changed(UserRecord
*h
, bool with_password
) {
685 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
694 n
= now(CLOCK_REALTIME
);
696 /* refuse downgrading */
697 if (h
->last_change_usec
!= UINT64_MAX
&& h
->last_change_usec
>= n
)
699 if (h
->last_password_change_usec
!= UINT64_MAX
&& h
->last_password_change_usec
>= n
)
702 v
= json_variant_ref(h
->json
);
704 r
= json_variant_set_field_unsigned(&v
, "lastChangeUSec", n
);
709 r
= json_variant_set_field_unsigned(&v
, "lastPasswordChangeUSec", n
);
713 h
->last_password_change_usec
= n
;
716 h
->last_change_usec
= n
;
718 json_variant_unref(h
->json
);
719 h
->json
= TAKE_PTR(v
);
721 h
->mask
|= USER_RECORD_REGULAR
;
725 int user_record_make_hashed_password(UserRecord
*h
, char **secret
, bool extend
) {
726 _cleanup_(json_variant_unrefp
) JsonVariant
*priv
= NULL
;
727 _cleanup_strv_free_
char **np
= NULL
;
734 /* Initializes the hashed password list from the specified plaintext passwords */
737 np
= strv_copy(h
->hashed_password
);
744 STRV_FOREACH(i
, secret
) {
745 _cleanup_free_
char *salt
= NULL
;
746 struct crypt_data cd
= {};
749 r
= make_salt(&salt
);
754 k
= crypt_r(*i
, salt
, &cd
);
756 return errno_or_else(EINVAL
);
758 r
= strv_extend(&np
, k
);
763 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
765 if (strv_isempty(np
))
766 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
768 _cleanup_(json_variant_unrefp
) JsonVariant
*new_array
= NULL
;
770 r
= json_variant_new_array_strv(&new_array
, np
);
774 r
= json_variant_set_field(&priv
, "hashedPassword", new_array
);
779 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
783 strv_free_and_replace(h
->hashed_password
, np
);
785 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
789 int user_record_set_hashed_password(UserRecord
*h
, char **hashed_password
) {
790 _cleanup_(json_variant_unrefp
) JsonVariant
*priv
= NULL
;
791 _cleanup_strv_free_
char **copy
= NULL
;
796 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
798 if (strv_isempty(hashed_password
))
799 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
801 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
803 copy
= strv_copy(hashed_password
);
809 r
= json_variant_new_array_strv(&array
, copy
);
813 r
= json_variant_set_field(&priv
, "hashedPassword", array
);
818 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
822 strv_free_and_replace(h
->hashed_password
, copy
);
824 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
828 int user_record_set_password(UserRecord
*h
, char **password
, bool prepend
) {
829 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
830 _cleanup_(strv_free_erasep
) char **e
= NULL
;
836 e
= strv_copy(password
);
840 r
= strv_extend_strv(&e
, h
->password
, true);
846 if (strv_equal(h
->password
, e
))
850 if (strv_equal(h
->password
, password
))
853 e
= strv_copy(password
);
860 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
863 r
= json_variant_filter(&w
, STRV_MAKE("password"));
865 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
867 r
= json_variant_new_array_strv(&l
, e
);
871 json_variant_sensitive(l
);
873 r
= json_variant_set_field(&w
, "password", l
);
878 json_variant_sensitive(w
);
880 r
= json_variant_set_field(&h
->json
, "secret", w
);
884 strv_free_and_replace(h
->password
, e
);
886 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
890 int user_record_set_pkcs11_pin(UserRecord
*h
, char **pin
, bool prepend
) {
891 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
892 _cleanup_(strv_free_erasep
) char **e
= NULL
;
902 r
= strv_extend_strv(&e
, h
->pkcs11_pin
, true);
908 if (strv_equal(h
->pkcs11_pin
, e
))
912 if (strv_equal(h
->pkcs11_pin
, pin
))
922 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
925 r
= json_variant_filter(&w
, STRV_MAKE("pkcs11Pin"));
927 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
929 r
= json_variant_new_array_strv(&l
, e
);
933 json_variant_sensitive(l
);
935 r
= json_variant_set_field(&w
, "pkcs11Pin", l
);
940 json_variant_sensitive(w
);
942 r
= json_variant_set_field(&h
->json
, "secret", w
);
946 strv_free_and_replace(h
->pkcs11_pin
, e
);
948 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
952 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord
*h
, int b
) {
953 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
958 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
961 r
= json_variant_filter(&w
, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
963 r
= json_variant_set_field_boolean(&w
, "pkcs11ProtectedAuthenticationPathPermitted", b
);
967 if (json_variant_is_blank_object(w
))
968 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
970 json_variant_sensitive(w
);
972 r
= json_variant_set_field(&h
->json
, "secret", w
);
977 h
->pkcs11_protected_authentication_path_permitted
= b
;
979 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
983 static bool per_machine_entry_empty(JsonVariant
*v
) {
985 _unused_ JsonVariant
*e
;
987 JSON_VARIANT_OBJECT_FOREACH(k
, e
, v
)
988 if (!STR_IN_SET(k
, "matchMachineId", "matchHostname"))
994 int user_record_set_password_change_now(UserRecord
*h
, int b
) {
995 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
996 JsonVariant
*per_machine
;
1001 w
= json_variant_ref(h
->json
);
1004 r
= json_variant_filter(&w
, STRV_MAKE("passwordChangeNow"));
1006 r
= json_variant_set_field_boolean(&w
, "passwordChangeNow", b
);
1010 /* Also drop the field from all perMachine entries */
1011 per_machine
= json_variant_by_key(w
, "perMachine");
1013 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
1016 JSON_VARIANT_ARRAY_FOREACH(e
, per_machine
) {
1017 _cleanup_(json_variant_unrefp
) JsonVariant
*z
= NULL
;
1019 if (!json_variant_is_object(e
))
1022 z
= json_variant_ref(e
);
1024 r
= json_variant_filter(&z
, STRV_MAKE("passwordChangeNow"));
1028 if (per_machine_entry_empty(z
))
1031 r
= json_variant_append_array(&array
, z
);
1036 if (json_variant_is_blank_array(array
))
1037 r
= json_variant_filter(&w
, STRV_MAKE("perMachine"));
1039 r
= json_variant_set_field(&w
, "perMachine", array
);
1043 SET_FLAG(h
->mask
, USER_RECORD_PER_MACHINE
, !json_variant_is_blank_array(array
));
1046 json_variant_unref(h
->json
);
1047 h
->json
= TAKE_PTR(w
);
1049 h
->password_change_now
= b
;
1054 int user_record_merge_secret(UserRecord
*h
, UserRecord
*secret
) {
1059 /* Merges the secrets from 'secret' into 'h'. */
1061 r
= user_record_set_password(h
, secret
->password
, true);
1065 r
= user_record_set_pkcs11_pin(h
, secret
->pkcs11_pin
, true);
1069 if (secret
->pkcs11_protected_authentication_path_permitted
>= 0) {
1070 r
= user_record_set_pkcs11_protected_authentication_path_permitted(h
, secret
->pkcs11_protected_authentication_path_permitted
);
1078 int user_record_good_authentication(UserRecord
*h
) {
1079 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1080 char buf
[SD_ID128_STRING_MAX
];
1081 uint64_t counter
, usec
;
1087 switch (h
->good_authentication_counter
) {
1092 counter
= h
->good_authentication_counter
; /* saturate */
1095 counter
= h
->good_authentication_counter
+ 1;
1099 usec
= now(CLOCK_REALTIME
);
1101 r
= sd_id128_get_machine(&mid
);
1105 v
= json_variant_ref(h
->json
);
1106 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1107 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1109 r
= json_variant_set_field_unsigned(&z
, "goodAuthenticationCounter", counter
);
1113 r
= json_variant_set_field_unsigned(&z
, "lastGoodAuthenticationUSec", usec
);
1117 r
= json_variant_set_field(&w
, buf
, z
);
1121 r
= json_variant_set_field(&v
, "status", w
);
1125 json_variant_unref(h
->json
);
1126 h
->json
= TAKE_PTR(v
);
1128 h
->good_authentication_counter
= counter
;
1129 h
->last_good_authentication_usec
= usec
;
1131 h
->mask
|= USER_RECORD_STATUS
;
1135 int user_record_bad_authentication(UserRecord
*h
) {
1136 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1137 char buf
[SD_ID128_STRING_MAX
];
1138 uint64_t counter
, usec
;
1144 switch (h
->bad_authentication_counter
) {
1149 counter
= h
->bad_authentication_counter
; /* saturate */
1152 counter
= h
->bad_authentication_counter
+ 1;
1156 usec
= now(CLOCK_REALTIME
);
1158 r
= sd_id128_get_machine(&mid
);
1162 v
= json_variant_ref(h
->json
);
1163 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1164 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1166 r
= json_variant_set_field_unsigned(&z
, "badAuthenticationCounter", counter
);
1170 r
= json_variant_set_field_unsigned(&z
, "lastBadAuthenticationUSec", usec
);
1174 r
= json_variant_set_field(&w
, buf
, z
);
1178 r
= json_variant_set_field(&v
, "status", w
);
1182 json_variant_unref(h
->json
);
1183 h
->json
= TAKE_PTR(v
);
1185 h
->bad_authentication_counter
= counter
;
1186 h
->last_bad_authentication_usec
= usec
;
1188 h
->mask
|= USER_RECORD_STATUS
;
1192 int user_record_ratelimit(UserRecord
*h
) {
1193 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1194 usec_t usec
, new_ratelimit_begin_usec
, new_ratelimit_count
;
1195 char buf
[SD_ID128_STRING_MAX
];
1201 usec
= now(CLOCK_REALTIME
);
1203 if (h
->ratelimit_begin_usec
!= UINT64_MAX
&& h
->ratelimit_begin_usec
> usec
)
1204 /* Hmm, time is running backwards? Say no! */
1206 else if (h
->ratelimit_begin_usec
== UINT64_MAX
||
1207 usec_add(h
->ratelimit_begin_usec
, user_record_ratelimit_interval_usec(h
)) <= usec
) {
1209 new_ratelimit_begin_usec
= usec
;
1210 new_ratelimit_count
= 1;
1211 } else if (h
->ratelimit_count
< user_record_ratelimit_burst(h
)) {
1213 new_ratelimit_begin_usec
= h
->ratelimit_begin_usec
;
1214 new_ratelimit_count
= h
->ratelimit_count
+ 1;
1219 r
= sd_id128_get_machine(&mid
);
1223 v
= json_variant_ref(h
->json
);
1224 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1225 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1227 r
= json_variant_set_field_unsigned(&z
, "rateLimitBeginUSec", new_ratelimit_begin_usec
);
1231 r
= json_variant_set_field_unsigned(&z
, "rateLimitCount", new_ratelimit_count
);
1235 r
= json_variant_set_field(&w
, buf
, z
);
1239 r
= json_variant_set_field(&v
, "status", w
);
1243 json_variant_unref(h
->json
);
1244 h
->json
= TAKE_PTR(v
);
1246 h
->ratelimit_begin_usec
= new_ratelimit_begin_usec
;
1247 h
->ratelimit_count
= new_ratelimit_count
;
1249 h
->mask
|= USER_RECORD_STATUS
;
1253 int user_record_is_supported(UserRecord
*hr
, sd_bus_error
*error
) {
1256 if (hr
->disposition
>= 0 && hr
->disposition
!= USER_REGULAR
)
1257 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Cannot manage anything but regular users.");
1259 if (hr
->storage
>= 0 && !IN_SET(hr
->storage
, USER_LUKS
, USER_DIRECTORY
, USER_SUBVOLUME
, USER_FSCRYPT
, USER_CIFS
))
1260 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User record has storage type this service cannot manage.");
1262 if (gid_is_valid(hr
->gid
) && hr
->uid
!= (uid_t
) hr
->gid
)
1263 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User record has to have matching UID/GID fields.");
1265 if (hr
->service
&& !streq(hr
->service
, "io.systemd.Home"))
1266 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Not accepted with service not matching io.systemd.Home.");