]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/user-record-util.c
core,home,machined: generate description fields for all groups we synthesize
[thirdparty/systemd.git] / src / home / user-record-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "errno-util.h"
4 #include "home-util.h"
5 #include "id128-util.h"
6 #include "libcrypt-util.h"
7 #include "mountpoint-util.h"
8 #include "path-util.h"
9 #include "stat-util.h"
10 #include "user-record-util.h"
11 #include "user-util.h"
12
13 int user_record_synthesize(
14 UserRecord *h,
15 const char *user_name,
16 const char *realm,
17 const char *image_path,
18 UserStorage storage,
19 uid_t uid,
20 gid_t gid) {
21
22 _cleanup_free_ char *hd = NULL, *un = NULL, *ip = NULL, *rr = NULL, *user_name_and_realm = NULL;
23 char smid[SD_ID128_STRING_MAX];
24 sd_id128_t mid;
25 int r;
26
27 assert(h);
28 assert(user_name);
29 assert(image_path);
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));
33
34 /* Fill in a home record from just a username and an image path. */
35
36 if (h->json)
37 return -EBUSY;
38
39 if (!suitable_user_name(user_name))
40 return -EINVAL;
41
42 if (realm) {
43 r = suitable_realm(realm);
44 if (r < 0)
45 return r;
46 if (r == 0)
47 return -EINVAL;
48 }
49
50 if (!suitable_image_path(image_path))
51 return -EINVAL;
52
53 r = sd_id128_get_machine(&mid);
54 if (r < 0)
55 return r;
56
57 un = strdup(user_name);
58 if (!un)
59 return -ENOMEM;
60
61 if (realm) {
62 rr = strdup(realm);
63 if (!rr)
64 return -ENOMEM;
65
66 user_name_and_realm = strjoin(user_name, "@", realm);
67 if (!user_name_and_realm)
68 return -ENOMEM;
69 }
70
71 ip = strdup(image_path);
72 if (!ip)
73 return -ENOMEM;
74
75 hd = path_join("/home/", user_name);
76 if (!hd)
77 return -ENOMEM;
78
79 r = json_build(&h->json,
80 JSON_BUILD_OBJECT(
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))))))));
91 if (r < 0)
92 return r;
93
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);
99 h->storage = storage;
100 h->uid = uid;
101
102 h->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
103 return 0;
104 }
105
106 int group_record_synthesize(GroupRecord *g, UserRecord *h) {
107 _cleanup_free_ char *un = NULL, *rr = NULL, *group_name_and_realm = NULL, *description = NULL;
108 char smid[SD_ID128_STRING_MAX];
109 sd_id128_t mid;
110 int r;
111
112 assert(g);
113 assert(h);
114
115 if (g->json)
116 return -EBUSY;
117
118 r = sd_id128_get_machine(&mid);
119 if (r < 0)
120 return r;
121
122 un = strdup(h->user_name);
123 if (!un)
124 return -ENOMEM;
125
126 if (h->realm) {
127 rr = strdup(h->realm);
128 if (!rr)
129 return -ENOMEM;
130
131 group_name_and_realm = strjoin(un, "@", rr);
132 if (!group_name_and_realm)
133 return -ENOMEM;
134 }
135
136 description = strjoin("Primary Group of User ", un);
137 if (!description)
138 return -ENOMEM;
139
140 r = json_build(&g->json,
141 JSON_BUILD_OBJECT(
142 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(un)),
143 JSON_BUILD_PAIR_CONDITION(!!rr, "realm", JSON_BUILD_STRING(rr)),
144 JSON_BUILD_PAIR("description", JSON_BUILD_STRING(description)),
145 JSON_BUILD_PAIR("binding", JSON_BUILD_OBJECT(
146 JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
147 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(user_record_gid(h))))))),
148 JSON_BUILD_PAIR_CONDITION(h->disposition >= 0, "disposition", JSON_BUILD_STRING(user_disposition_to_string(user_record_disposition(h)))),
149 JSON_BUILD_PAIR("status", JSON_BUILD_OBJECT(
150 JSON_BUILD_PAIR(sd_id128_to_string(mid, smid), JSON_BUILD_OBJECT(
151 JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Home"))))))));
152 if (r < 0)
153 return r;
154
155 free_and_replace(g->group_name, un);
156 free_and_replace(g->realm, rr);
157 free_and_replace(g->group_name_and_realm_auto, group_name_and_realm);
158 g->gid = user_record_gid(h);
159 g->disposition = h->disposition;
160
161 g->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
162 return 0;
163 }
164
165 int user_record_reconcile(
166 UserRecord *host,
167 UserRecord *embedded,
168 UserReconcileMode mode,
169 UserRecord **ret) {
170
171 int r, result;
172
173 /* Reconciles the identity record stored on the host with the one embedded in a $HOME
174 * directory. Returns the following error codes:
175 *
176 * -EINVAL: one of the records not valid
177 * -REMCHG: identity records are not about the same user
178 * -ESTALE: embedded identity record is equally new or newer than supplied record
179 *
180 * Return the new record to use, which is either the embedded record updated with the host
181 * binding or the host record. In both cases the secret data is stripped. */
182
183 assert(host);
184 assert(embedded);
185
186 /* Make sure both records are initialized */
187 if (!host->json || !embedded->json)
188 return -EINVAL;
189
190 /* Ensure these records actually contain user data */
191 if (!(embedded->mask & host->mask & USER_RECORD_REGULAR))
192 return -EINVAL;
193
194 /* Make sure the user name and realm matches */
195 if (!user_record_compatible(host, embedded))
196 return -EREMCHG;
197
198 /* Embedded identities may not contain secrets or binding info*/
199 if ((embedded->mask & (USER_RECORD_SECRET|USER_RECORD_BINDING)) != 0)
200 return -EINVAL;
201
202 /* The embedded record checked out, let's now figure out which of the two identities we'll consider
203 * in effect from now on. We do this by checking the last change timestamp, and in doubt always let
204 * the embedded data win. */
205 if (host->last_change_usec != UINT64_MAX &&
206 (embedded->last_change_usec == UINT64_MAX || host->last_change_usec > embedded->last_change_usec))
207
208 /* The host version is definitely newer, either because it has a version at all and the
209 * embedded version doesn't or because it is numerically newer. */
210 result = USER_RECONCILE_HOST_WON;
211
212 else if (host->last_change_usec == embedded->last_change_usec) {
213
214 /* The nominal version number of the host and the embedded identity is the same. If so, let's
215 * verify that, and tell the caller if we are ignoring embedded data. */
216
217 r = user_record_masked_equal(host, embedded, USER_RECORD_REGULAR|USER_RECORD_PRIVILEGED|USER_RECORD_PER_MACHINE);
218 if (r < 0)
219 return r;
220 if (r > 0) {
221 if (mode == USER_RECONCILE_REQUIRE_NEWER)
222 return -ESTALE;
223
224 result = USER_RECONCILE_IDENTICAL;
225 } else
226 result = USER_RECONCILE_HOST_WON;
227 } else {
228 _cleanup_(json_variant_unrefp) JsonVariant *extended = NULL;
229 _cleanup_(user_record_unrefp) UserRecord *merged = NULL;
230 JsonVariant *e;
231
232 /* The embedded version is newer */
233
234 if (mode == USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL)
235 return -ESTALE;
236
237 /* Copy in the binding data */
238 extended = json_variant_ref(embedded->json);
239
240 e = json_variant_by_key(host->json, "binding");
241 if (e) {
242 r = json_variant_set_field(&extended, "binding", e);
243 if (r < 0)
244 return r;
245 }
246
247 merged = user_record_new();
248 if (!merged)
249 return -ENOMEM;
250
251 r = user_record_load(merged, extended, USER_RECORD_LOAD_MASK_SECRET);
252 if (r < 0)
253 return r;
254
255 *ret = TAKE_PTR(merged);
256 return USER_RECONCILE_EMBEDDED_WON; /* update */
257 }
258
259 /* Strip out secrets */
260 r = user_record_clone(host, USER_RECORD_LOAD_MASK_SECRET, ret);
261 if (r < 0)
262 return r;
263
264 return result;
265 }
266
267 int user_record_add_binding(
268 UserRecord *h,
269 UserStorage storage,
270 const char *image_path,
271 sd_id128_t partition_uuid,
272 sd_id128_t luks_uuid,
273 sd_id128_t fs_uuid,
274 const char *luks_cipher,
275 const char *luks_cipher_mode,
276 uint64_t luks_volume_key_size,
277 const char *file_system_type,
278 const char *home_directory,
279 uid_t uid,
280 gid_t gid) {
281
282 _cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
283 char smid[SD_ID128_STRING_MAX], partition_uuids[ID128_UUID_STRING_MAX], luks_uuids[ID128_UUID_STRING_MAX], fs_uuids[ID128_UUID_STRING_MAX];
284 _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
285 sd_id128_t mid;
286 int r;
287
288 assert(h);
289
290 if (!h->json)
291 return -EUNATCH;
292
293 r = sd_id128_get_machine(&mid);
294 if (r < 0)
295 return r;
296 sd_id128_to_string(mid, smid);
297
298 if (image_path) {
299 ip = strdup(image_path);
300 if (!ip)
301 return -ENOMEM;
302 } else if (!h->image_path && storage >= 0) {
303 r = user_record_build_image_path(storage, user_record_user_name_and_realm(h), &ip_auto);
304 if (r < 0)
305 return r;
306 }
307
308 if (home_directory) {
309 hd = strdup(home_directory);
310 if (!hd)
311 return -ENOMEM;
312 }
313
314 if (file_system_type) {
315 fst = strdup(file_system_type);
316 if (!fst)
317 return -ENOMEM;
318 }
319
320 if (luks_cipher) {
321 lc = strdup(luks_cipher);
322 if (!lc)
323 return -ENOMEM;
324 }
325
326 if (luks_cipher_mode) {
327 lcm = strdup(luks_cipher_mode);
328 if (!lcm)
329 return -ENOMEM;
330 }
331
332 r = json_build(&new_binding_entry,
333 JSON_BUILD_OBJECT(
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(id128_to_uuid_string(partition_uuid, partition_uuids))),
336 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(id128_to_uuid_string(luks_uuid, luks_uuids))),
337 JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(id128_to_uuid_string(fs_uuid, fs_uuids))),
338 JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)),
339 JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)),
340 JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)),
341 JSON_BUILD_PAIR_CONDITION(!!file_system_type, "fileSystemType", JSON_BUILD_STRING(file_system_type)),
342 JSON_BUILD_PAIR_CONDITION(!!home_directory, "homeDirectory", JSON_BUILD_STRING(home_directory)),
343 JSON_BUILD_PAIR_CONDITION(uid_is_valid(uid), "uid", JSON_BUILD_UNSIGNED(uid)),
344 JSON_BUILD_PAIR_CONDITION(gid_is_valid(gid), "gid", JSON_BUILD_UNSIGNED(gid)),
345 JSON_BUILD_PAIR_CONDITION(storage >= 0, "storage", JSON_BUILD_STRING(user_storage_to_string(storage)))));
346 if (r < 0)
347 return r;
348
349 binding = json_variant_ref(json_variant_by_key(h->json, "binding"));
350 if (binding) {
351 _cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
352
353 /* Merge the new entry with an old one, if that exists */
354 be = json_variant_ref(json_variant_by_key(binding, smid));
355 if (be) {
356 r = json_variant_merge(&be, new_binding_entry);
357 if (r < 0)
358 return r;
359
360 json_variant_unref(new_binding_entry);
361 new_binding_entry = TAKE_PTR(be);
362 }
363 }
364
365 r = json_variant_set_field(&binding, smid, new_binding_entry);
366 if (r < 0)
367 return r;
368
369 r = json_variant_set_field(&h->json, "binding", binding);
370 if (r < 0)
371 return r;
372
373 if (storage >= 0)
374 h->storage = storage;
375
376 if (ip)
377 free_and_replace(h->image_path, ip);
378 if (ip_auto)
379 free_and_replace(h->image_path_auto, ip_auto);
380
381 if (!sd_id128_is_null(partition_uuid))
382 h->partition_uuid = partition_uuid;
383
384 if (!sd_id128_is_null(luks_uuid))
385 h->luks_uuid = luks_uuid;
386
387 if (!sd_id128_is_null(fs_uuid))
388 h->file_system_uuid = fs_uuid;
389
390 if (lc)
391 free_and_replace(h->luks_cipher, lc);
392 if (lcm)
393 free_and_replace(h->luks_cipher_mode, lcm);
394 if (luks_volume_key_size != UINT64_MAX)
395 h->luks_volume_key_size = luks_volume_key_size;
396
397 if (fst)
398 free_and_replace(h->file_system_type, fst);
399 if (hd)
400 free_and_replace(h->home_directory, hd);
401
402 if (uid_is_valid(uid))
403 h->uid = uid;
404 if (gid_is_valid(gid))
405 h->gid = gid;
406
407 h->mask |= USER_RECORD_BINDING;
408 return 1;
409 }
410
411 int user_record_test_home_directory(UserRecord *h) {
412 const char *hd;
413 int r;
414
415 assert(h);
416
417 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
418
419 hd = user_record_home_directory(h);
420 if (!hd)
421 return -ENXIO;
422
423 r = is_dir(hd, false);
424 if (r == -ENOENT)
425 return USER_TEST_ABSENT;
426 if (r < 0)
427 return r;
428 if (r == 0)
429 return -ENOTDIR;
430
431 r = path_is_mount_point(hd, NULL, 0);
432 if (r < 0)
433 return r;
434 if (r > 0)
435 return USER_TEST_MOUNTED;
436
437 /* If the image path and the home directory are identical, then it's OK if the directory is
438 * populated. */
439 if (IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) {
440 const char *ip;
441
442 ip = user_record_image_path(h);
443 if (ip && path_equal(ip, hd))
444 return USER_TEST_EXISTS;
445 }
446
447 /* Otherwise it's not OK */
448 r = dir_is_empty(hd);
449 if (r < 0)
450 return r;
451 if (r == 0)
452 return -EBUSY;
453
454 return USER_TEST_EXISTS;
455 }
456
457 int user_record_test_home_directory_and_warn(UserRecord *h) {
458 int r;
459
460 assert(h);
461
462 r = user_record_test_home_directory(h);
463 if (r == -ENXIO)
464 return log_error_errno(r, "User record lacks home directory, refusing.");
465 if (r == -ENOTDIR)
466 return log_error_errno(r, "Home directory %s is not a directory, refusing.", user_record_home_directory(h));
467 if (r == -EBUSY)
468 return log_error_errno(r, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h));
469 if (r < 0)
470 return log_error_errno(r, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h));
471
472 return r;
473 }
474
475 int user_record_test_image_path(UserRecord *h) {
476 const char *ip;
477 struct stat st;
478
479 assert(h);
480
481 if (user_record_storage(h) == USER_CIFS)
482 return USER_TEST_UNDEFINED;
483
484 ip = user_record_image_path(h);
485 if (!ip)
486 return -ENXIO;
487
488 if (stat(ip, &st) < 0) {
489 if (errno == ENOENT)
490 return USER_TEST_ABSENT;
491
492 return -errno;
493 }
494
495 switch (user_record_storage(h)) {
496
497 case USER_LUKS:
498 if (S_ISREG(st.st_mode))
499 return USER_TEST_EXISTS;
500 if (S_ISBLK(st.st_mode)) {
501 /* For block devices we can't really be sure if the device referenced actually is the
502 * fs we look for or some other file system (think: what does /dev/sdb1 refer
503 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
504 * case, except if the device path used is one of the paths that is based on a
505 * filesystem or partition UUID or label, because in those cases we can be sure we
506 * are referring to the right device. */
507
508 if (PATH_STARTSWITH_SET(ip,
509 "/dev/disk/by-uuid/",
510 "/dev/disk/by-partuuid/",
511 "/dev/disk/by-partlabel/",
512 "/dev/disk/by-label/"))
513 return USER_TEST_EXISTS;
514
515 return USER_TEST_MAYBE;
516 }
517
518 return -EBADFD;
519
520 case USER_CLASSIC:
521 case USER_DIRECTORY:
522 case USER_SUBVOLUME:
523 case USER_FSCRYPT:
524 if (S_ISDIR(st.st_mode))
525 return USER_TEST_EXISTS;
526
527 return -ENOTDIR;
528
529 default:
530 assert_not_reached("Unexpected record type");
531 }
532 }
533
534 int user_record_test_image_path_and_warn(UserRecord *h) {
535 int r;
536
537 assert(h);
538
539 r = user_record_test_image_path(h);
540 if (r == -ENXIO)
541 return log_error_errno(r, "User record lacks image path, refusing.");
542 if (r == -EBADFD)
543 return log_error_errno(r, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h));
544 if (r == -ENOTDIR)
545 return log_error_errno(r, "Image path %s is not a directory, refusing.", user_record_image_path(h));
546 if (r < 0)
547 return log_error_errno(r, "Failed to test whether image path %s exists: %m", user_record_image_path(h));
548
549 return r;
550 }
551
552 int user_record_test_secret(UserRecord *h, UserRecord *secret) {
553 char **i;
554 int r;
555
556 assert(h);
557
558 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
559
560 if (strv_isempty(h->hashed_password))
561 return -ENXIO;
562
563 STRV_FOREACH(i, secret->password) {
564 r = test_password_many(h->hashed_password, *i);
565 if (r < 0)
566 return r;
567 if (r > 0)
568 return 0;
569 }
570
571 return -ENOKEY;
572 }
573
574 int user_record_set_disk_size(UserRecord *h, uint64_t disk_size) {
575 _cleanup_(json_variant_unrefp) JsonVariant *new_per_machine = NULL, *midv = NULL, *midav = NULL, *ne = NULL;
576 _cleanup_free_ JsonVariant **array = NULL;
577 char smid[SD_ID128_STRING_MAX];
578 size_t idx = SIZE_MAX, n;
579 JsonVariant *per_machine;
580 sd_id128_t mid;
581 int r;
582
583 assert(h);
584
585 if (!h->json)
586 return -EUNATCH;
587
588 if (disk_size < USER_DISK_SIZE_MIN || disk_size > USER_DISK_SIZE_MAX)
589 return -ERANGE;
590
591 r = sd_id128_get_machine(&mid);
592 if (r < 0)
593 return r;
594
595 sd_id128_to_string(mid, smid);
596
597 r = json_variant_new_string(&midv, smid);
598 if (r < 0)
599 return r;
600
601 r = json_variant_new_array(&midav, (JsonVariant*[]) { midv }, 1);
602 if (r < 0)
603 return r;
604
605 per_machine = json_variant_by_key(h->json, "perMachine");
606 if (per_machine) {
607 size_t i;
608
609 if (!json_variant_is_array(per_machine))
610 return -EINVAL;
611
612 n = json_variant_elements(per_machine);
613
614 array = new(JsonVariant*, n + 1);
615 if (!array)
616 return -ENOMEM;
617
618 for (i = 0; i < n; i++) {
619 JsonVariant *m;
620
621 array[i] = json_variant_by_index(per_machine, i);
622
623 if (!json_variant_is_object(array[i]))
624 return -EINVAL;
625
626 m = json_variant_by_key(array[i], "matchMachineId");
627 if (!m) {
628 /* No machineId field? Let's ignore this, but invalidate what we found so far */
629 idx = SIZE_MAX;
630 continue;
631 }
632
633 if (json_variant_equal(m, midv) ||
634 json_variant_equal(m, midav)) {
635 /* Matches exactly what we are looking for. Let's use this */
636 idx = i;
637 continue;
638 }
639
640 r = per_machine_id_match(m, JSON_PERMISSIVE);
641 if (r < 0)
642 return r;
643 if (r > 0)
644 /* Also matches what we are looking for, but with a broader match. In this
645 * case let's ignore this entry, and add a new specific one to the end. */
646 idx = SIZE_MAX;
647 }
648
649 if (idx == SIZE_MAX)
650 idx = n++; /* Nothing suitable found, place new entry at end */
651 else
652 ne = json_variant_ref(array[idx]);
653
654 } else {
655 array = new(JsonVariant*, 1);
656 if (!array)
657 return -ENOMEM;
658
659 idx = 0;
660 n = 1;
661 }
662
663 if (!ne) {
664 r = json_variant_set_field(&ne, "matchMachineId", midav);
665 if (r < 0)
666 return r;
667 }
668
669 r = json_variant_set_field_unsigned(&ne, "diskSize", disk_size);
670 if (r < 0)
671 return r;
672
673 assert(idx < n);
674 array[idx] = ne;
675
676 r = json_variant_new_array(&new_per_machine, array, n);
677 if (r < 0)
678 return r;
679
680 r = json_variant_set_field(&h->json, "perMachine", new_per_machine);
681 if (r < 0)
682 return r;
683
684 h->disk_size = disk_size;
685 h->mask |= USER_RECORD_PER_MACHINE;
686 return 0;
687 }
688
689 int user_record_update_last_changed(UserRecord *h, bool with_password) {
690 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
691 usec_t n;
692 int r;
693
694 assert(h);
695
696 if (!h->json)
697 return -EUNATCH;
698
699 n = now(CLOCK_REALTIME);
700
701 /* refuse downgrading */
702 if (h->last_change_usec != UINT64_MAX && h->last_change_usec >= n)
703 return -ECHRNG;
704 if (h->last_password_change_usec != UINT64_MAX && h->last_password_change_usec >= n)
705 return -ECHRNG;
706
707 v = json_variant_ref(h->json);
708
709 r = json_variant_set_field_unsigned(&v, "lastChangeUSec", n);
710 if (r < 0)
711 return r;
712
713 if (with_password) {
714 r = json_variant_set_field_unsigned(&v, "lastPasswordChangeUSec", n);
715 if (r < 0)
716 return r;
717
718 h->last_password_change_usec = n;
719 }
720
721 h->last_change_usec = n;
722
723 json_variant_unref(h->json);
724 h->json = TAKE_PTR(v);
725
726 h->mask |= USER_RECORD_REGULAR;
727 return 0;
728 }
729
730 int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) {
731 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
732 _cleanup_strv_free_ char **np = NULL;
733 char **i;
734 int r;
735
736 assert(h);
737 assert(secret);
738
739 /* Initializes the hashed password list from the specified plaintext passwords */
740
741 if (extend) {
742 np = strv_copy(h->hashed_password);
743 if (!np)
744 return -ENOMEM;
745
746 strv_uniq(np);
747 }
748
749 STRV_FOREACH(i, secret) {
750 _cleanup_free_ char *salt = NULL;
751 struct crypt_data cd = {};
752 char *k;
753
754 r = make_salt(&salt);
755 if (r < 0)
756 return r;
757
758 errno = 0;
759 k = crypt_r(*i, salt, &cd);
760 if (!k)
761 return errno_or_else(EINVAL);
762
763 r = strv_extend(&np, k);
764 if (r < 0)
765 return r;
766 }
767
768 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
769
770 if (strv_isempty(np))
771 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
772 else {
773 _cleanup_(json_variant_unrefp) JsonVariant *new_array = NULL;
774
775 r = json_variant_new_array_strv(&new_array, np);
776 if (r < 0)
777 return r;
778
779 r = json_variant_set_field(&priv, "hashedPassword", new_array);
780 if (r < 0)
781 return r;
782 }
783
784 r = json_variant_set_field(&h->json, "privileged", priv);
785 if (r < 0)
786 return r;
787
788 strv_free_and_replace(h->hashed_password, np);
789
790 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
791 return 0;
792 }
793
794 int user_record_set_hashed_password(UserRecord *h, char **hashed_password) {
795 _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL;
796 _cleanup_strv_free_ char **copy = NULL;
797 int r;
798
799 assert(h);
800
801 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
802
803 if (strv_isempty(hashed_password))
804 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
805 else {
806 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
807
808 copy = strv_copy(hashed_password);
809 if (!copy)
810 return -ENOMEM;
811
812 strv_uniq(copy);
813
814 r = json_variant_new_array_strv(&array, copy);
815 if (r < 0)
816 return r;
817
818 r = json_variant_set_field(&priv, "hashedPassword", array);
819 }
820 if (r < 0)
821 return r;
822
823 r = json_variant_set_field(&h->json, "privileged", priv);
824 if (r < 0)
825 return r;
826
827 strv_free_and_replace(h->hashed_password, copy);
828
829 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
830 return 0;
831 }
832
833 int user_record_set_password(UserRecord *h, char **password, bool prepend) {
834 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
835 _cleanup_(strv_free_erasep) char **e = NULL;
836 int r;
837
838 assert(h);
839
840 if (prepend) {
841 e = strv_copy(password);
842 if (!e)
843 return -ENOMEM;
844
845 r = strv_extend_strv(&e, h->password, true);
846 if (r < 0)
847 return r;
848
849 strv_uniq(e);
850
851 if (strv_equal(h->password, e))
852 return 0;
853
854 } else {
855 if (strv_equal(h->password, password))
856 return 0;
857
858 e = strv_copy(password);
859 if (!e)
860 return -ENOMEM;
861
862 strv_uniq(e);
863 }
864
865 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
866
867 if (strv_isempty(e))
868 r = json_variant_filter(&w, STRV_MAKE("password"));
869 else {
870 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
871
872 r = json_variant_new_array_strv(&l, e);
873 if (r < 0)
874 return r;
875
876 json_variant_sensitive(l);
877
878 r = json_variant_set_field(&w, "password", l);
879 }
880 if (r < 0)
881 return r;
882
883 json_variant_sensitive(w);
884
885 r = json_variant_set_field(&h->json, "secret", w);
886 if (r < 0)
887 return r;
888
889 strv_free_and_replace(h->password, e);
890
891 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
892 return 0;
893 }
894
895 int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) {
896 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
897 _cleanup_(strv_free_erasep) char **e = NULL;
898 int r;
899
900 assert(h);
901
902 if (prepend) {
903 e = strv_copy(pin);
904 if (!e)
905 return -ENOMEM;
906
907 r = strv_extend_strv(&e, h->token_pin, true);
908 if (r < 0)
909 return r;
910
911 strv_uniq(e);
912
913 if (strv_equal(h->token_pin, e))
914 return 0;
915
916 } else {
917 if (strv_equal(h->token_pin, pin))
918 return 0;
919
920 e = strv_copy(pin);
921 if (!e)
922 return -ENOMEM;
923
924 strv_uniq(e);
925 }
926
927 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
928
929 if (strv_isempty(e))
930 r = json_variant_filter(&w, STRV_MAKE("tokenPin"));
931 else {
932 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
933
934 r = json_variant_new_array_strv(&l, e);
935 if (r < 0)
936 return r;
937
938 json_variant_sensitive(l);
939
940 r = json_variant_set_field(&w, "tokenPin", l);
941 }
942 if (r < 0)
943 return r;
944
945 json_variant_sensitive(w);
946
947 r = json_variant_set_field(&h->json, "secret", w);
948 if (r < 0)
949 return r;
950
951 strv_free_and_replace(h->token_pin, e);
952
953 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
954 return 0;
955 }
956
957 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b) {
958 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
959 int r;
960
961 assert(h);
962
963 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
964
965 if (b < 0)
966 r = json_variant_filter(&w, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
967 else
968 r = json_variant_set_field_boolean(&w, "pkcs11ProtectedAuthenticationPathPermitted", b);
969 if (r < 0)
970 return r;
971
972 if (json_variant_is_blank_object(w))
973 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
974 else {
975 json_variant_sensitive(w);
976
977 r = json_variant_set_field(&h->json, "secret", w);
978 }
979 if (r < 0)
980 return r;
981
982 h->pkcs11_protected_authentication_path_permitted = b;
983
984 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
985 return 0;
986 }
987
988 int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
989 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
990 int r;
991
992 assert(h);
993
994 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
995
996 if (b < 0)
997 r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted"));
998 else
999 r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b);
1000 if (r < 0)
1001 return r;
1002
1003 if (json_variant_is_blank_object(w))
1004 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
1005 else
1006 r = json_variant_set_field(&h->json, "secret", w);
1007 if (r < 0)
1008 return r;
1009
1010 h->fido2_user_presence_permitted = b;
1011
1012 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
1013 return 0;
1014 }
1015
1016 static bool per_machine_entry_empty(JsonVariant *v) {
1017 const char *k;
1018 _unused_ JsonVariant *e;
1019
1020 JSON_VARIANT_OBJECT_FOREACH(k, e, v)
1021 if (!STR_IN_SET(k, "matchMachineId", "matchHostname"))
1022 return false;
1023
1024 return true;
1025 }
1026
1027 int user_record_set_password_change_now(UserRecord *h, int b) {
1028 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1029 JsonVariant *per_machine;
1030 int r;
1031
1032 assert(h);
1033
1034 w = json_variant_ref(h->json);
1035
1036 if (b < 0)
1037 r = json_variant_filter(&w, STRV_MAKE("passwordChangeNow"));
1038 else
1039 r = json_variant_set_field_boolean(&w, "passwordChangeNow", b);
1040 if (r < 0)
1041 return r;
1042
1043 /* Also drop the field from all perMachine entries */
1044 per_machine = json_variant_by_key(w, "perMachine");
1045 if (per_machine) {
1046 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1047 JsonVariant *e;
1048
1049 JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
1050 _cleanup_(json_variant_unrefp) JsonVariant *z = NULL;
1051
1052 if (!json_variant_is_object(e))
1053 return -EINVAL;
1054
1055 z = json_variant_ref(e);
1056
1057 r = json_variant_filter(&z, STRV_MAKE("passwordChangeNow"));
1058 if (r < 0)
1059 return r;
1060
1061 if (per_machine_entry_empty(z))
1062 continue;
1063
1064 r = json_variant_append_array(&array, z);
1065 if (r < 0)
1066 return r;
1067 }
1068
1069 if (json_variant_is_blank_array(array))
1070 r = json_variant_filter(&w, STRV_MAKE("perMachine"));
1071 else
1072 r = json_variant_set_field(&w, "perMachine", array);
1073 if (r < 0)
1074 return r;
1075
1076 SET_FLAG(h->mask, USER_RECORD_PER_MACHINE, !json_variant_is_blank_array(array));
1077 }
1078
1079 json_variant_unref(h->json);
1080 h->json = TAKE_PTR(w);
1081
1082 h->password_change_now = b;
1083
1084 return 0;
1085 }
1086
1087 int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
1088 int r;
1089
1090 assert(h);
1091
1092 /* Merges the secrets from 'secret' into 'h'. */
1093
1094 r = user_record_set_password(h, secret->password, true);
1095 if (r < 0)
1096 return r;
1097
1098 r = user_record_set_token_pin(h, secret->token_pin, true);
1099 if (r < 0)
1100 return r;
1101
1102 if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
1103 r = user_record_set_pkcs11_protected_authentication_path_permitted(
1104 h,
1105 secret->pkcs11_protected_authentication_path_permitted);
1106 if (r < 0)
1107 return r;
1108 }
1109
1110 if (secret->fido2_user_presence_permitted >= 0) {
1111 r = user_record_set_fido2_user_presence_permitted(
1112 h,
1113 secret->fido2_user_presence_permitted);
1114 if (r < 0)
1115 return r;
1116 }
1117
1118 return 0;
1119 }
1120
1121 int user_record_good_authentication(UserRecord *h) {
1122 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1123 char buf[SD_ID128_STRING_MAX];
1124 uint64_t counter, usec;
1125 sd_id128_t mid;
1126 int r;
1127
1128 assert(h);
1129
1130 switch (h->good_authentication_counter) {
1131 case UINT64_MAX:
1132 counter = 1;
1133 break;
1134 case UINT64_MAX-1:
1135 counter = h->good_authentication_counter; /* saturate */
1136 break;
1137 default:
1138 counter = h->good_authentication_counter + 1;
1139 break;
1140 }
1141
1142 usec = now(CLOCK_REALTIME);
1143
1144 r = sd_id128_get_machine(&mid);
1145 if (r < 0)
1146 return r;
1147
1148 v = json_variant_ref(h->json);
1149 w = json_variant_ref(json_variant_by_key(v, "status"));
1150 z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
1151
1152 r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
1153 if (r < 0)
1154 return r;
1155
1156 r = json_variant_set_field_unsigned(&z, "lastGoodAuthenticationUSec", usec);
1157 if (r < 0)
1158 return r;
1159
1160 r = json_variant_set_field(&w, buf, z);
1161 if (r < 0)
1162 return r;
1163
1164 r = json_variant_set_field(&v, "status", w);
1165 if (r < 0)
1166 return r;
1167
1168 json_variant_unref(h->json);
1169 h->json = TAKE_PTR(v);
1170
1171 h->good_authentication_counter = counter;
1172 h->last_good_authentication_usec = usec;
1173
1174 h->mask |= USER_RECORD_STATUS;
1175 return 0;
1176 }
1177
1178 int user_record_bad_authentication(UserRecord *h) {
1179 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1180 char buf[SD_ID128_STRING_MAX];
1181 uint64_t counter, usec;
1182 sd_id128_t mid;
1183 int r;
1184
1185 assert(h);
1186
1187 switch (h->bad_authentication_counter) {
1188 case UINT64_MAX:
1189 counter = 1;
1190 break;
1191 case UINT64_MAX-1:
1192 counter = h->bad_authentication_counter; /* saturate */
1193 break;
1194 default:
1195 counter = h->bad_authentication_counter + 1;
1196 break;
1197 }
1198
1199 usec = now(CLOCK_REALTIME);
1200
1201 r = sd_id128_get_machine(&mid);
1202 if (r < 0)
1203 return r;
1204
1205 v = json_variant_ref(h->json);
1206 w = json_variant_ref(json_variant_by_key(v, "status"));
1207 z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
1208
1209 r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
1210 if (r < 0)
1211 return r;
1212
1213 r = json_variant_set_field_unsigned(&z, "lastBadAuthenticationUSec", usec);
1214 if (r < 0)
1215 return r;
1216
1217 r = json_variant_set_field(&w, buf, z);
1218 if (r < 0)
1219 return r;
1220
1221 r = json_variant_set_field(&v, "status", w);
1222 if (r < 0)
1223 return r;
1224
1225 json_variant_unref(h->json);
1226 h->json = TAKE_PTR(v);
1227
1228 h->bad_authentication_counter = counter;
1229 h->last_bad_authentication_usec = usec;
1230
1231 h->mask |= USER_RECORD_STATUS;
1232 return 0;
1233 }
1234
1235 int user_record_ratelimit(UserRecord *h) {
1236 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL, *z = NULL;
1237 usec_t usec, new_ratelimit_begin_usec, new_ratelimit_count;
1238 char buf[SD_ID128_STRING_MAX];
1239 sd_id128_t mid;
1240 int r;
1241
1242 assert(h);
1243
1244 usec = now(CLOCK_REALTIME);
1245
1246 if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec)
1247 /* Hmm, time is running backwards? Say no! */
1248 return 0;
1249 else if (h->ratelimit_begin_usec == UINT64_MAX ||
1250 usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
1251 /* Fresh start */
1252 new_ratelimit_begin_usec = usec;
1253 new_ratelimit_count = 1;
1254 } else if (h->ratelimit_count < user_record_ratelimit_burst(h)) {
1255 /* Count up */
1256 new_ratelimit_begin_usec = h->ratelimit_begin_usec;
1257 new_ratelimit_count = h->ratelimit_count + 1;
1258 } else
1259 /* Limit hit */
1260 return 0;
1261
1262 r = sd_id128_get_machine(&mid);
1263 if (r < 0)
1264 return r;
1265
1266 v = json_variant_ref(h->json);
1267 w = json_variant_ref(json_variant_by_key(v, "status"));
1268 z = json_variant_ref(json_variant_by_key(w, sd_id128_to_string(mid, buf)));
1269
1270 r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
1271 if (r < 0)
1272 return r;
1273
1274 r = json_variant_set_field_unsigned(&z, "rateLimitCount", new_ratelimit_count);
1275 if (r < 0)
1276 return r;
1277
1278 r = json_variant_set_field(&w, buf, z);
1279 if (r < 0)
1280 return r;
1281
1282 r = json_variant_set_field(&v, "status", w);
1283 if (r < 0)
1284 return r;
1285
1286 json_variant_unref(h->json);
1287 h->json = TAKE_PTR(v);
1288
1289 h->ratelimit_begin_usec = new_ratelimit_begin_usec;
1290 h->ratelimit_count = new_ratelimit_count;
1291
1292 h->mask |= USER_RECORD_STATUS;
1293 return 1;
1294 }
1295
1296 int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
1297 assert(hr);
1298
1299 if (hr->disposition >= 0 && hr->disposition != USER_REGULAR)
1300 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot manage anything but regular users.");
1301
1302 if (hr->storage >= 0 && !IN_SET(hr->storage, USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
1303 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has storage type this service cannot manage.");
1304
1305 if (gid_is_valid(hr->gid) && hr->uid != (uid_t) hr->gid)
1306 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User record has to have matching UID/GID fields.");
1307
1308 if (hr->service && !streq(hr->service, "io.systemd.Home"))
1309 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not accepted with service not matching io.systemd.Home.");
1310
1311 return 0;
1312 }