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
;
27 char smid
[SD_ID128_STRING_MAX
];
34 assert(IN_SET(storage
, USER_LUKS
, USER_SUBVOLUME
, USER_FSCRYPT
, USER_DIRECTORY
));
35 assert(uid_is_valid(uid
));
36 assert(gid_is_valid(gid
));
38 /* Fill in a home record from just a username and an image path. */
43 if (!suitable_user_name(user_name
))
47 r
= suitable_realm(realm
);
54 if (!suitable_image_path(image_path
))
57 r
= sd_id128_get_machine(&mid
);
61 un
= strdup(user_name
);
70 user_name_and_realm
= strjoin(user_name
, "@", realm
);
71 if (!user_name_and_realm
)
75 ip
= strdup(image_path
);
79 hd
= path_join("/home/", user_name
);
83 r
= json_build(&h
->json
,
85 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name
)),
86 JSON_BUILD_PAIR_CONDITION(!!rr
, "realm", JSON_BUILD_STRING(realm
)),
87 JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("regular")),
88 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
89 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
90 JSON_BUILD_PAIR("imagePath", JSON_BUILD_STRING(image_path
)),
91 JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING(hd
)),
92 JSON_BUILD_PAIR("storage", JSON_BUILD_STRING(user_storage_to_string(storage
))),
93 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid
)),
94 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid
))))))));
98 free_and_replace(h
->user_name
, un
);
99 free_and_replace(h
->realm
, rr
);
100 free_and_replace(h
->user_name_and_realm_auto
, user_name_and_realm
);
101 free_and_replace(h
->image_path
, ip
);
102 free_and_replace(h
->home_directory
, hd
);
103 h
->storage
= storage
;
106 h
->mask
= USER_RECORD_REGULAR
|USER_RECORD_BINDING
;
110 int group_record_synthesize(GroupRecord
*g
, UserRecord
*h
) {
111 _cleanup_free_
char *un
= NULL
, *rr
= NULL
, *group_name_and_realm
= NULL
, *description
= NULL
;
112 char smid
[SD_ID128_STRING_MAX
];
122 r
= sd_id128_get_machine(&mid
);
126 un
= strdup(h
->user_name
);
131 rr
= strdup(h
->realm
);
135 group_name_and_realm
= strjoin(un
, "@", rr
);
136 if (!group_name_and_realm
)
140 description
= strjoin("Primary Group of User ", un
);
144 r
= json_build(&g
->json
,
146 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(un
)),
147 JSON_BUILD_PAIR_CONDITION(!!rr
, "realm", JSON_BUILD_STRING(rr
)),
148 JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description
)),
149 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
150 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
151 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h
))))))),
152 JSON_BUILD_PAIR_CONDITION(h
->disposition
>= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h
)))),
153 JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
154 JSON_BUILD_PAIR(sd_id128_to_string(mid
, smid
), JSON_BUILD_OBJECT(
155 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
159 free_and_replace(g
->group_name
, un
);
160 free_and_replace(g
->realm
, rr
);
161 free_and_replace(g
->group_name_and_realm_auto
, group_name_and_realm
);
162 g
->gid
= user_record_gid(h
);
163 g
->disposition
= h
->disposition
;
165 g
->mask
= USER_RECORD_REGULAR
|USER_RECORD_BINDING
;
169 int user_record_reconcile(
171 UserRecord
*embedded
,
172 UserReconcileMode mode
,
177 /* Reconciles the identity record stored on the host with the one embedded in a $HOME
178 * directory. Returns the following error codes:
180 * -EINVAL: one of the records not valid
181 * -REMCHG: identity records are not about the same user
182 * -ESTALE: embedded identity record is equally new or newer than supplied record
184 * Return the new record to use, which is either the embedded record updated with the host
185 * binding or the host record. In both cases the secret data is stripped. */
190 /* Make sure both records are initialized */
191 if (!host
->json
|| !embedded
->json
)
194 /* Ensure these records actually contain user data */
195 if (!(embedded
->mask
& host
->mask
& USER_RECORD_REGULAR
))
198 /* Make sure the user name and realm matches */
199 if (!user_record_compatible(host
, embedded
))
202 /* Embedded identities may not contain secrets or binding info*/
203 if ((embedded
->mask
& (USER_RECORD_SECRET
|USER_RECORD_BINDING
)) != 0)
206 /* The embedded record checked out, let's now figure out which of the two identities we'll consider
207 * in effect from now on. We do this by checking the last change timestamp, and in doubt always let
208 * the embedded data win. */
209 if (host
->last_change_usec
!= UINT64_MAX
&&
210 (embedded
->last_change_usec
== UINT64_MAX
|| host
->last_change_usec
> embedded
->last_change_usec
))
212 /* The host version is definitely newer, either because it has a version at all and the
213 * embedded version doesn't or because it is numerically newer. */
214 result
= USER_RECONCILE_HOST_WON
;
216 else if (host
->last_change_usec
== embedded
->last_change_usec
) {
218 /* The nominal version number of the host and the embedded identity is the same. If so, let's
219 * verify that, and tell the caller if we are ignoring embedded data. */
221 r
= user_record_masked_equal(host
, embedded
, USER_RECORD_REGULAR
|USER_RECORD_PRIVILEGED
|USER_RECORD_PER_MACHINE
);
225 if (mode
== USER_RECONCILE_REQUIRE_NEWER
)
228 result
= USER_RECONCILE_IDENTICAL
;
230 result
= USER_RECONCILE_HOST_WON
;
232 _cleanup_(json_variant_unrefp
) JsonVariant
*extended
= NULL
;
233 _cleanup_(user_record_unrefp
) UserRecord
*merged
= NULL
;
236 /* The embedded version is newer */
238 if (mode
== USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL
)
241 /* Copy in the binding data */
242 extended
= json_variant_ref(embedded
->json
);
244 e
= json_variant_by_key(host
->json
, "binding");
246 r
= json_variant_set_field(&extended
, "binding", e
);
251 merged
= user_record_new();
255 r
= user_record_load(merged
, extended
, USER_RECORD_LOAD_MASK_SECRET
);
259 *ret
= TAKE_PTR(merged
);
260 return USER_RECONCILE_EMBEDDED_WON
; /* update */
263 /* Strip out secrets */
264 r
= user_record_clone(host
, USER_RECORD_LOAD_MASK_SECRET
, ret
);
271 int user_record_add_binding(
274 const char *image_path
,
275 sd_id128_t partition_uuid
,
276 sd_id128_t luks_uuid
,
278 const char *luks_cipher
,
279 const char *luks_cipher_mode
,
280 uint64_t luks_volume_key_size
,
281 const char *file_system_type
,
282 const char *home_directory
,
286 _cleanup_(json_variant_unrefp
) JsonVariant
*new_binding_entry
= NULL
, *binding
= NULL
;
287 char smid
[SD_ID128_STRING_MAX
], partition_uuids
[ID128_UUID_STRING_MAX
], luks_uuids
[ID128_UUID_STRING_MAX
], fs_uuids
[ID128_UUID_STRING_MAX
];
288 _cleanup_free_
char *ip
= NULL
, *hd
= NULL
, *ip_auto
= NULL
, *lc
= NULL
, *lcm
= NULL
, *fst
= NULL
;
297 r
= sd_id128_get_machine(&mid
);
300 sd_id128_to_string(mid
, smid
);
303 ip
= strdup(image_path
);
306 } else if (!h
->image_path
&& storage
>= 0) {
307 r
= user_record_build_image_path(storage
, user_record_user_name_and_realm(h
), &ip_auto
);
312 if (home_directory
) {
313 hd
= strdup(home_directory
);
318 if (file_system_type
) {
319 fst
= strdup(file_system_type
);
325 lc
= strdup(luks_cipher
);
330 if (luks_cipher_mode
) {
331 lcm
= strdup(luks_cipher_mode
);
336 r
= json_build(&new_binding_entry
,
338 JSON_BUILD_PAIR_CONDITION(!!image_path
, "imagePath", JSON_BUILD_STRING(image_path
)),
339 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid
), "partitionUuid", JSON_BUILD_STRING(id128_to_uuid_string(partition_uuid
, partition_uuids
))),
340 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid
), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid
, luks_uuids
))),
341 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid
), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid
, fs_uuids
))),
342 JSON_BUILD_PAIR_CONDITION(!!luks_cipher
, "luksCipher", JSON_BUILD_STRING(luks_cipher
)),
343 JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode
, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode
)),
344 JSON_BUILD_PAIR_CONDITION(luks_volume_key_size
!= UINT64_MAX
, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size
)),
345 JSON_BUILD_PAIR_CONDITION(!!file_system_type
, "fileSystemType", JSON_BUILD_STRING(file_system_type
)),
346 JSON_BUILD_PAIR_CONDITION(!!home_directory
, "homeDirectory", JSON_BUILD_STRING(home_directory
)),
347 JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid
), "uid", JSON_BUILD_UNSIGNED(uid
)),
348 JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid
), "gid", JSON_BUILD_UNSIGNED(gid
)),
349 JSON_BUILD_PAIR_CONDITION(storage
>= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage
)))));
353 binding
= json_variant_ref(json_variant_by_key(h
->json
, "binding"));
355 _cleanup_(json_variant_unrefp
) JsonVariant
*be
= NULL
;
357 /* Merge the new entry with an old one, if that exists */
358 be
= json_variant_ref(json_variant_by_key(binding
, smid
));
360 r
= json_variant_merge(&be
, new_binding_entry
);
364 json_variant_unref(new_binding_entry
);
365 new_binding_entry
= TAKE_PTR(be
);
369 r
= json_variant_set_field(&binding
, smid
, new_binding_entry
);
373 r
= json_variant_set_field(&h
->json
, "binding", binding
);
378 h
->storage
= storage
;
381 free_and_replace(h
->image_path
, ip
);
383 free_and_replace(h
->image_path_auto
, ip_auto
);
385 if (!sd_id128_is_null(partition_uuid
))
386 h
->partition_uuid
= partition_uuid
;
388 if (!sd_id128_is_null(luks_uuid
))
389 h
->luks_uuid
= luks_uuid
;
391 if (!sd_id128_is_null(fs_uuid
))
392 h
->file_system_uuid
= fs_uuid
;
395 free_and_replace(h
->luks_cipher
, lc
);
397 free_and_replace(h
->luks_cipher_mode
, lcm
);
398 if (luks_volume_key_size
!= UINT64_MAX
)
399 h
->luks_volume_key_size
= luks_volume_key_size
;
402 free_and_replace(h
->file_system_type
, fst
);
404 free_and_replace(h
->home_directory
, hd
);
406 if (uid_is_valid(uid
))
408 if (gid_is_valid(gid
))
411 h
->mask
|= USER_RECORD_BINDING
;
415 int user_record_test_home_directory(UserRecord
*h
) {
421 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
423 hd
= user_record_home_directory(h
);
427 r
= is_dir(hd
, false);
429 return USER_TEST_ABSENT
;
435 r
= path_is_mount_point(hd
, NULL
, 0);
439 return USER_TEST_MOUNTED
;
441 /* If the image path and the home directory are identical, then it's OK if the directory is
443 if (IN_SET(user_record_storage(h
), USER_CLASSIC
, USER_DIRECTORY
, USER_SUBVOLUME
, USER_FSCRYPT
)) {
446 ip
= user_record_image_path(h
);
447 if (ip
&& path_equal(ip
, hd
))
448 return USER_TEST_EXISTS
;
451 /* Otherwise it's not OK */
452 r
= dir_is_empty(hd
);
458 return USER_TEST_EXISTS
;
461 int user_record_test_home_directory_and_warn(UserRecord
*h
) {
466 r
= user_record_test_home_directory(h
);
468 return log_error_errno(r
, "User record lacks home directory, refusing.");
470 return log_error_errno(r
, "Home directory %s is not a directory, refusing.", user_record_home_directory(h
));
472 return log_error_errno(r
, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h
));
474 return log_error_errno(r
, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h
));
479 int user_record_test_image_path(UserRecord
*h
) {
485 if (user_record_storage(h
) == USER_CIFS
)
486 return USER_TEST_UNDEFINED
;
488 ip
= user_record_image_path(h
);
492 if (stat(ip
, &st
) < 0) {
494 return USER_TEST_ABSENT
;
499 switch (user_record_storage(h
)) {
502 if (S_ISREG(st
.st_mode
)) {
506 n
= getxattr(ip
, "user.home-dirty", x
, sizeof(x
));
508 if (errno
!= ENODATA
)
509 log_debug_errno(errno
, "Unable to read dirty xattr off image file, ignoring: %m");
511 } else if (n
== 1 && x
[0] == '1')
512 return USER_TEST_DIRTY
;
514 return USER_TEST_EXISTS
;
517 if (S_ISBLK(st
.st_mode
)) {
518 /* For block devices we can't really be sure if the device referenced actually is the
519 * fs we look for or some other file system (think: what does /dev/sdb1 refer
520 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
521 * case, except if the device path used is one of the paths that is based on a
522 * filesystem or partition UUID or label, because in those cases we can be sure we
523 * are referring to the right device. */
525 if (PATH_STARTSWITH_SET(ip
,
526 "/dev/disk/by-uuid/",
527 "/dev/disk/by-partuuid/",
528 "/dev/disk/by-partlabel/",
529 "/dev/disk/by-label/"))
530 return USER_TEST_EXISTS
;
532 return USER_TEST_MAYBE
;
541 if (S_ISDIR(st
.st_mode
))
542 return USER_TEST_EXISTS
;
547 assert_not_reached("Unexpected record type");
551 int user_record_test_image_path_and_warn(UserRecord
*h
) {
556 r
= user_record_test_image_path(h
);
558 return log_error_errno(r
, "User record lacks image path, refusing.");
560 return log_error_errno(r
, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h
));
562 return log_error_errno(r
, "Image path %s is not a directory, refusing.", user_record_image_path(h
));
564 return log_error_errno(r
, "Failed to test whether image path %s exists: %m", user_record_image_path(h
));
569 int user_record_test_password(UserRecord
*h
, UserRecord
*secret
) {
575 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
577 if (strv_isempty(h
->hashed_password
))
580 STRV_FOREACH(i
, secret
->password
) {
581 r
= test_password_many(h
->hashed_password
, *i
);
591 int user_record_test_recovery_key(UserRecord
*h
, UserRecord
*secret
) {
597 /* Checks whether any of the specified passwords matches any of the hashed recovery keys of the entry */
599 if (h
->n_recovery_key
== 0)
602 STRV_FOREACH(i
, secret
->password
) {
603 for (size_t j
= 0; j
< h
->n_recovery_key
; j
++) {
604 _cleanup_(erase_and_freep
) char *mangled
= NULL
;
607 if (streq(h
->recovery_key
[j
].type
, "modhex64")) {
608 /* If this key is for a modhex64 recovery key, then try to normalize the
609 * passphrase to make things more robust: that way the password becomes case
610 * insensitive and the dashes become optional. */
612 r
= normalize_recovery_key(*i
, &mangled
);
613 if (r
== -EINVAL
) /* Not a valid modhex64 passphrase, don't bother */
620 p
= *i
; /* Unknown recovery key types process as is */
622 r
= test_password_one(h
->recovery_key
[j
].hashed_password
, p
);
633 int user_record_set_disk_size(UserRecord
*h
, uint64_t disk_size
) {
634 _cleanup_(json_variant_unrefp
) JsonVariant
*new_per_machine
= NULL
, *midv
= NULL
, *midav
= NULL
, *ne
= NULL
;
635 _cleanup_free_ JsonVariant
**array
= NULL
;
636 char smid
[SD_ID128_STRING_MAX
];
637 size_t idx
= SIZE_MAX
, n
;
638 JsonVariant
*per_machine
;
647 if (disk_size
< USER_DISK_SIZE_MIN
|| disk_size
> USER_DISK_SIZE_MAX
)
650 r
= sd_id128_get_machine(&mid
);
654 sd_id128_to_string(mid
, smid
);
656 r
= json_variant_new_string(&midv
, smid
);
660 r
= json_variant_new_array(&midav
, (JsonVariant
*[]) { midv
}, 1);
664 per_machine
= json_variant_by_key(h
->json
, "perMachine");
668 if (!json_variant_is_array(per_machine
))
671 n
= json_variant_elements(per_machine
);
673 array
= new(JsonVariant
*, n
+ 1);
677 for (i
= 0; i
< n
; i
++) {
680 array
[i
] = json_variant_by_index(per_machine
, i
);
682 if (!json_variant_is_object(array
[i
]))
685 m
= json_variant_by_key(array
[i
], "matchMachineId");
687 /* No machineId field? Let's ignore this, but invalidate what we found so far */
692 if (json_variant_equal(m
, midv
) ||
693 json_variant_equal(m
, midav
)) {
694 /* Matches exactly what we are looking for. Let's use this */
699 r
= per_machine_id_match(m
, JSON_PERMISSIVE
);
703 /* Also matches what we are looking for, but with a broader match. In this
704 * case let's ignore this entry, and add a new specific one to the end. */
709 idx
= n
++; /* Nothing suitable found, place new entry at end */
711 ne
= json_variant_ref(array
[idx
]);
714 array
= new(JsonVariant
*, 1);
723 r
= json_variant_set_field(&ne
, "matchMachineId", midav
);
728 r
= json_variant_set_field_unsigned(&ne
, "diskSize", disk_size
);
735 r
= json_variant_new_array(&new_per_machine
, array
, n
);
739 r
= json_variant_set_field(&h
->json
, "perMachine", new_per_machine
);
743 h
->disk_size
= disk_size
;
744 h
->mask
|= USER_RECORD_PER_MACHINE
;
748 int user_record_update_last_changed(UserRecord
*h
, bool with_password
) {
749 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
758 n
= now(CLOCK_REALTIME
);
760 /* refuse downgrading */
761 if (h
->last_change_usec
!= UINT64_MAX
&& h
->last_change_usec
>= n
)
763 if (h
->last_password_change_usec
!= UINT64_MAX
&& h
->last_password_change_usec
>= n
)
766 v
= json_variant_ref(h
->json
);
768 r
= json_variant_set_field_unsigned(&v
, "lastChangeUSec", n
);
773 r
= json_variant_set_field_unsigned(&v
, "lastPasswordChangeUSec", n
);
777 h
->last_password_change_usec
= n
;
780 h
->last_change_usec
= n
;
782 json_variant_unref(h
->json
);
783 h
->json
= TAKE_PTR(v
);
785 h
->mask
|= USER_RECORD_REGULAR
;
789 int user_record_make_hashed_password(UserRecord
*h
, char **secret
, bool extend
) {
790 _cleanup_(json_variant_unrefp
) JsonVariant
*priv
= NULL
;
791 _cleanup_strv_free_
char **np
= NULL
;
798 /* Initializes the hashed password list from the specified plaintext passwords */
801 np
= strv_copy(h
->hashed_password
);
808 STRV_FOREACH(i
, secret
) {
809 _cleanup_(erase_and_freep
) char *hashed
= NULL
;
811 r
= hash_password(*i
, &hashed
);
815 r
= strv_consume(&np
, TAKE_PTR(hashed
));
820 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
822 if (strv_isempty(np
))
823 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
825 _cleanup_(json_variant_unrefp
) JsonVariant
*new_array
= NULL
;
827 r
= json_variant_new_array_strv(&new_array
, np
);
831 r
= json_variant_set_field(&priv
, "hashedPassword", new_array
);
836 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
840 strv_free_and_replace(h
->hashed_password
, np
);
842 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
846 int user_record_set_hashed_password(UserRecord
*h
, char **hashed_password
) {
847 _cleanup_(json_variant_unrefp
) JsonVariant
*priv
= NULL
;
848 _cleanup_strv_free_
char **copy
= NULL
;
853 priv
= json_variant_ref(json_variant_by_key(h
->json
, "privileged"));
855 if (strv_isempty(hashed_password
))
856 r
= json_variant_filter(&priv
, STRV_MAKE("hashedPassword"));
858 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
860 copy
= strv_copy(hashed_password
);
866 r
= json_variant_new_array_strv(&array
, copy
);
870 r
= json_variant_set_field(&priv
, "hashedPassword", array
);
875 r
= json_variant_set_field(&h
->json
, "privileged", priv
);
879 strv_free_and_replace(h
->hashed_password
, copy
);
881 SET_FLAG(h
->mask
, USER_RECORD_PRIVILEGED
, !json_variant_is_blank_object(priv
));
885 int user_record_set_password(UserRecord
*h
, char **password
, bool prepend
) {
886 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
887 _cleanup_(strv_free_erasep
) char **e
= NULL
;
893 e
= strv_copy(password
);
897 r
= strv_extend_strv(&e
, h
->password
, true);
903 if (strv_equal(h
->password
, e
))
907 if (strv_equal(h
->password
, password
))
910 e
= strv_copy(password
);
917 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
920 r
= json_variant_filter(&w
, STRV_MAKE("password"));
922 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
924 r
= json_variant_new_array_strv(&l
, e
);
928 json_variant_sensitive(l
);
930 r
= json_variant_set_field(&w
, "password", l
);
935 json_variant_sensitive(w
);
937 r
= json_variant_set_field(&h
->json
, "secret", w
);
941 strv_free_and_replace(h
->password
, e
);
943 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
947 int user_record_set_token_pin(UserRecord
*h
, char **pin
, bool prepend
) {
948 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
949 _cleanup_(strv_free_erasep
) char **e
= NULL
;
959 r
= strv_extend_strv(&e
, h
->token_pin
, true);
965 if (strv_equal(h
->token_pin
, e
))
969 if (strv_equal(h
->token_pin
, pin
))
979 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
982 r
= json_variant_filter(&w
, STRV_MAKE("tokenPin"));
984 _cleanup_(json_variant_unrefp
) JsonVariant
*l
= NULL
;
986 r
= json_variant_new_array_strv(&l
, e
);
990 json_variant_sensitive(l
);
992 r
= json_variant_set_field(&w
, "tokenPin", l
);
997 json_variant_sensitive(w
);
999 r
= json_variant_set_field(&h
->json
, "secret", w
);
1003 strv_free_and_replace(h
->token_pin
, e
);
1005 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1009 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord
*h
, int b
) {
1010 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1015 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
1018 r
= json_variant_filter(&w
, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
1020 r
= json_variant_set_field_boolean(&w
, "pkcs11ProtectedAuthenticationPathPermitted", b
);
1024 if (json_variant_is_blank_object(w
))
1025 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
1027 json_variant_sensitive(w
);
1029 r
= json_variant_set_field(&h
->json
, "secret", w
);
1034 h
->pkcs11_protected_authentication_path_permitted
= b
;
1036 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1040 int user_record_set_fido2_user_presence_permitted(UserRecord
*h
, int b
) {
1041 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1046 w
= json_variant_ref(json_variant_by_key(h
->json
, "secret"));
1049 r
= json_variant_filter(&w
, STRV_MAKE("fido2UserPresencePermitted"));
1051 r
= json_variant_set_field_boolean(&w
, "fido2UserPresencePermitted", b
);
1055 if (json_variant_is_blank_object(w
))
1056 r
= json_variant_filter(&h
->json
, STRV_MAKE("secret"));
1058 r
= json_variant_set_field(&h
->json
, "secret", w
);
1062 h
->fido2_user_presence_permitted
= b
;
1064 SET_FLAG(h
->mask
, USER_RECORD_SECRET
, !json_variant_is_blank_object(w
));
1068 static bool per_machine_entry_empty(JsonVariant
*v
) {
1070 _unused_ JsonVariant
*e
;
1072 JSON_VARIANT_OBJECT_FOREACH(k
, e
, v
)
1073 if (!STR_IN_SET(k
, "matchMachineId", "matchHostname"))
1079 int user_record_set_password_change_now(UserRecord
*h
, int b
) {
1080 _cleanup_(json_variant_unrefp
) JsonVariant
*w
= NULL
;
1081 JsonVariant
*per_machine
;
1086 w
= json_variant_ref(h
->json
);
1089 r
= json_variant_filter(&w
, STRV_MAKE("passwordChangeNow"));
1091 r
= json_variant_set_field_boolean(&w
, "passwordChangeNow", b
);
1095 /* Also drop the field from all perMachine entries */
1096 per_machine
= json_variant_by_key(w
, "perMachine");
1098 _cleanup_(json_variant_unrefp
) JsonVariant
*array
= NULL
;
1101 JSON_VARIANT_ARRAY_FOREACH(e
, per_machine
) {
1102 _cleanup_(json_variant_unrefp
) JsonVariant
*z
= NULL
;
1104 if (!json_variant_is_object(e
))
1107 z
= json_variant_ref(e
);
1109 r
= json_variant_filter(&z
, STRV_MAKE("passwordChangeNow"));
1113 if (per_machine_entry_empty(z
))
1116 r
= json_variant_append_array(&array
, z
);
1121 if (json_variant_is_blank_array(array
))
1122 r
= json_variant_filter(&w
, STRV_MAKE("perMachine"));
1124 r
= json_variant_set_field(&w
, "perMachine", array
);
1128 SET_FLAG(h
->mask
, USER_RECORD_PER_MACHINE
, !json_variant_is_blank_array(array
));
1131 json_variant_unref(h
->json
);
1132 h
->json
= TAKE_PTR(w
);
1134 h
->password_change_now
= b
;
1139 int user_record_merge_secret(UserRecord
*h
, UserRecord
*secret
) {
1144 /* Merges the secrets from 'secret' into 'h'. */
1146 r
= user_record_set_password(h
, secret
->password
, true);
1150 r
= user_record_set_token_pin(h
, secret
->token_pin
, true);
1154 if (secret
->pkcs11_protected_authentication_path_permitted
>= 0) {
1155 r
= user_record_set_pkcs11_protected_authentication_path_permitted(
1157 secret
->pkcs11_protected_authentication_path_permitted
);
1162 if (secret
->fido2_user_presence_permitted
>= 0) {
1163 r
= user_record_set_fido2_user_presence_permitted(
1165 secret
->fido2_user_presence_permitted
);
1173 int user_record_good_authentication(UserRecord
*h
) {
1174 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1175 char buf
[SD_ID128_STRING_MAX
];
1176 uint64_t counter
, usec
;
1182 switch (h
->good_authentication_counter
) {
1187 counter
= h
->good_authentication_counter
; /* saturate */
1190 counter
= h
->good_authentication_counter
+ 1;
1194 usec
= now(CLOCK_REALTIME
);
1196 r
= sd_id128_get_machine(&mid
);
1200 v
= json_variant_ref(h
->json
);
1201 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1202 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1204 r
= json_variant_set_field_unsigned(&z
, "goodAuthenticationCounter", counter
);
1208 r
= json_variant_set_field_unsigned(&z
, "lastGoodAuthenticationUSec", usec
);
1212 r
= json_variant_set_field(&w
, buf
, z
);
1216 r
= json_variant_set_field(&v
, "status", w
);
1220 json_variant_unref(h
->json
);
1221 h
->json
= TAKE_PTR(v
);
1223 h
->good_authentication_counter
= counter
;
1224 h
->last_good_authentication_usec
= usec
;
1226 h
->mask
|= USER_RECORD_STATUS
;
1230 int user_record_bad_authentication(UserRecord
*h
) {
1231 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1232 char buf
[SD_ID128_STRING_MAX
];
1233 uint64_t counter
, usec
;
1239 switch (h
->bad_authentication_counter
) {
1244 counter
= h
->bad_authentication_counter
; /* saturate */
1247 counter
= h
->bad_authentication_counter
+ 1;
1251 usec
= now(CLOCK_REALTIME
);
1253 r
= sd_id128_get_machine(&mid
);
1257 v
= json_variant_ref(h
->json
);
1258 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1259 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1261 r
= json_variant_set_field_unsigned(&z
, "badAuthenticationCounter", counter
);
1265 r
= json_variant_set_field_unsigned(&z
, "lastBadAuthenticationUSec", usec
);
1269 r
= json_variant_set_field(&w
, buf
, z
);
1273 r
= json_variant_set_field(&v
, "status", w
);
1277 json_variant_unref(h
->json
);
1278 h
->json
= TAKE_PTR(v
);
1280 h
->bad_authentication_counter
= counter
;
1281 h
->last_bad_authentication_usec
= usec
;
1283 h
->mask
|= USER_RECORD_STATUS
;
1287 int user_record_ratelimit(UserRecord
*h
) {
1288 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
, *w
= NULL
, *z
= NULL
;
1289 usec_t usec
, new_ratelimit_begin_usec
, new_ratelimit_count
;
1290 char buf
[SD_ID128_STRING_MAX
];
1296 usec
= now(CLOCK_REALTIME
);
1298 if (h
->ratelimit_begin_usec
!= UINT64_MAX
&& h
->ratelimit_begin_usec
> usec
) {
1299 /* Hmm, start-time is after the current time? If so, the RTC most likely doesn't work. */
1300 new_ratelimit_begin_usec
= usec
;
1301 new_ratelimit_count
= 1;
1302 log_debug("Rate limit timestamp is in the future, assuming incorrect system clock, resetting limit.");
1303 } else if (h
->ratelimit_begin_usec
== UINT64_MAX
||
1304 usec_add(h
->ratelimit_begin_usec
, user_record_ratelimit_interval_usec(h
)) <= usec
) {
1306 new_ratelimit_begin_usec
= usec
;
1307 new_ratelimit_count
= 1;
1308 } else if (h
->ratelimit_count
< user_record_ratelimit_burst(h
)) {
1310 new_ratelimit_begin_usec
= h
->ratelimit_begin_usec
;
1311 new_ratelimit_count
= h
->ratelimit_count
+ 1;
1316 r
= sd_id128_get_machine(&mid
);
1320 v
= json_variant_ref(h
->json
);
1321 w
= json_variant_ref(json_variant_by_key(v
, "status"));
1322 z
= json_variant_ref(json_variant_by_key(w
, sd_id128_to_string(mid
, buf
)));
1324 r
= json_variant_set_field_unsigned(&z
, "rateLimitBeginUSec", new_ratelimit_begin_usec
);
1328 r
= json_variant_set_field_unsigned(&z
, "rateLimitCount", new_ratelimit_count
);
1332 r
= json_variant_set_field(&w
, buf
, z
);
1336 r
= json_variant_set_field(&v
, "status", w
);
1340 json_variant_unref(h
->json
);
1341 h
->json
= TAKE_PTR(v
);
1343 h
->ratelimit_begin_usec
= new_ratelimit_begin_usec
;
1344 h
->ratelimit_count
= new_ratelimit_count
;
1346 h
->mask
|= USER_RECORD_STATUS
;
1350 int user_record_is_supported(UserRecord
*hr
, sd_bus_error
*error
) {
1353 if (hr
->disposition
>= 0 && hr
->disposition
!= USER_REGULAR
)
1354 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Cannot manage anything but regular users.");
1356 if (hr
->storage
>= 0 && !IN_SET(hr
->storage
, USER_LUKS
, USER_DIRECTORY
, USER_SUBVOLUME
, USER_FSCRYPT
, USER_CIFS
))
1357 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User record has storage type this service cannot manage.");
1359 if (gid_is_valid(hr
->gid
) && hr
->uid
!= (uid_t
) hr
->gid
)
1360 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "User record has to have matching UID/GID fields.");
1362 if (hr
->service
&& !streq(hr
->service
, "io.systemd.Home"))
1363 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Not accepted with service not matching io.systemd.Home.");