]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/user-record-util.c
Merge pull request #16338 from keszybz/spelling2
[thirdparty/systemd.git] / src / home / user-record-util.c
CommitLineData
70a5db58
LP
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
13int 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
106int 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];
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 r = json_build(&g->json,
137 JSON_BUILD_OBJECT(
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"))))))));
147 if (r < 0)
148 return r;
149
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;
155
156 g->mask = USER_RECORD_REGULAR|USER_RECORD_BINDING;
157 return 0;
158}
159
160int user_record_reconcile(
161 UserRecord *host,
162 UserRecord *embedded,
163 UserReconcileMode mode,
164 UserRecord **ret) {
165
166 int r, result;
167
168 /* Reconciles the identity record stored on the host with the one embedded in a $HOME
169 * directory. Returns the following error codes:
170 *
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
174 *
37b22b3b 175 * Return the new record to use, which is either the embedded record updated with the host
70a5db58
LP
176 * binding or the host record. In both cases the secret data is stripped. */
177
178 assert(host);
179 assert(embedded);
180
181 /* Make sure both records are initialized */
182 if (!host->json || !embedded->json)
183 return -EINVAL;
184
185 /* Ensure these records actually contain user data */
186 if (!(embedded->mask & host->mask & USER_RECORD_REGULAR))
187 return -EINVAL;
188
189 /* Make sure the user name and realm matches */
190 if (!user_record_compatible(host, embedded))
191 return -EREMCHG;
192
193 /* Embedded identities may not contain secrets or binding info*/
194 if ((embedded->mask & (USER_RECORD_SECRET|USER_RECORD_BINDING)) != 0)
195 return -EINVAL;
196
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))
202
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;
206
207 else if (host->last_change_usec == embedded->last_change_usec) {
208
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. */
211
212 r = user_record_masked_equal(host, embedded, USER_RECORD_REGULAR|USER_RECORD_PRIVILEGED|USER_RECORD_PER_MACHINE);
213 if (r < 0)
214 return r;
215 if (r > 0) {
216 if (mode == USER_RECONCILE_REQUIRE_NEWER)
217 return -ESTALE;
218
219 result = USER_RECONCILE_IDENTICAL;
220 } else
221 result = USER_RECONCILE_HOST_WON;
222 } else {
223 _cleanup_(json_variant_unrefp) JsonVariant *extended = NULL;
224 _cleanup_(user_record_unrefp) UserRecord *merged = NULL;
225 JsonVariant *e;
226
227 /* The embedded version is newer */
228
229 if (mode == USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL)
230 return -ESTALE;
231
232 /* Copy in the binding data */
233 extended = json_variant_ref(embedded->json);
234
235 e = json_variant_by_key(host->json, "binding");
236 if (e) {
237 r = json_variant_set_field(&extended, "binding", e);
238 if (r < 0)
239 return r;
240 }
241
242 merged = user_record_new();
243 if (!merged)
244 return -ENOMEM;
245
246 r = user_record_load(merged, extended, USER_RECORD_LOAD_MASK_SECRET);
247 if (r < 0)
248 return r;
249
250 *ret = TAKE_PTR(merged);
251 return USER_RECONCILE_EMBEDDED_WON; /* update */
252 }
253
254 /* Strip out secrets */
255 r = user_record_clone(host, USER_RECORD_LOAD_MASK_SECRET, ret);
256 if (r < 0)
257 return r;
258
259 return result;
260}
261
262int user_record_add_binding(
263 UserRecord *h,
264 UserStorage storage,
265 const char *image_path,
266 sd_id128_t partition_uuid,
267 sd_id128_t luks_uuid,
268 sd_id128_t fs_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,
274 uid_t uid,
275 gid_t gid) {
276
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];
0d5e5234 279 _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
70a5db58
LP
280 sd_id128_t mid;
281 int r;
282
283 assert(h);
284
285 if (!h->json)
286 return -EUNATCH;
287
288 r = sd_id128_get_machine(&mid);
289 if (r < 0)
290 return r;
291 sd_id128_to_string(mid, smid);
292
293 if (image_path) {
294 ip = strdup(image_path);
295 if (!ip)
296 return -ENOMEM;
0d5e5234
LP
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);
299 if (r < 0)
300 return r;
70a5db58
LP
301 }
302
303 if (home_directory) {
304 hd = strdup(home_directory);
305 if (!hd)
306 return -ENOMEM;
307 }
308
0d5e5234
LP
309 if (file_system_type) {
310 fst = strdup(file_system_type);
311 if (!fst)
312 return -ENOMEM;
313 }
314
315 if (luks_cipher) {
316 lc = strdup(luks_cipher);
317 if (!lc)
318 return -ENOMEM;
319 }
320
321 if (luks_cipher_mode) {
322 lcm = strdup(luks_cipher_mode);
323 if (!lcm)
324 return -ENOMEM;
325 }
326
70a5db58
LP
327 r = json_build(&new_binding_entry,
328 JSON_BUILD_OBJECT(
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)))));
341 if (r < 0)
342 return r;
343
344 binding = json_variant_ref(json_variant_by_key(h->json, "binding"));
345 if (binding) {
346 _cleanup_(json_variant_unrefp) JsonVariant *be = NULL;
347
348 /* Merge the new entry with an old one, if that exists */
349 be = json_variant_ref(json_variant_by_key(binding, smid));
350 if (be) {
351 r = json_variant_merge(&be, new_binding_entry);
352 if (r < 0)
353 return r;
354
355 json_variant_unref(new_binding_entry);
356 new_binding_entry = TAKE_PTR(be);
357 }
358 }
359
360 r = json_variant_set_field(&binding, smid, new_binding_entry);
361 if (r < 0)
362 return r;
363
364 r = json_variant_set_field(&h->json, "binding", binding);
365 if (r < 0)
366 return r;
367
368 if (storage >= 0)
369 h->storage = storage;
370
371 if (ip)
372 free_and_replace(h->image_path, ip);
0d5e5234
LP
373 if (ip_auto)
374 free_and_replace(h->image_path_auto, ip_auto);
70a5db58
LP
375
376 if (!sd_id128_is_null(partition_uuid))
377 h->partition_uuid = partition_uuid;
378
379 if (!sd_id128_is_null(luks_uuid))
380 h->luks_uuid = luks_uuid;
381
382 if (!sd_id128_is_null(fs_uuid))
383 h->file_system_uuid = fs_uuid;
384
0d5e5234
LP
385 if (lc)
386 free_and_replace(h->luks_cipher, lc);
387 if (lcm)
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;
391
392 if (fst)
393 free_and_replace(h->file_system_type, fst);
70a5db58
LP
394 if (hd)
395 free_and_replace(h->home_directory, hd);
396
397 if (uid_is_valid(uid))
398 h->uid = uid;
0d5e5234
LP
399 if (gid_is_valid(gid))
400 h->gid = gid;
70a5db58
LP
401
402 h->mask |= USER_RECORD_BINDING;
403 return 1;
404}
405
406int user_record_test_home_directory(UserRecord *h) {
407 const char *hd;
408 int r;
409
410 assert(h);
411
412 /* Returns one of USER_TEST_ABSENT, USER_TEST_MOUNTED, USER_TEST_EXISTS on success */
413
414 hd = user_record_home_directory(h);
415 if (!hd)
416 return -ENXIO;
417
418 r = is_dir(hd, false);
419 if (r == -ENOENT)
420 return USER_TEST_ABSENT;
421 if (r < 0)
422 return r;
423 if (r == 0)
424 return -ENOTDIR;
425
426 r = path_is_mount_point(hd, NULL, 0);
427 if (r < 0)
428 return r;
429 if (r > 0)
430 return USER_TEST_MOUNTED;
431
432 /* If the image path and the home directory are identical, then it's OK if the directory is
433 * populated. */
434 if (IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT)) {
435 const char *ip;
436
437 ip = user_record_image_path(h);
438 if (ip && path_equal(ip, hd))
439 return USER_TEST_EXISTS;
440 }
441
442 /* Otherwise it's not OK */
443 r = dir_is_empty(hd);
444 if (r < 0)
445 return r;
446 if (r == 0)
447 return -EBUSY;
448
449 return USER_TEST_EXISTS;
450}
451
452int user_record_test_home_directory_and_warn(UserRecord *h) {
453 int r;
454
455 assert(h);
456
457 r = user_record_test_home_directory(h);
458 if (r == -ENXIO)
459 return log_error_errno(r, "User record lacks home directory, refusing.");
460 if (r == -ENOTDIR)
461 return log_error_errno(r, "Home directory %s is not a directory, refusing.", user_record_home_directory(h));
462 if (r == -EBUSY)
463 return log_error_errno(r, "Home directory %s exists, is not mounted but populated, refusing.", user_record_home_directory(h));
464 if (r < 0)
465 return log_error_errno(r, "Failed to test whether the home directory %s exists: %m", user_record_home_directory(h));
466
467 return r;
468}
469
470int user_record_test_image_path(UserRecord *h) {
471 const char *ip;
472 struct stat st;
473
474 assert(h);
475
476 if (user_record_storage(h) == USER_CIFS)
477 return USER_TEST_UNDEFINED;
478
479 ip = user_record_image_path(h);
480 if (!ip)
481 return -ENXIO;
482
483 if (stat(ip, &st) < 0) {
484 if (errno == ENOENT)
485 return USER_TEST_ABSENT;
486
487 return -errno;
488 }
489
490 switch (user_record_storage(h)) {
491
492 case USER_LUKS:
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
162392b7 498 * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
70a5db58
LP
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. */
502
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;
509
510 return USER_TEST_MAYBE;
511 }
512
513 return -EBADFD;
514
515 case USER_CLASSIC:
516 case USER_DIRECTORY:
517 case USER_SUBVOLUME:
518 case USER_FSCRYPT:
519 if (S_ISDIR(st.st_mode))
520 return USER_TEST_EXISTS;
521
522 return -ENOTDIR;
523
524 default:
525 assert_not_reached("Unexpected record type");
526 }
527}
528
529int user_record_test_image_path_and_warn(UserRecord *h) {
530 int r;
531
532 assert(h);
533
534 r = user_record_test_image_path(h);
535 if (r == -ENXIO)
536 return log_error_errno(r, "User record lacks image path, refusing.");
537 if (r == -EBADFD)
538 return log_error_errno(r, "Image path %s is not a regular file or block device, refusing.", user_record_image_path(h));
539 if (r == -ENOTDIR)
540 return log_error_errno(r, "Image path %s is not a directory, refusing.", user_record_image_path(h));
541 if (r < 0)
542 return log_error_errno(r, "Failed to test whether image path %s exists: %m", user_record_image_path(h));
543
544 return r;
545}
546
547int user_record_test_secret(UserRecord *h, UserRecord *secret) {
548 char **i;
549 int r;
550
551 assert(h);
552
553 /* Checks whether any of the specified passwords matches any of the hashed passwords of the entry */
554
555 if (strv_isempty(h->hashed_password))
556 return -ENXIO;
557
558 STRV_FOREACH(i, secret->password) {
559 r = test_password_many(h->hashed_password, *i);
560 if (r < 0)
561 return r;
562 if (r > 0)
563 return 0;
564 }
565
566 return -ENOKEY;
567}
568
569int 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;
575 sd_id128_t mid;
576 int r;
577
578 assert(h);
579
580 if (!h->json)
581 return -EUNATCH;
582
583 if (disk_size < USER_DISK_SIZE_MIN || disk_size > USER_DISK_SIZE_MAX)
584 return -ERANGE;
585
586 r = sd_id128_get_machine(&mid);
587 if (r < 0)
588 return r;
589
590 sd_id128_to_string(mid, smid);
591
592 r = json_variant_new_string(&midv, smid);
593 if (r < 0)
594 return r;
595
596 r = json_variant_new_array(&midav, (JsonVariant*[]) { midv }, 1);
597 if (r < 0)
598 return r;
599
600 per_machine = json_variant_by_key(h->json, "perMachine");
601 if (per_machine) {
602 size_t i;
603
604 if (!json_variant_is_array(per_machine))
605 return -EINVAL;
606
607 n = json_variant_elements(per_machine);
608
609 array = new(JsonVariant*, n + 1);
610 if (!array)
611 return -ENOMEM;
612
613 for (i = 0; i < n; i++) {
614 JsonVariant *m;
615
616 array[i] = json_variant_by_index(per_machine, i);
617
618 if (!json_variant_is_object(array[i]))
619 return -EINVAL;
620
621 m = json_variant_by_key(array[i], "matchMachineId");
622 if (!m) {
623 /* No machineId field? Let's ignore this, but invalidate what we found so far */
624 idx = SIZE_MAX;
625 continue;
626 }
627
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 */
631 idx = i;
632 continue;
633 }
634
635 r = per_machine_id_match(m, JSON_PERMISSIVE);
636 if (r < 0)
637 return r;
638 if (r > 0)
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. */
641 idx = SIZE_MAX;
642 }
643
644 if (idx == SIZE_MAX)
645 idx = n++; /* Nothing suitable found, place new entry at end */
646 else
647 ne = json_variant_ref(array[idx]);
648
649 } else {
650 array = new(JsonVariant*, 1);
651 if (!array)
652 return -ENOMEM;
653
654 idx = 0;
655 n = 1;
656 }
657
658 if (!ne) {
659 r = json_variant_set_field(&ne, "matchMachineId", midav);
660 if (r < 0)
661 return r;
662 }
663
664 r = json_variant_set_field_unsigned(&ne, "diskSize", disk_size);
665 if (r < 0)
666 return r;
667
668 assert(idx < n);
669 array[idx] = ne;
670
671 r = json_variant_new_array(&new_per_machine, array, n);
672 if (r < 0)
673 return r;
674
675 r = json_variant_set_field(&h->json, "perMachine", new_per_machine);
676 if (r < 0)
677 return r;
678
679 h->disk_size = disk_size;
680 h->mask |= USER_RECORD_PER_MACHINE;
681 return 0;
682}
683
684int user_record_update_last_changed(UserRecord *h, bool with_password) {
685 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
686 usec_t n;
687 int r;
688
689 assert(h);
690
691 if (!h->json)
692 return -EUNATCH;
693
694 n = now(CLOCK_REALTIME);
695
696 /* refuse downgrading */
697 if (h->last_change_usec != UINT64_MAX && h->last_change_usec >= n)
698 return -ECHRNG;
699 if (h->last_password_change_usec != UINT64_MAX && h->last_password_change_usec >= n)
700 return -ECHRNG;
701
702 v = json_variant_ref(h->json);
703
704 r = json_variant_set_field_unsigned(&v, "lastChangeUSec", n);
705 if (r < 0)
706 return r;
707
708 if (with_password) {
709 r = json_variant_set_field_unsigned(&v, "lastPasswordChangeUSec", n);
710 if (r < 0)
711 return r;
712
713 h->last_password_change_usec = n;
714 }
715
716 h->last_change_usec = n;
717
718 json_variant_unref(h->json);
719 h->json = TAKE_PTR(v);
720
721 h->mask |= USER_RECORD_REGULAR;
722 return 0;
723}
724
725int 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;
728 char **i;
729 int r;
730
731 assert(h);
732 assert(secret);
733
734 /* Initializes the hashed password list from the specified plaintext passwords */
735
736 if (extend) {
737 np = strv_copy(h->hashed_password);
738 if (!np)
739 return -ENOMEM;
740
741 strv_uniq(np);
742 }
743
744 STRV_FOREACH(i, secret) {
745 _cleanup_free_ char *salt = NULL;
746 struct crypt_data cd = {};
747 char *k;
748
749 r = make_salt(&salt);
750 if (r < 0)
751 return r;
752
753 errno = 0;
754 k = crypt_r(*i, salt, &cd);
755 if (!k)
756 return errno_or_else(EINVAL);
757
758 r = strv_extend(&np, k);
759 if (r < 0)
760 return r;
761 }
762
763 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
764
765 if (strv_isempty(np))
766 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
767 else {
768 _cleanup_(json_variant_unrefp) JsonVariant *new_array = NULL;
769
770 r = json_variant_new_array_strv(&new_array, np);
771 if (r < 0)
772 return r;
773
774 r = json_variant_set_field(&priv, "hashedPassword", new_array);
02cec156
YW
775 if (r < 0)
776 return r;
70a5db58
LP
777 }
778
779 r = json_variant_set_field(&h->json, "privileged", priv);
780 if (r < 0)
781 return r;
782
783 strv_free_and_replace(h->hashed_password, np);
784
785 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
786 return 0;
787}
788
789int 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;
792 int r;
793
794 assert(h);
795
796 priv = json_variant_ref(json_variant_by_key(h->json, "privileged"));
797
798 if (strv_isempty(hashed_password))
799 r = json_variant_filter(&priv, STRV_MAKE("hashedPassword"));
800 else {
801 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
802
803 copy = strv_copy(hashed_password);
804 if (!copy)
805 return -ENOMEM;
806
807 strv_uniq(copy);
808
809 r = json_variant_new_array_strv(&array, copy);
810 if (r < 0)
811 return r;
812
813 r = json_variant_set_field(&priv, "hashedPassword", array);
814 }
815 if (r < 0)
816 return r;
817
818 r = json_variant_set_field(&h->json, "privileged", priv);
819 if (r < 0)
820 return r;
821
822 strv_free_and_replace(h->hashed_password, copy);
823
824 SET_FLAG(h->mask, USER_RECORD_PRIVILEGED, !json_variant_is_blank_object(priv));
825 return 0;
826}
827
828int 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;
831 int r;
832
833 assert(h);
834
835 if (prepend) {
836 e = strv_copy(password);
837 if (!e)
838 return -ENOMEM;
839
840 r = strv_extend_strv(&e, h->password, true);
841 if (r < 0)
842 return r;
843
844 strv_uniq(e);
845
846 if (strv_equal(h->password, e))
847 return 0;
848
849 } else {
850 if (strv_equal(h->password, password))
851 return 0;
852
853 e = strv_copy(password);
854 if (!e)
855 return -ENOMEM;
856
857 strv_uniq(e);
858 }
859
860 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
861
862 if (strv_isempty(e))
863 r = json_variant_filter(&w, STRV_MAKE("password"));
864 else {
865 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
866
867 r = json_variant_new_array_strv(&l, e);
868 if (r < 0)
869 return r;
870
871 json_variant_sensitive(l);
872
873 r = json_variant_set_field(&w, "password", l);
874 }
875 if (r < 0)
876 return r;
877
560a3e5d
LP
878 json_variant_sensitive(w);
879
70a5db58
LP
880 r = json_variant_set_field(&h->json, "secret", w);
881 if (r < 0)
882 return r;
883
884 strv_free_and_replace(h->password, e);
885
886 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
887 return 0;
888}
889
890int 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;
893 int r;
894
895 assert(h);
896
897 if (prepend) {
898 e = strv_copy(pin);
899 if (!e)
900 return -ENOMEM;
901
902 r = strv_extend_strv(&e, h->pkcs11_pin, true);
903 if (r < 0)
904 return r;
905
906 strv_uniq(e);
907
908 if (strv_equal(h->pkcs11_pin, e))
909 return 0;
910
911 } else {
912 if (strv_equal(h->pkcs11_pin, pin))
913 return 0;
914
915 e = strv_copy(pin);
916 if (!e)
917 return -ENOMEM;
918
919 strv_uniq(e);
920 }
921
922 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
923
924 if (strv_isempty(e))
925 r = json_variant_filter(&w, STRV_MAKE("pkcs11Pin"));
926 else {
927 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
928
929 r = json_variant_new_array_strv(&l, e);
930 if (r < 0)
931 return r;
932
933 json_variant_sensitive(l);
934
935 r = json_variant_set_field(&w, "pkcs11Pin", l);
936 }
937 if (r < 0)
938 return r;
939
560a3e5d
LP
940 json_variant_sensitive(w);
941
70a5db58
LP
942 r = json_variant_set_field(&h->json, "secret", w);
943 if (r < 0)
944 return r;
945
946 strv_free_and_replace(h->pkcs11_pin, e);
947
948 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
949 return 0;
950}
951
952int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b) {
953 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
954 int r;
955
956 assert(h);
957
958 w = json_variant_ref(json_variant_by_key(h->json, "secret"));
959
960 if (b < 0)
961 r = json_variant_filter(&w, STRV_MAKE("pkcs11ProtectedAuthenticationPathPermitted"));
962 else
963 r = json_variant_set_field_boolean(&w, "pkcs11ProtectedAuthenticationPathPermitted", b);
964 if (r < 0)
965 return r;
966
967 if (json_variant_is_blank_object(w))
968 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
560a3e5d
LP
969 else {
970 json_variant_sensitive(w);
971
70a5db58 972 r = json_variant_set_field(&h->json, "secret", w);
560a3e5d 973 }
70a5db58
LP
974 if (r < 0)
975 return r;
976
977 h->pkcs11_protected_authentication_path_permitted = b;
978
979 SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
980 return 0;
981}
982
983static bool per_machine_entry_empty(JsonVariant *v) {
984 const char *k;
985 _unused_ JsonVariant *e;
986
987 JSON_VARIANT_OBJECT_FOREACH(k, e, v)
988 if (!STR_IN_SET(k, "matchMachineId", "matchHostname"))
989 return false;
990
991 return true;
992}
993
994int user_record_set_password_change_now(UserRecord *h, int b) {
995 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
996 JsonVariant *per_machine;
997 int r;
998
999 assert(h);
1000
1001 w = json_variant_ref(h->json);
1002
1003 if (b < 0)
1004 r = json_variant_filter(&w, STRV_MAKE("passwordChangeNow"));
1005 else
1006 r = json_variant_set_field_boolean(&w, "passwordChangeNow", b);
1007 if (r < 0)
1008 return r;
1009
1010 /* Also drop the field from all perMachine entries */
1011 per_machine = json_variant_by_key(w, "perMachine");
1012 if (per_machine) {
1013 _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
1014 JsonVariant *e;
1015
1016 JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
1017 _cleanup_(json_variant_unrefp) JsonVariant *z = NULL;
1018
1019 if (!json_variant_is_object(e))
1020 return -EINVAL;
1021
1022 z = json_variant_ref(e);
1023
1024 r = json_variant_filter(&z, STRV_MAKE("passwordChangeNow"));
1025 if (r < 0)
1026 return r;
1027
1028 if (per_machine_entry_empty(z))
1029 continue;
1030
1031 r = json_variant_append_array(&array, z);
1032 if (r < 0)
1033 return r;
1034 }
1035
1036 if (json_variant_is_blank_array(array))
1037 r = json_variant_filter(&w, STRV_MAKE("perMachine"));
1038 else
1039 r = json_variant_set_field(&w, "perMachine", array);
1040 if (r < 0)
1041 return r;
1042
1043 SET_FLAG(h->mask, USER_RECORD_PER_MACHINE, !json_variant_is_blank_array(array));
1044 }
1045
1046 json_variant_unref(h->json);
1047 h->json = TAKE_PTR(w);
1048
1049 h->password_change_now = b;
1050
1051 return 0;
1052}
1053
1054int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
1055 int r;
1056
1057 assert(h);
1058
1059 /* Merges the secrets from 'secret' into 'h'. */
1060
1061 r = user_record_set_password(h, secret->password, true);
1062 if (r < 0)
1063 return r;
1064
1065 r = user_record_set_pkcs11_pin(h, secret->pkcs11_pin, true);
1066 if (r < 0)
1067 return r;
1068
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);
1071 if (r < 0)
1072 return r;
1073 }
1074
1075 return 0;
1076}
1077
1078int 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;
1082 sd_id128_t mid;
1083 int r;
1084
1085 assert(h);
1086
1087 switch (h->good_authentication_counter) {
1088 case UINT64_MAX:
1089 counter = 1;
1090 break;
1091 case UINT64_MAX-1:
1092 counter = h->good_authentication_counter; /* saturate */
1093 break;
1094 default:
1095 counter = h->good_authentication_counter + 1;
1096 break;
1097 }
1098
1099 usec = now(CLOCK_REALTIME);
1100
1101 r = sd_id128_get_machine(&mid);
1102 if (r < 0)
1103 return r;
1104
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)));
1108
1109 r = json_variant_set_field_unsigned(&z, "goodAuthenticationCounter", counter);
1110 if (r < 0)
1111 return r;
1112
1113 r = json_variant_set_field_unsigned(&z, "lastGoodAuthenticationUSec", usec);
1114 if (r < 0)
1115 return r;
1116
1117 r = json_variant_set_field(&w, buf, z);
1118 if (r < 0)
1119 return r;
1120
1121 r = json_variant_set_field(&v, "status", w);
1122 if (r < 0)
1123 return r;
1124
1125 json_variant_unref(h->json);
1126 h->json = TAKE_PTR(v);
1127
1128 h->good_authentication_counter = counter;
1129 h->last_good_authentication_usec = usec;
1130
1131 h->mask |= USER_RECORD_STATUS;
1132 return 0;
1133}
1134
1135int 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;
1139 sd_id128_t mid;
1140 int r;
1141
1142 assert(h);
1143
1144 switch (h->bad_authentication_counter) {
1145 case UINT64_MAX:
1146 counter = 1;
1147 break;
1148 case UINT64_MAX-1:
1149 counter = h->bad_authentication_counter; /* saturate */
1150 break;
1151 default:
1152 counter = h->bad_authentication_counter + 1;
1153 break;
1154 }
1155
1156 usec = now(CLOCK_REALTIME);
1157
1158 r = sd_id128_get_machine(&mid);
1159 if (r < 0)
1160 return r;
1161
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)));
1165
1166 r = json_variant_set_field_unsigned(&z, "badAuthenticationCounter", counter);
1167 if (r < 0)
1168 return r;
1169
1170 r = json_variant_set_field_unsigned(&z, "lastBadAuthenticationUSec", usec);
1171 if (r < 0)
1172 return r;
1173
1174 r = json_variant_set_field(&w, buf, z);
1175 if (r < 0)
1176 return r;
1177
1178 r = json_variant_set_field(&v, "status", w);
1179 if (r < 0)
1180 return r;
1181
1182 json_variant_unref(h->json);
1183 h->json = TAKE_PTR(v);
1184
1185 h->bad_authentication_counter = counter;
1186 h->last_bad_authentication_usec = usec;
1187
1188 h->mask |= USER_RECORD_STATUS;
1189 return 0;
1190}
1191
1192int 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];
1196 sd_id128_t mid;
1197 int r;
1198
1199 assert(h);
1200
1201 usec = now(CLOCK_REALTIME);
1202
1203 if (h->ratelimit_begin_usec != UINT64_MAX && h->ratelimit_begin_usec > usec)
1204 /* Hmm, time is running backwards? Say no! */
1205 return 0;
1206 else if (h->ratelimit_begin_usec == UINT64_MAX ||
1207 usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h)) <= usec) {
1208 /* Fresh start */
1209 new_ratelimit_begin_usec = usec;
1210 new_ratelimit_count = 1;
1211 } else if (h->ratelimit_count < user_record_ratelimit_burst(h)) {
1212 /* Count up */
1213 new_ratelimit_begin_usec = h->ratelimit_begin_usec;
1214 new_ratelimit_count = h->ratelimit_count + 1;
1215 } else
1216 /* Limit hit */
1217 return 0;
1218
1219 r = sd_id128_get_machine(&mid);
1220 if (r < 0)
1221 return r;
1222
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)));
1226
1227 r = json_variant_set_field_unsigned(&z, "rateLimitBeginUSec", new_ratelimit_begin_usec);
1228 if (r < 0)
1229 return r;
1230
1231 r = json_variant_set_field_unsigned(&z, "rateLimitCount", new_ratelimit_count);
1232 if (r < 0)
1233 return r;
1234
1235 r = json_variant_set_field(&w, buf, z);
1236 if (r < 0)
1237 return r;
1238
1239 r = json_variant_set_field(&v, "status", w);
1240 if (r < 0)
1241 return r;
1242
1243 json_variant_unref(h->json);
1244 h->json = TAKE_PTR(v);
1245
1246 h->ratelimit_begin_usec = new_ratelimit_begin_usec;
1247 h->ratelimit_count = new_ratelimit_count;
1248
1249 h->mask |= USER_RECORD_STATUS;
1250 return 1;
1251}
1252
1253int user_record_is_supported(UserRecord *hr, sd_bus_error *error) {
1254 assert(hr);
1255
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.");
1258
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.");
1261
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.");
1264
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.");
1267
1268 return 0;
1269}