]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/user-record.c
Merge pull request #20303 from andir/sysconfig-example
[thirdparty/systemd.git] / src / shared / user-record.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
71d0b9d4
LP
2
3#include <sys/mount.h>
4
5#include "cgroup-util.h"
6#include "dns-domain.h"
7#include "env-util.h"
53393c89
ZJS
8#include "fd-util.h"
9#include "fileio.h"
71d0b9d4
LP
10#include "fs-util.h"
11#include "hexdecoct.h"
12#include "hostname-util.h"
13#include "memory-util.h"
14#include "path-util.h"
15#include "pkcs11-util.h"
16#include "rlimit-util.h"
17#include "stat-util.h"
18#include "string-table.h"
19#include "strv.h"
20#include "user-record.h"
21#include "user-util.h"
22
23#define DEFAULT_RATELIMIT_BURST 30
24#define DEFAULT_RATELIMIT_INTERVAL_USEC (1*USEC_PER_MINUTE)
25
53393c89
ZJS
26#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
27static int parse_alloc_uid(const char *path, const char *name, const char *t, uid_t *ret_uid) {
28 uid_t uid;
29 int r;
30
31 r = parse_uid(t, &uid);
32 if (r < 0)
33 return log_debug_errno(r, "%s: failed to parse %s %s, ignoring: %m", path, name, t);
34 if (uid == 0)
35 uid = 1;
36
37 *ret_uid = uid;
38 return 0;
39}
aa25270c 40#endif
53393c89 41
aa25270c 42int read_login_defs(UGIDAllocationRange *ret_defs, const char *path, const char *root) {
53393c89 43 UGIDAllocationRange defs = {
fc1a5d1a 44 .system_alloc_uid_min = SYSTEM_ALLOC_UID_MIN,
53393c89 45 .system_uid_max = SYSTEM_UID_MAX,
fc1a5d1a 46 .system_alloc_gid_min = SYSTEM_ALLOC_GID_MIN,
53393c89
ZJS
47 .system_gid_max = SYSTEM_GID_MAX,
48 };
aa25270c
ZJS
49
50#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
51 _cleanup_fclose_ FILE *f = NULL;
53393c89
ZJS
52 int r;
53
54 if (!path)
55 path = "/etc/login.defs";
56
aa25270c 57 r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &f, NULL);
53393c89
ZJS
58 if (r == -ENOENT)
59 goto assign;
60 if (r < 0)
61 return log_debug_errno(r, "Failed to open %s: %m", path);
62
63 for (;;) {
64 _cleanup_free_ char *line = NULL;
65 char *t;
66
67 r = read_line(f, LINE_MAX, &line);
68 if (r < 0)
69 return log_debug_errno(r, "Failed to read %s: %m", path);
70 if (r == 0)
71 break;
72
fc1a5d1a
ZJS
73 if ((t = first_word(line, "SYS_UID_MIN")))
74 (void) parse_alloc_uid(path, "SYS_UID_MIN", t, &defs.system_alloc_uid_min);
75 else if ((t = first_word(line, "SYS_UID_MAX")))
53393c89 76 (void) parse_alloc_uid(path, "SYS_UID_MAX", t, &defs.system_uid_max);
fc1a5d1a
ZJS
77 else if ((t = first_word(line, "SYS_GID_MIN")))
78 (void) parse_alloc_uid(path, "SYS_GID_MIN", t, &defs.system_alloc_gid_min);
53393c89
ZJS
79 else if ((t = first_word(line, "SYS_GID_MAX")))
80 (void) parse_alloc_uid(path, "SYS_GID_MAX", t, &defs.system_gid_max);
81 }
82
83 assign:
fc1a5d1a
ZJS
84 if (defs.system_alloc_uid_min > defs.system_uid_max) {
85 log_debug("%s: SYS_UID_MIN > SYS_UID_MAX, resetting.", path);
86 defs.system_alloc_uid_min = MIN(defs.system_uid_max - 1, (uid_t) SYSTEM_ALLOC_UID_MIN);
87 /* Look at sys_uid_max to make sure sys_uid_min..sys_uid_max remains a valid range. */
88 }
89 if (defs.system_alloc_gid_min > defs.system_gid_max) {
90 log_debug("%s: SYS_GID_MIN > SYS_GID_MAX, resetting.", path);
91 defs.system_alloc_gid_min = MIN(defs.system_gid_max - 1, (gid_t) SYSTEM_ALLOC_GID_MIN);
92 /* Look at sys_gid_max to make sure sys_gid_min..sys_gid_max remains a valid range. */
93 }
aa25270c 94#endif
fc1a5d1a 95
53393c89
ZJS
96 *ret_defs = defs;
97 return 0;
98}
53393c89
ZJS
99
100const UGIDAllocationRange *acquire_ugid_allocation_range(void) {
101#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
102 static thread_local UGIDAllocationRange defs = {
103#else
104 static const UGIDAllocationRange defs = {
105#endif
fc1a5d1a 106 .system_alloc_uid_min = SYSTEM_ALLOC_UID_MIN,
53393c89 107 .system_uid_max = SYSTEM_UID_MAX,
fc1a5d1a 108 .system_alloc_gid_min = SYSTEM_ALLOC_GID_MIN,
53393c89
ZJS
109 .system_gid_max = SYSTEM_GID_MAX,
110 };
111
112#if ENABLE_COMPAT_MUTABLE_UID_BOUNDARIES
113 /* This function will ignore failure to read the file, so it should only be called from places where
114 * we don't crucially depend on the answer. In other words, it's appropriate for journald, but
115 * probably not for sysusers. */
116
117 static thread_local bool initialized = false;
118
119 if (!initialized) {
aa25270c 120 (void) read_login_defs(&defs, NULL, NULL);
53393c89
ZJS
121 initialized = true;
122 }
123#endif
124
125 return &defs;
126}
127
128bool uid_is_system(uid_t uid) {
129 const UGIDAllocationRange *defs;
130 assert_se(defs = acquire_ugid_allocation_range());
131
132 return uid <= defs->system_uid_max;
133}
134
135bool gid_is_system(gid_t gid) {
136 const UGIDAllocationRange *defs;
137 assert_se(defs = acquire_ugid_allocation_range());
138
139 return gid <= defs->system_gid_max;
140}
141
71d0b9d4
LP
142UserRecord* user_record_new(void) {
143 UserRecord *h;
144
145 h = new(UserRecord, 1);
146 if (!h)
147 return NULL;
148
149 *h = (UserRecord) {
150 .n_ref = 1,
151 .disposition = _USER_DISPOSITION_INVALID,
152 .last_change_usec = UINT64_MAX,
153 .last_password_change_usec = UINT64_MAX,
154 .umask = MODE_INVALID,
155 .nice_level = INT_MAX,
156 .not_before_usec = UINT64_MAX,
157 .not_after_usec = UINT64_MAX,
158 .locked = -1,
159 .storage = _USER_STORAGE_INVALID,
160 .access_mode = MODE_INVALID,
161 .disk_size = UINT64_MAX,
162 .disk_size_relative = UINT64_MAX,
163 .tasks_max = UINT64_MAX,
164 .memory_high = UINT64_MAX,
165 .memory_max = UINT64_MAX,
166 .cpu_weight = UINT64_MAX,
167 .io_weight = UINT64_MAX,
168 .uid = UID_INVALID,
169 .gid = GID_INVALID,
170 .nodev = true,
171 .nosuid = true,
172 .luks_discard = -1,
5e86c82a 173 .luks_offline_discard = -1,
71d0b9d4
LP
174 .luks_volume_key_size = UINT64_MAX,
175 .luks_pbkdf_time_cost_usec = UINT64_MAX,
176 .luks_pbkdf_memory_cost = UINT64_MAX,
177 .luks_pbkdf_parallel_threads = UINT64_MAX,
178 .disk_usage = UINT64_MAX,
179 .disk_free = UINT64_MAX,
180 .disk_ceiling = UINT64_MAX,
181 .disk_floor = UINT64_MAX,
182 .signed_locally = -1,
183 .good_authentication_counter = UINT64_MAX,
184 .bad_authentication_counter = UINT64_MAX,
185 .last_good_authentication_usec = UINT64_MAX,
186 .last_bad_authentication_usec = UINT64_MAX,
187 .ratelimit_begin_usec = UINT64_MAX,
188 .ratelimit_count = UINT64_MAX,
189 .ratelimit_interval_usec = UINT64_MAX,
190 .ratelimit_burst = UINT64_MAX,
191 .removable = -1,
192 .enforce_password_policy = -1,
193 .auto_login = -1,
194 .stop_delay_usec = UINT64_MAX,
195 .kill_processes = -1,
196 .password_change_min_usec = UINT64_MAX,
197 .password_change_max_usec = UINT64_MAX,
198 .password_change_warn_usec = UINT64_MAX,
199 .password_change_inactive_usec = UINT64_MAX,
200 .password_change_now = -1,
201 .pkcs11_protected_authentication_path_permitted = -1,
7b78db28 202 .fido2_user_presence_permitted = -1,
17e7561a 203 .fido2_user_verification_permitted = -1,
71d0b9d4
LP
204 };
205
206 return h;
207}
208
209static void pkcs11_encrypted_key_done(Pkcs11EncryptedKey *k) {
210 if (!k)
211 return;
212
213 free(k->uri);
214 erase_and_free(k->data);
215 erase_and_free(k->hashed_password);
216}
217
5e4fa456
LP
218static void fido2_hmac_credential_done(Fido2HmacCredential *c) {
219 if (!c)
220 return;
221
222 free(c->id);
223}
224
225static void fido2_hmac_salt_done(Fido2HmacSalt *s) {
226 if (!s)
227 return;
228
229 fido2_hmac_credential_done(&s->credential);
230 erase_and_free(s->salt);
231 erase_and_free(s->hashed_password);
232}
233
b3a97fd3
LP
234static void recovery_key_done(RecoveryKey *k) {
235 if (!k)
236 return;
237
238 free(k->type);
239 erase_and_free(k->hashed_password);
240}
241
71d0b9d4
LP
242static UserRecord* user_record_free(UserRecord *h) {
243 if (!h)
244 return NULL;
245
246 free(h->user_name);
247 free(h->realm);
248 free(h->user_name_and_realm_auto);
249 free(h->real_name);
250 free(h->email_address);
251 erase_and_free(h->password_hint);
252 free(h->location);
253 free(h->icon_name);
254
255 free(h->shell);
256
257 strv_free(h->environment);
258 free(h->time_zone);
259 free(h->preferred_language);
260 rlimit_free_all(h->rlimits);
261
262 free(h->skeleton_directory);
263
264 strv_free_erase(h->hashed_password);
265 strv_free_erase(h->ssh_authorized_keys);
266 strv_free_erase(h->password);
c0bde0d2 267 strv_free_erase(h->token_pin);
71d0b9d4
LP
268
269 free(h->cifs_service);
270 free(h->cifs_user_name);
271 free(h->cifs_domain);
272
273 free(h->image_path);
274 free(h->image_path_auto);
275 free(h->home_directory);
276 free(h->home_directory_auto);
277
278 strv_free(h->member_of);
279
280 free(h->file_system_type);
281 free(h->luks_cipher);
282 free(h->luks_cipher_mode);
283 free(h->luks_pbkdf_hash_algorithm);
284 free(h->luks_pbkdf_type);
285
286 free(h->state);
287 free(h->service);
288
289 strv_free(h->pkcs11_token_uri);
290 for (size_t i = 0; i < h->n_pkcs11_encrypted_key; i++)
291 pkcs11_encrypted_key_done(h->pkcs11_encrypted_key + i);
292 free(h->pkcs11_encrypted_key);
293
5e4fa456
LP
294 for (size_t i = 0; i < h->n_fido2_hmac_credential; i++)
295 fido2_hmac_credential_done(h->fido2_hmac_credential + i);
296 for (size_t i = 0; i < h->n_fido2_hmac_salt; i++)
297 fido2_hmac_salt_done(h->fido2_hmac_salt + i);
298
b3a97fd3
LP
299 strv_free(h->recovery_key_type);
300 for (size_t i = 0; i < h->n_recovery_key; i++)
301 recovery_key_done(h->recovery_key + i);
302
71d0b9d4
LP
303 json_variant_unref(h->json);
304
305 return mfree(h);
306}
307
308DEFINE_TRIVIAL_REF_UNREF_FUNC(UserRecord, user_record, user_record_free);
309
310int json_dispatch_realm(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
311 char **s = userdata;
312 const char *n;
313 int r;
314
315 if (json_variant_is_null(variant)) {
316 *s = mfree(*s);
317 return 0;
318 }
319
320 if (!json_variant_is_string(variant))
321 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
322
323 n = json_variant_string(variant);
324 r = dns_name_is_valid(n);
325 if (r < 0)
326 return json_log(variant, flags, r, "Failed to check if JSON field '%s' is a valid DNS domain.", strna(name));
327 if (r == 0)
328 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid DNS domain.", strna(name));
329
330 r = free_and_strdup(s, n);
331 if (r < 0)
332 return json_log(variant, flags, r, "Failed to allocate string: %m");
333
334 return 0;
335}
336
0bb43080 337int json_dispatch_gecos(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
71d0b9d4
LP
338 char **s = userdata;
339 const char *n;
71d0b9d4
LP
340
341 if (json_variant_is_null(variant)) {
342 *s = mfree(*s);
343 return 0;
344 }
345
346 if (!json_variant_is_string(variant))
347 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
348
349 n = json_variant_string(variant);
5cd12aba
LP
350 if (valid_gecos(n)) {
351 if (free_and_strdup(s, n) < 0)
352 return json_log_oom(variant, flags);
353 } else {
354 _cleanup_free_ char *m = NULL;
71d0b9d4 355
5cd12aba
LP
356 json_log(variant, flags|JSON_DEBUG, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible string, mangling.", strna(name));
357
358 m = mangle_gecos(n);
359 if (!m)
360 return json_log_oom(variant, flags);
361
362 free_and_replace(*s, m);
363 }
71d0b9d4
LP
364
365 return 0;
366}
367
368static int json_dispatch_nice(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
369 int *nl = userdata;
370 intmax_t m;
371
372 if (json_variant_is_null(variant)) {
373 *nl = INT_MAX;
374 return 0;
375 }
376
377 if (!json_variant_is_integer(variant))
378 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
379
380 m = json_variant_integer(variant);
381 if (m < PRIO_MIN || m >= PRIO_MAX)
382 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not a valid nice level.", strna(name));
383
384 *nl = m;
385 return 0;
386}
387
388static int json_dispatch_rlimit_value(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
389 rlim_t *ret = userdata;
390
391 if (json_variant_is_null(variant))
392 *ret = RLIM_INFINITY;
393 else if (json_variant_is_unsigned(variant)) {
394 uintmax_t w;
395
396 w = json_variant_unsigned(variant);
397 if (w == RLIM_INFINITY || (uintmax_t) w != json_variant_unsigned(variant))
398 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "Resource limit value '%s' is out of range.", name);
399
400 *ret = (rlim_t) w;
401 } else
402 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit value '%s' is not an unsigned integer.", name);
403
404 return 0;
405}
406
407static int json_dispatch_rlimits(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
408 struct rlimit** limits = userdata;
409 JsonVariant *value;
410 const char *key;
411 int r;
412
413 assert_se(limits);
414
415 if (json_variant_is_null(variant)) {
416 rlimit_free_all(limits);
417 return 0;
418 }
419
420 if (!json_variant_is_object(variant))
421 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
422
423 JSON_VARIANT_OBJECT_FOREACH(key, value, variant) {
424 JsonVariant *jcur, *jmax;
425 struct rlimit rl;
426 const char *p;
427 int l;
428
429 p = startswith(key, "RLIMIT_");
430 if (!p)
7211c853 431 l = -SYNTHETIC_ERRNO(EINVAL);
71d0b9d4
LP
432 else
433 l = rlimit_from_string(p);
434 if (l < 0)
7211c853 435 return json_log(variant, flags, l, "Resource limit '%s' not known.", key);
71d0b9d4
LP
436
437 if (!json_variant_is_object(value))
438 return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' has invalid value.", key);
439
440 if (json_variant_elements(value) != 4)
441 return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' value is does not have two fields as expected.", key);
442
443 jcur = json_variant_by_key(value, "cur");
444 if (!jcur)
445 return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'cur' field.", key);
446 r = json_dispatch_rlimit_value("cur", jcur, flags, &rl.rlim_cur);
447 if (r < 0)
448 return r;
449
450 jmax = json_variant_by_key(value, "max");
451 if (!jmax)
452 return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'max' field.", key);
453 r = json_dispatch_rlimit_value("max", jmax, flags, &rl.rlim_max);
454 if (r < 0)
455 return r;
456
457 if (limits[l])
458 *(limits[l]) = rl;
459 else {
460 limits[l] = newdup(struct rlimit, &rl, 1);
461 if (!limits[l])
462 return log_oom();
463 }
464 }
465
466 return 0;
467}
468
469static int json_dispatch_filename_or_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
470 char **s = userdata;
471 const char *n;
472 int r;
473
474 assert(s);
475
476 if (json_variant_is_null(variant)) {
477 *s = mfree(*s);
478 return 0;
479 }
480
481 if (!json_variant_is_string(variant))
482 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
483
484 n = json_variant_string(variant);
485 if (!filename_is_valid(n) && !path_is_normalized(n))
486 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid file name or normalized path.", strna(name));
487
488 r = free_and_strdup(s, n);
489 if (r < 0)
490 return json_log(variant, flags, r, "Failed to allocate string: %m");
491
492 return 0;
493}
494
495static int json_dispatch_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
496 char **s = userdata;
497 const char *n;
498 int r;
499
500 if (json_variant_is_null(variant)) {
501 *s = mfree(*s);
502 return 0;
503 }
504
505 if (!json_variant_is_string(variant))
506 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
507
508 n = json_variant_string(variant);
509 if (!path_is_normalized(n))
510 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a normalized file system path.", strna(name));
511 if (!path_is_absolute(n))
512 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an absolute file system path.", strna(name));
513
514 r = free_and_strdup(s, n);
515 if (r < 0)
516 return json_log(variant, flags, r, "Failed to allocate string: %m");
517
518 return 0;
519}
520
521static int json_dispatch_home_directory(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
522 char **s = userdata;
523 const char *n;
524 int r;
525
526 if (json_variant_is_null(variant)) {
527 *s = mfree(*s);
528 return 0;
529 }
530
531 if (!json_variant_is_string(variant))
532 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
533
534 n = json_variant_string(variant);
535 if (!valid_home(n))
536 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid home directory path.", strna(name));
537
538 r = free_and_strdup(s, n);
539 if (r < 0)
540 return json_log(variant, flags, r, "Failed to allocate string: %m");
541
542 return 0;
543}
544
545static int json_dispatch_image_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
546 char **s = userdata;
547 const char *n;
548 int r;
549
550 if (json_variant_is_null(variant)) {
551 *s = mfree(*s);
552 return 0;
553 }
554
555 if (!json_variant_is_string(variant))
556 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
557
558 n = json_variant_string(variant);
559 if (empty_or_root(n) || !path_is_valid(n) || !path_is_absolute(n))
560 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid image path.", strna(name));
561
562 r = free_and_strdup(s, n);
563 if (r < 0)
564 return json_log(variant, flags, r, "Failed to allocate string: %m");
565
566 return 0;
567}
568
569static int json_dispatch_umask(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
570 mode_t *m = userdata;
571 uintmax_t k;
572
573 if (json_variant_is_null(variant)) {
f5fbe71d 574 *m = MODE_INVALID;
71d0b9d4
LP
575 return 0;
576 }
577
578 if (!json_variant_is_unsigned(variant))
579 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
580
581 k = json_variant_unsigned(variant);
582 if (k > 0777)
583 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' outside of valid range 0…0777.", strna(name));
584
585 *m = (mode_t) k;
586 return 0;
587}
588
589static int json_dispatch_access_mode(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
590 mode_t *m = userdata;
591 uintmax_t k;
592
593 if (json_variant_is_null(variant)) {
f5fbe71d 594 *m = MODE_INVALID;
71d0b9d4
LP
595 return 0;
596 }
597
598 if (!json_variant_is_unsigned(variant))
599 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
600
601 k = json_variant_unsigned(variant);
602 if (k > 07777)
603 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' outside of valid range 0…07777.", strna(name));
604
605 *m = (mode_t) k;
606 return 0;
607}
608
609static int json_dispatch_environment(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
610 _cleanup_strv_free_ char **n = NULL;
611 char ***l = userdata;
71d0b9d4
LP
612 int r;
613
614 if (json_variant_is_null(variant)) {
615 *l = strv_free(*l);
616 return 0;
617 }
618
619 if (!json_variant_is_array(variant))
620 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
621
6f8f8688 622 for (size_t i = 0; i < json_variant_elements(variant); i++) {
71d0b9d4
LP
623 JsonVariant *e;
624 const char *a;
625
626 e = json_variant_by_index(variant, i);
627 if (!json_variant_is_string(e))
628 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
629
630 assert_se(a = json_variant_string(e));
631
632 if (!env_assignment_is_valid(a))
633 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of environment variables.", strna(name));
634
aaf057c4 635 r = strv_env_replace_strdup(&n, a);
71d0b9d4
LP
636 if (r < 0)
637 return json_log_oom(variant, flags);
71d0b9d4
LP
638 }
639
aaf057c4 640 return strv_free_and_replace(*l, n);
71d0b9d4
LP
641}
642
643int json_dispatch_user_disposition(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
644 UserDisposition *disposition = userdata, k;
645
646 if (json_variant_is_null(variant)) {
647 *disposition = _USER_DISPOSITION_INVALID;
648 return 0;
649 }
650
651 if (!json_variant_is_string(variant))
652 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
653
654 k = user_disposition_from_string(json_variant_string(variant));
655 if (k < 0)
7211c853 656 return json_log(variant, flags, k, "Disposition type '%s' not known.", json_variant_string(variant));
71d0b9d4
LP
657
658 *disposition = k;
659 return 0;
660}
661
662static int json_dispatch_storage(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
663 UserStorage *storage = userdata, k;
664
665 if (json_variant_is_null(variant)) {
666 *storage = _USER_STORAGE_INVALID;
667 return 0;
668 }
669
670 if (!json_variant_is_string(variant))
671 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
672
673 k = user_storage_from_string(json_variant_string(variant));
674 if (k < 0)
7211c853 675 return json_log(variant, flags, k, "Storage type '%s' not known.", json_variant_string(variant));
71d0b9d4
LP
676
677 *storage = k;
678 return 0;
679}
680
681static int json_dispatch_disk_size(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
682 uint64_t *size = userdata;
683 uintmax_t k;
684
685 if (json_variant_is_null(variant)) {
686 *size = UINT64_MAX;
687 return 0;
688 }
689
690 if (!json_variant_is_unsigned(variant))
691 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
692
693 k = json_variant_unsigned(variant);
694 if (k < USER_DISK_SIZE_MIN || k > USER_DISK_SIZE_MAX)
695 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), USER_DISK_SIZE_MIN, USER_DISK_SIZE_MAX);
696
697 *size = k;
698 return 0;
699}
700
701static int json_dispatch_tasks_or_memory_max(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
702 uint64_t *limit = userdata;
703 uintmax_t k;
704
705 if (json_variant_is_null(variant)) {
706 *limit = UINT64_MAX;
707 return 0;
708 }
709
710 if (!json_variant_is_unsigned(variant))
387f6955 711 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
71d0b9d4
LP
712
713 k = json_variant_unsigned(variant);
714 if (k <= 0 || k >= UINT64_MAX)
715 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), (uint64_t) 1, UINT64_MAX-1);
716
717 *limit = k;
718 return 0;
719}
720
721static int json_dispatch_weight(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
722 uint64_t *weight = userdata;
723 uintmax_t k;
724
725 if (json_variant_is_null(variant)) {
726 *weight = UINT64_MAX;
727 return 0;
728 }
729
730 if (!json_variant_is_unsigned(variant))
387f6955 731 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
71d0b9d4
LP
732
733 k = json_variant_unsigned(variant);
734 if (k <= CGROUP_WEIGHT_MIN || k >= CGROUP_WEIGHT_MAX)
735 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not in valid range %" PRIu64 "…%" PRIu64 ".", strna(name), (uint64_t) CGROUP_WEIGHT_MIN, (uint64_t) CGROUP_WEIGHT_MAX);
736
737 *weight = k;
738 return 0;
739}
740
741int json_dispatch_user_group_list(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
742 _cleanup_strv_free_ char **l = NULL;
743 char ***list = userdata;
744 JsonVariant *e;
745 int r;
746
747 if (!json_variant_is_array(variant))
748 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
749
750 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
751
752 if (!json_variant_is_string(e))
753 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
754
7a8867ab 755 if (!valid_user_group_name(json_variant_string(e), FLAGS_SET(flags, JSON_RELAX) ? VALID_USER_RELAX : 0))
71d0b9d4
LP
756 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a valid user/group name: %s", json_variant_string(e));
757
758 r = strv_extend(&l, json_variant_string(e));
759 if (r < 0)
760 return json_log(e, flags, r, "Failed to append array element: %m");
761 }
762
763 r = strv_extend_strv(list, l, true);
764 if (r < 0)
765 return json_log(variant, flags, r, "Failed to merge user/group arrays: %m");
766
767 return 0;
768}
769
770static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
771
772 static const JsonDispatch secret_dispatch_table[] = {
773 { "password", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, password), 0 },
c0bde0d2
LP
774 { "tokenPin", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, token_pin), 0 },
775 { "pkcs11Pin", /* legacy alias */ _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, token_pin), 0 },
71d0b9d4 776 { "pkcs11ProtectedAuthenticationPathPermitted", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
7b78db28 777 { "fido2UserPresencePermitted", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, fido2_user_presence_permitted), 0 },
17e7561a 778 { "fido2UserVerificationPermitted", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, fido2_user_verification_permitted), 0 },
71d0b9d4
LP
779 {},
780 };
781
782 return json_dispatch(variant, secret_dispatch_table, NULL, flags, userdata);
783}
784
785static int dispatch_pkcs11_uri(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
786 char **s = userdata;
787 const char *n;
788 int r;
789
790 if (json_variant_is_null(variant)) {
791 *s = mfree(*s);
792 return 0;
793 }
794
795 if (!json_variant_is_string(variant))
796 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
797
798 n = json_variant_string(variant);
799 if (!pkcs11_uri_valid(n))
800 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
801
802 r = free_and_strdup(s, n);
803 if (r < 0)
804 return json_log(variant, flags, r, "Failed to allocate string: %m");
805
806 return 0;
807}
808
809static int dispatch_pkcs11_uri_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
810 _cleanup_strv_free_ char **z = NULL;
811 char ***l = userdata;
812 JsonVariant *e;
813 int r;
814
815 if (json_variant_is_null(variant)) {
816 *l = strv_free(*l);
817 return 0;
818 }
819
820 if (json_variant_is_string(variant)) {
821 const char *n;
822
823 n = json_variant_string(variant);
824 if (!pkcs11_uri_valid(n))
825 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
826
827 z = strv_new(n);
828 if (!z)
829 return log_oom();
830
831 } else {
832
833 if (!json_variant_is_array(variant))
834 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string or array of strings.", strna(name));
835
836 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
837 const char *n;
838
839 if (!json_variant_is_string(e))
840 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
841
842 n = json_variant_string(e);
843 if (!pkcs11_uri_valid(n))
844 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element in '%s' is not a valid RFC7512 PKCS#11 URI: %s", strna(name), n);
845
846 r = strv_extend(&z, n);
847 if (r < 0)
848 return log_oom();
849 }
850 }
851
852 strv_free_and_replace(*l, z);
853 return 0;
854}
855
856static int dispatch_pkcs11_key_data(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
857 Pkcs11EncryptedKey *k = userdata;
858 size_t l;
859 void *b;
860 int r;
861
862 if (json_variant_is_null(variant)) {
d00f3183 863 k->data = erase_and_free(k->data);
71d0b9d4
LP
864 k->size = 0;
865 return 0;
866 }
867
868 if (!json_variant_is_string(variant))
869 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
870
f5fbe71d 871 r = unbase64mem(json_variant_string(variant), SIZE_MAX, &b, &l);
71d0b9d4
LP
872 if (r < 0)
873 return json_log(variant, flags, r, "Failed to decode encrypted PKCS#11 key: %m");
874
875 erase_and_free(k->data);
876 k->data = b;
877 k->size = l;
878
879 return 0;
880}
881
882static int dispatch_pkcs11_key(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
883 UserRecord *h = userdata;
884 JsonVariant *e;
885 int r;
886
887 if (!json_variant_is_array(variant))
888 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
889
890 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
891 Pkcs11EncryptedKey *array, *k;
892
893 static const JsonDispatch pkcs11_key_dispatch_table[] = {
894 { "uri", JSON_VARIANT_STRING, dispatch_pkcs11_uri, offsetof(Pkcs11EncryptedKey, uri), JSON_MANDATORY },
895 { "data", JSON_VARIANT_STRING, dispatch_pkcs11_key_data, 0, JSON_MANDATORY },
896 { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string, offsetof(Pkcs11EncryptedKey, hashed_password), JSON_MANDATORY },
897 {},
898 };
899
900 if (!json_variant_is_object(e))
901 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
902
903 array = reallocarray(h->pkcs11_encrypted_key, h->n_pkcs11_encrypted_key + 1, sizeof(Pkcs11EncryptedKey));
904 if (!array)
905 return log_oom();
906
907 h->pkcs11_encrypted_key = array;
908 k = h->pkcs11_encrypted_key + h->n_pkcs11_encrypted_key;
909 *k = (Pkcs11EncryptedKey) {};
910
911 r = json_dispatch(e, pkcs11_key_dispatch_table, NULL, flags, k);
912 if (r < 0) {
913 pkcs11_encrypted_key_done(k);
914 return r;
915 }
916
917 h->n_pkcs11_encrypted_key++;
918 }
919
920 return 0;
921}
922
5e4fa456
LP
923static int dispatch_fido2_hmac_credential(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
924 Fido2HmacCredential *k = userdata;
925 size_t l;
926 void *b;
927 int r;
928
929 if (json_variant_is_null(variant)) {
930 k->id = mfree(k->id);
931 k->size = 0;
932 return 0;
933 }
934
935 if (!json_variant_is_string(variant))
936 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
937
f5fbe71d 938 r = unbase64mem(json_variant_string(variant), SIZE_MAX, &b, &l);
5e4fa456
LP
939 if (r < 0)
940 return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
941
942 free_and_replace(k->id, b);
943 k->size = l;
944
945 return 0;
946}
947
948static int dispatch_fido2_hmac_credential_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
949 UserRecord *h = userdata;
950 JsonVariant *e;
951 int r;
952
953 if (!json_variant_is_array(variant))
954 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
955
956 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
957 Fido2HmacCredential *array;
958 size_t l;
959 void *b;
960
961 if (!json_variant_is_string(e))
962 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
963
964 array = reallocarray(h->fido2_hmac_credential, h->n_fido2_hmac_credential + 1, sizeof(Fido2HmacCredential));
965 if (!array)
966 return log_oom();
967
f5fbe71d 968 r = unbase64mem(json_variant_string(e), SIZE_MAX, &b, &l);
5e4fa456
LP
969 if (r < 0)
970 return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
971
972 h->fido2_hmac_credential = array;
973
974 h->fido2_hmac_credential[h->n_fido2_hmac_credential++] = (Fido2HmacCredential) {
975 .id = b,
976 .size = l,
977 };
978 }
979
980 return 0;
981}
982
983static int dispatch_fido2_hmac_salt_value(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
984 Fido2HmacSalt *k = userdata;
985 size_t l;
986 void *b;
987 int r;
988
989 if (json_variant_is_null(variant)) {
990 k->salt = erase_and_free(k->salt);
991 k->salt_size = 0;
992 return 0;
993 }
994
995 if (!json_variant_is_string(variant))
996 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
997
f5fbe71d 998 r = unbase64mem(json_variant_string(variant), SIZE_MAX, &b, &l);
5e4fa456
LP
999 if (r < 0)
1000 return json_log(variant, flags, r, "Failed to decode FIDO2 salt: %m");
1001
1002 erase_and_free(k->salt);
1003 k->salt = b;
1004 k->salt_size = l;
1005
1006 return 0;
1007}
1008
1009static int dispatch_fido2_hmac_salt(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
1010 UserRecord *h = userdata;
1011 JsonVariant *e;
1012 int r;
1013
1014 if (!json_variant_is_array(variant))
1015 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
1016
1017 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
1018 Fido2HmacSalt *array, *k;
1019
1020 static const JsonDispatch fido2_hmac_salt_dispatch_table[] = {
17e7561a
LP
1021 { "credential", JSON_VARIANT_STRING, dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential), JSON_MANDATORY },
1022 { "salt", JSON_VARIANT_STRING, dispatch_fido2_hmac_salt_value, 0, JSON_MANDATORY },
1023 { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string, offsetof(Fido2HmacSalt, hashed_password), JSON_MANDATORY },
1024 { "up", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(Fido2HmacSalt, up), 0 },
1025 { "uv", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(Fido2HmacSalt, uv), 0 },
1026 { "clientPin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(Fido2HmacSalt, client_pin), 0 },
5e4fa456
LP
1027 {},
1028 };
1029
1030 if (!json_variant_is_object(e))
1031 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
1032
1033 array = reallocarray(h->fido2_hmac_salt, h->n_fido2_hmac_salt + 1, sizeof(Fido2HmacSalt));
1034 if (!array)
1035 return log_oom();
1036
1037 h->fido2_hmac_salt = array;
1038 k = h->fido2_hmac_salt + h->n_fido2_hmac_salt;
17e7561a
LP
1039 *k = (Fido2HmacSalt) {
1040 .uv = -1,
1041 .up = -1,
1042 .client_pin = -1,
1043 };
5e4fa456
LP
1044
1045 r = json_dispatch(e, fido2_hmac_salt_dispatch_table, NULL, flags, k);
1046 if (r < 0) {
1047 fido2_hmac_salt_done(k);
1048 return r;
1049 }
1050
1051 h->n_fido2_hmac_salt++;
1052 }
1053
1054 return 0;
1055}
1056
b3a97fd3
LP
1057static int dispatch_recovery_key(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
1058 UserRecord *h = userdata;
1059 JsonVariant *e;
1060 int r;
1061
1062 if (!json_variant_is_array(variant))
1063 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
1064
1065 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
1066 RecoveryKey *array, *k;
1067
1068 static const JsonDispatch recovery_key_dispatch_table[] = {
1069 { "type", JSON_VARIANT_STRING, json_dispatch_string, 0, JSON_MANDATORY },
1070 { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string, offsetof(RecoveryKey, hashed_password), JSON_MANDATORY },
1071 {},
1072 };
1073
1074 if (!json_variant_is_object(e))
1075 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
1076
1077 array = reallocarray(h->recovery_key, h->n_recovery_key + 1, sizeof(RecoveryKey));
1078 if (!array)
1079 return log_oom();
1080
1081 h->recovery_key = array;
1082 k = h->recovery_key + h->n_recovery_key;
1083 *k = (RecoveryKey) {};
1084
1085 r = json_dispatch(e, recovery_key_dispatch_table, NULL, flags, k);
1086 if (r < 0) {
1087 recovery_key_done(k);
1088 return r;
1089 }
1090
1091 h->n_recovery_key++;
1092 }
1093
1094 return 0;
1095}
1096
71d0b9d4
LP
1097static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
1098
1099 static const JsonDispatch privileged_dispatch_table[] = {
5e4fa456
LP
1100 { "passwordHint", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, password_hint), 0 },
1101 { "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, hashed_password), JSON_SAFE },
1102 { "sshAuthorizedKeys", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(UserRecord, ssh_authorized_keys), 0 },
1103 { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY, dispatch_pkcs11_key, 0, 0 },
1104 { "fido2HmacSalt", JSON_VARIANT_ARRAY, dispatch_fido2_hmac_salt, 0, 0 },
b3a97fd3 1105 { "recoveryKey", JSON_VARIANT_ARRAY, dispatch_recovery_key, 0, 0 },
71d0b9d4
LP
1106 {},
1107 };
1108
1109 return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata);
1110}
1111
1112static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
1113
1114 static const JsonDispatch binding_dispatch_table[] = {
1115 { "imagePath", JSON_VARIANT_STRING, json_dispatch_image_path, offsetof(UserRecord, image_path), 0 },
1116 { "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 },
1117 { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
1118 { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
1119 { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
1120 { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
1121 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
1122 { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 },
1123 { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
1124 { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
1125 { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
1126 { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
1127 {},
1128 };
1129
71d0b9d4
LP
1130 JsonVariant *m;
1131 sd_id128_t mid;
1132 int r;
1133
1134 if (!variant)
1135 return 0;
1136
1137 if (!json_variant_is_object(variant))
1138 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
1139
1140 r = sd_id128_get_machine(&mid);
1141 if (r < 0)
1142 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
1143
85b55869 1144 m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
71d0b9d4
LP
1145 if (!m)
1146 return 0;
1147
1148 return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata);
1149}
1150
1151int per_machine_id_match(JsonVariant *ids, JsonDispatchFlags flags) {
1152 sd_id128_t mid;
1153 int r;
1154
1155 r = sd_id128_get_machine(&mid);
1156 if (r < 0)
1157 return json_log(ids, flags, r, "Failed to acquire machine ID: %m");
1158
1159 if (json_variant_is_string(ids)) {
1160 sd_id128_t k;
1161
1162 r = sd_id128_from_string(json_variant_string(ids), &k);
1163 if (r < 0) {
1164 json_log(ids, flags, r, "%s is not a valid machine ID, ignoring: %m", json_variant_string(ids));
1165 return 0;
1166 }
1167
1168 return sd_id128_equal(mid, k);
1169 }
1170
1171 if (json_variant_is_array(ids)) {
1172 JsonVariant *e;
1173
1174 JSON_VARIANT_ARRAY_FOREACH(e, ids) {
1175 sd_id128_t k;
1176
1177 if (!json_variant_is_string(e)) {
1178 json_log(e, flags, 0, "Machine ID is not a string, ignoring: %m");
1179 continue;
1180 }
1181
1182 r = sd_id128_from_string(json_variant_string(e), &k);
1183 if (r < 0) {
1184 json_log(e, flags, r, "%s is not a valid machine ID, ignoring: %m", json_variant_string(e));
1185 continue;
1186 }
1187
1188 if (sd_id128_equal(mid, k))
1189 return true;
1190 }
1191
1192 return false;
1193 }
1194
1195 json_log(ids, flags, 0, "Machine ID is not a string or array of strings, ignoring: %m");
1196 return false;
1197}
1198
1199int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags) {
1200 _cleanup_free_ char *hn = NULL;
1201 int r;
1202
1203 r = gethostname_strict(&hn);
1204 if (r == -ENXIO) {
1205 json_log(hns, flags, r, "No hostname set, not matching perMachine hostname record: %m");
1206 return false;
1207 }
1208 if (r < 0)
1209 return json_log(hns, flags, r, "Failed to acquire hostname: %m");
1210
1211 if (json_variant_is_string(hns))
1212 return streq(json_variant_string(hns), hn);
1213
1214 if (json_variant_is_array(hns)) {
1215 JsonVariant *e;
1216
1217 JSON_VARIANT_ARRAY_FOREACH(e, hns) {
1218
1219 if (!json_variant_is_string(e)) {
1220 json_log(e, flags, 0, "Hostname is not a string, ignoring: %m");
1221 continue;
1222 }
1223
1224 if (streq(json_variant_string(hns), hn))
1225 return true;
1226 }
1227
1228 return false;
1229 }
1230
1231 json_log(hns, flags, 0, "Hostname is not a string or array of strings, ignoring: %m");
1232 return false;
1233}
1234
1235static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
1236
1237 static const JsonDispatch per_machine_dispatch_table[] = {
5e4fa456
LP
1238 { "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
1239 { "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
1240 { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE },
1241 { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 },
1242 { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
1243 { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
1244 { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 },
1245 { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE },
1246 { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE },
1247 { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 },
1248 { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 },
1249 { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 },
1250 { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 },
1251 { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 },
1252 { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 },
1253 { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 },
1254 { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
1255 { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 },
1256 { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
1257 { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
1258 { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
1259 { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },
1260 { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 },
1261 { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 },
1262 { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 },
1263 { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 },
1264 { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 },
1265 { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE },
1266 { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE },
1267 { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE },
1268 { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 },
1269 { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
1270 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
1271 { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX},
1272 { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
1273 { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
1274 { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
1275 { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
1276 { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0, },
1277 { "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0, },
1278 { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
1279 { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
1280 { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
1281 { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE },
1282 { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE },
1283 { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
1284 { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
1285 { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
1286 { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
1287 { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
1288 { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 },
1289 { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 },
1290 { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 },
1291 { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 },
1292 { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 },
1293 { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 },
1294 { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 },
1295 { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 },
1296 { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 },
1297 { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
1298 { "fido2HmacCredential", JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 },
71d0b9d4
LP
1299 {},
1300 };
1301
1302 JsonVariant *e;
1303 int r;
1304
1305 if (!variant)
1306 return 0;
1307
1308 if (!json_variant_is_array(variant))
1309 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
1310
1311 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
1312 bool matching = false;
1313 JsonVariant *m;
1314
1315 if (!json_variant_is_object(e))
1316 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
1317
1318 m = json_variant_by_key(e, "matchMachineId");
1319 if (m) {
1320 r = per_machine_id_match(m, flags);
1321 if (r < 0)
1322 return r;
1323
1324 matching = r > 0;
1325 }
1326
1327 if (!matching) {
1328 m = json_variant_by_key(e, "matchHostname");
1329 if (m) {
1330 r = per_machine_hostname_match(m, flags);
1331 if (r < 0)
1332 return r;
1333
1334 matching = r > 0;
1335 }
1336 }
1337
1338 if (!matching)
1339 continue;
1340
1341 r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata);
1342 if (r < 0)
1343 return r;
1344 }
1345
1346 return 0;
1347}
1348
1349static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
1350
1351 static const JsonDispatch status_dispatch_table[] = {
1352 { "diskUsage", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_usage), 0 },
1353 { "diskFree", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_free), 0 },
1354 { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
1355 { "diskCeiling", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_ceiling), 0 },
1356 { "diskFloor", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_floor), 0 },
1357 { "state", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, state), JSON_SAFE },
1358 { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
1359 { "signedLocally", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, signed_locally), 0 },
1360 { "goodAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, good_authentication_counter), 0 },
1361 { "badAuthenticationCounter", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, bad_authentication_counter), 0 },
1362 { "lastGoodAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_good_authentication_usec), 0 },
1363 { "lastBadAuthenticationUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_bad_authentication_usec), 0 },
1364 { "rateLimitBeginUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_begin_usec), 0 },
1365 { "rateLimitCount", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_count), 0 },
1366 { "removable", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, removable), 0 },
1367 {},
1368 };
1369
71d0b9d4
LP
1370 JsonVariant *m;
1371 sd_id128_t mid;
1372 int r;
1373
1374 if (!variant)
1375 return 0;
1376
1377 if (!json_variant_is_object(variant))
1378 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
1379
1380 r = sd_id128_get_machine(&mid);
1381 if (r < 0)
1382 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
1383
85b55869 1384 m = json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
71d0b9d4
LP
1385 if (!m)
1386 return 0;
1387
1388 return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
1389}
1390
a43eddbd
LP
1391int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret) {
1392 const char *suffix;
1393 char *z;
1394
1395 assert(storage >= 0);
1396 assert(user_name_and_realm);
1397 assert(ret);
1398
1399 if (storage == USER_LUKS)
1400 suffix = ".home";
1401 else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
1402 suffix = ".homedir";
1403 else {
1404 *ret = NULL;
1405 return 0;
1406 }
1407
1408 z = strjoin("/home/", user_name_and_realm, suffix);
1409 if (!z)
1410 return -ENOMEM;
1411
1412 *ret = z;
1413 return 1;
1414}
1415
71d0b9d4 1416static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
a43eddbd
LP
1417 int r;
1418
71d0b9d4
LP
1419 assert(h);
1420
1421 if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
1422 return 0;
1423
1424 assert(h->user_name);
1425
1426 if (!h->user_name_and_realm_auto && h->realm) {
1427 h->user_name_and_realm_auto = strjoin(h->user_name, "@", h->realm);
1428 if (!h->user_name_and_realm_auto)
1429 return json_log_oom(h->json, json_flags);
1430 }
1431
162392b7 1432 /* Let's add in the following automatisms only for regular users, they don't make sense for any others */
71d0b9d4
LP
1433 if (user_record_disposition(h) != USER_REGULAR)
1434 return 0;
1435
1436 if (!h->home_directory && !h->home_directory_auto) {
1437 h->home_directory_auto = path_join("/home/", h->user_name);
1438 if (!h->home_directory_auto)
1439 return json_log_oom(h->json, json_flags);
1440 }
1441
1442 if (!h->image_path && !h->image_path_auto) {
a43eddbd
LP
1443 r = user_record_build_image_path(user_record_storage(h), user_record_user_name_and_realm(h), &h->image_path_auto);
1444 if (r < 0)
1445 return json_log(h->json, json_flags, r, "Failed to determine default image path: %m");
71d0b9d4
LP
1446 }
1447
1448 return 0;
1449}
1450
1451int user_group_record_mangle(
1452 JsonVariant *v,
1453 UserRecordLoadFlags load_flags,
1454 JsonVariant **ret_variant,
1455 UserRecordMask *ret_mask) {
1456
1457 static const struct {
1458 UserRecordMask mask;
1459 const char *name;
1460 } mask_field[] = {
1461 { USER_RECORD_PRIVILEGED, "privileged" },
1462 { USER_RECORD_SECRET, "secret" },
1463 { USER_RECORD_BINDING, "binding" },
1464 { USER_RECORD_PER_MACHINE, "perMachine" },
1465 { USER_RECORD_STATUS, "status" },
1466 { USER_RECORD_SIGNATURE, "signature" },
1467 };
1468
1469 JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
1470 _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
1471 JsonVariant *array[ELEMENTSOF(mask_field) * 2];
6f8f8688 1472 size_t n_retain = 0;
71d0b9d4
LP
1473 UserRecordMask m = 0;
1474 int r;
1475
1476 assert((load_flags & _USER_RECORD_MASK_MAX) == 0); /* detect mistakes when accidentally passing
1477 * UserRecordMask bit masks as UserRecordLoadFlags
1478 * value */
1479
1480 assert(v);
1481 assert(ret_variant);
1482 assert(ret_mask);
1483
1484 /* Note that this function is shared with the group record parser, hence we try to be generic in our
1485 * log message wording here, to cover both cases. */
1486
1487 if (!json_variant_is_object(v))
1488 return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is not a JSON object, refusing.");
1489
1490 if (USER_RECORD_ALLOW_MASK(load_flags) == 0) /* allow nothing? */
1491 return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Nothing allowed in record, refusing.");
1492
1493 if (USER_RECORD_STRIP_MASK(load_flags) == _USER_RECORD_MASK_MAX) /* strip everything? */
1494 return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Stripping everything from record, refusing.");
1495
1496 /* Check if we have the special sections and if they match our flags set */
6f8f8688 1497 for (size_t i = 0; i < ELEMENTSOF(mask_field); i++) {
71d0b9d4
LP
1498 JsonVariant *e, *k;
1499
1500 if (FLAGS_SET(USER_RECORD_STRIP_MASK(load_flags), mask_field[i].mask)) {
1501 if (!w)
1502 w = json_variant_ref(v);
1503
1504 r = json_variant_filter(&w, STRV_MAKE(mask_field[i].name));
1505 if (r < 0)
1506 return json_log(w, json_flags, r, "Failed to remove field from variant: %m");
1507
1508 continue;
1509 }
1510
1511 e = json_variant_by_key_full(v, mask_field[i].name, &k);
1512 if (e) {
1513 if (!FLAGS_SET(USER_RECORD_ALLOW_MASK(load_flags), mask_field[i].mask))
1514 return json_log(e, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", mask_field[i].name);
1515
1516 if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
1517 array[n_retain++] = k;
1518 array[n_retain++] = e;
1519 }
1520
1521 m |= mask_field[i].mask;
1522 } else {
1523 if (FLAGS_SET(USER_RECORD_REQUIRE_MASK(load_flags), mask_field[i].mask))
1524 return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks '%s' field, which is required.", mask_field[i].name);
1525 }
1526 }
1527
1528 if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
1529 /* If we are supposed to strip regular items, then let's instead just allocate a new object
1530 * with just the stuff we need. */
1531
1532 w = json_variant_unref(w);
1533 r = json_variant_new_object(&w, array, n_retain);
1534 if (r < 0)
1535 return json_log(v, json_flags, r, "Failed to allocate new object: %m");
6f8f8688 1536 } else
71d0b9d4 1537 /* And now check if there's anything else in the record */
6f8f8688 1538 for (size_t i = 0; i < json_variant_elements(v); i += 2) {
71d0b9d4
LP
1539 const char *f;
1540 bool special = false;
71d0b9d4
LP
1541
1542 assert_se(f = json_variant_string(json_variant_by_index(v, i)));
1543
6f8f8688 1544 for (size_t j = 0; j < ELEMENTSOF(mask_field); j++)
71d0b9d4
LP
1545 if (streq(f, mask_field[j].name)) { /* already covered in the loop above */
1546 special = true;
1547 continue;
1548 }
1549
1550 if (!special) {
1551 if ((load_flags & (USER_RECORD_ALLOW_REGULAR|USER_RECORD_REQUIRE_REGULAR)) == 0)
1552 return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", f);
1553
1554 m |= USER_RECORD_REGULAR;
1555 break;
1556 }
1557 }
71d0b9d4
LP
1558
1559 if (FLAGS_SET(load_flags, USER_RECORD_REQUIRE_REGULAR) && !FLAGS_SET(m, USER_RECORD_REGULAR))
1560 return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks basic identity fields, which are required.");
1561
1a298a20 1562 if (!FLAGS_SET(load_flags, USER_RECORD_EMPTY_OK) && m == 0)
71d0b9d4
LP
1563 return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is empty.");
1564
1565 if (w)
1566 *ret_variant = TAKE_PTR(w);
1567 else
1568 *ret_variant = json_variant_ref(v);
1569
1570 *ret_mask = m;
1571 return 0;
1572}
1573
1574int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_flags) {
1575
1576 static const JsonDispatch user_dispatch_table[] = {
5e4fa456
LP
1577 { "userName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(UserRecord, user_name), JSON_RELAX},
1578 { "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(UserRecord, realm), 0 },
1579 { "realName", JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(UserRecord, real_name), 0 },
1580 { "emailAddress", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, email_address), JSON_SAFE },
1581 { "iconName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, icon_name), JSON_SAFE },
1582 { "location", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, location), 0 },
1583 { "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(UserRecord, disposition), 0 },
1584 { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_change_usec), 0 },
1585 { "lastPasswordChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 },
1586 { "shell", JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
1587 { "umask", JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
1588 { "environment", JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 },
1589 { "timeZone", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, time_zone), JSON_SAFE },
1590 { "preferredLanguage", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, preferred_language), JSON_SAFE },
1591 { "niceLevel", _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice, offsetof(UserRecord, nice_level), 0 },
1592 { "resourceLimits", _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits, offsetof(UserRecord, rlimits), 0 },
1593 { "locked", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, locked), 0 },
1594 { "notBeforeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_before_usec), 0 },
1595 { "notAfterUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, not_after_usec), 0 },
1596 { "storage", JSON_VARIANT_STRING, json_dispatch_storage, offsetof(UserRecord, storage), 0 },
1597 { "diskSize", JSON_VARIANT_UNSIGNED, json_dispatch_disk_size, offsetof(UserRecord, disk_size), 0 },
1598 { "diskSizeRelative", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
1599 { "skeletonDirectory", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), 0 },
1600 { "accessMode", JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
1601 { "tasksMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
1602 { "memoryHigh", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
1603 { "memoryMax", JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },
1604 { "cpuWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, cpu_weight), 0 },
1605 { "ioWeight", JSON_VARIANT_UNSIGNED, json_dispatch_weight, offsetof(UserRecord, io_weight), 0 },
1606 { "mountNoDevices", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nodev), 0 },
1607 { "mountNoSuid", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, nosuid), 0 },
1608 { "mountNoExecute", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(UserRecord, noexec), 0 },
1609 { "cifsDomain", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_domain), JSON_SAFE },
1610 { "cifsUserName", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_user_name), JSON_SAFE },
1611 { "cifsService", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, cifs_service), JSON_SAFE },
1612 { "imagePath", JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, image_path), 0 },
1613 { "homeDirectory", JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, home_directory), 0 },
1614 { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, uid), 0 },
1615 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(UserRecord, gid), 0 },
1616 { "memberOf", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(UserRecord, member_of), JSON_RELAX},
1617 { "fileSystemType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, file_system_type), JSON_SAFE },
1618 { "partitionUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, partition_uuid), 0 },
1619 { "luksUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, luks_uuid), 0 },
1620 { "fileSystemUuid", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(UserRecord, file_system_uuid), 0 },
1621 { "luksDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_discard), 0 },
5e86c82a 1622 { "luksOfflineDiscard", _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate, offsetof(UserRecord, luks_offline_discard), 0 },
5e4fa456
LP
1623 { "luksCipher", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher), JSON_SAFE },
1624 { "luksCipherMode", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_cipher_mode), JSON_SAFE },
1625 { "luksVolumeKeySize", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_volume_key_size), 0 },
1626 { "luksPbkdfHashAlgorithm", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_hash_algorithm), JSON_SAFE },
1627 { "luksPbkdfType", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, luks_pbkdf_type), JSON_SAFE },
1628 { "luksPbkdfTimeCostUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_time_cost_usec), 0 },
1629 { "luksPbkdfMemoryCost", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_memory_cost), 0 },
1630 { "luksPbkdfParallelThreads", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, luks_pbkdf_parallel_threads), 0 },
1631 { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(UserRecord, service), JSON_SAFE },
1632 { "rateLimitIntervalUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_interval_usec), 0 },
1633 { "rateLimitBurst", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, ratelimit_burst), 0 },
1634 { "enforcePasswordPolicy", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, enforce_password_policy), 0 },
1635 { "autoLogin", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, auto_login), 0 },
1636 { "stopDelayUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, stop_delay_usec), 0 },
1637 { "killProcesses", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, kill_processes), 0 },
1638 { "passwordChangeMinUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_min_usec), 0 },
1639 { "passwordChangeMaxUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_max_usec), 0 },
1640 { "passwordChangeWarnUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_warn_usec), 0 },
1641 { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(UserRecord, password_change_inactive_usec), 0 },
1642 { "passwordChangeNow", JSON_VARIANT_BOOLEAN, json_dispatch_tristate, offsetof(UserRecord, password_change_now), 0 },
1643 { "pkcs11TokenUri", JSON_VARIANT_ARRAY, dispatch_pkcs11_uri_array, offsetof(UserRecord, pkcs11_token_uri), 0 },
1644 { "fido2HmacCredential", JSON_VARIANT_ARRAY, dispatch_fido2_hmac_credential_array, 0, 0 },
b3a97fd3 1645 { "recoveryKeyType", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(UserRecord, recovery_key_type), 0 },
5e4fa456
LP
1646
1647 { "secret", JSON_VARIANT_OBJECT, dispatch_secret, 0, 0 },
1648 { "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
71d0b9d4
LP
1649
1650 /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */
5e4fa456
LP
1651 { "perMachine", JSON_VARIANT_ARRAY, NULL, 0, 0 },
1652 { "binding", JSON_VARIANT_OBJECT, NULL, 0, 0 },
1653 { "status", JSON_VARIANT_OBJECT, NULL, 0, 0 },
71d0b9d4
LP
1654
1655 /* Ignore 'signature', we check it with explicit accessors instead */
5e4fa456 1656 { "signature", JSON_VARIANT_ARRAY, NULL, 0, 0 },
71d0b9d4
LP
1657 {},
1658 };
1659
1660 JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
1661 int r;
1662
1663 assert(h);
1664 assert(!h->json);
1665
1666 /* Note that this call will leave a half-initialized record around on failure! */
1667
1668 r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
1669 if (r < 0)
1670 return r;
1671
1672 r = json_dispatch(h->json, user_dispatch_table, NULL, json_flags, h);
1673 if (r < 0)
1674 return r;
1675
1676 /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields,
1677 * since we want them to override the global options. Let's process them now. */
1678
1679 r = dispatch_per_machine("perMachine", json_variant_by_key(h->json, "perMachine"), json_flags, h);
1680 if (r < 0)
1681 return r;
1682
1683 r = dispatch_binding("binding", json_variant_by_key(h->json, "binding"), json_flags, h);
1684 if (r < 0)
1685 return r;
1686
1687 r = dispatch_status("status", json_variant_by_key(h->json, "status"), json_flags, h);
1688 if (r < 0)
1689 return r;
1690
1691 if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->user_name)
1692 return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "User name field missing, refusing.");
1693
1694 r = user_record_augment(h, json_flags);
1695 if (r < 0)
1696 return r;
1697
1698 return 0;
1699}
1700
1701int user_record_build(UserRecord **ret, ...) {
1702 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
1703 _cleanup_(user_record_unrefp) UserRecord *u = NULL;
1704 va_list ap;
1705 int r;
1706
1707 assert(ret);
1708
1709 va_start(ap, ret);
1710 r = json_buildv(&v, ap);
1711 va_end(ap);
1712
1713 if (r < 0)
1714 return r;
1715
1716 u = user_record_new();
1717 if (!u)
1718 return -ENOMEM;
1719
1720 r = user_record_load(u, v, USER_RECORD_LOAD_FULL);
1721 if (r < 0)
1722 return r;
1723
1724 *ret = TAKE_PTR(u);
1725 return 0;
1726}
1727
1728const char *user_record_user_name_and_realm(UserRecord *h) {
1729 assert(h);
1730
1731 /* Return the pre-initialized joined string if it is defined */
1732 if (h->user_name_and_realm_auto)
1733 return h->user_name_and_realm_auto;
1734
1735 /* If it's not defined then we cannot have a realm */
1736 assert(!h->realm);
1737 return h->user_name;
1738}
1739
1740UserStorage user_record_storage(UserRecord *h) {
1741 assert(h);
1742
1743 if (h->storage >= 0)
1744 return h->storage;
1745
1746 return USER_CLASSIC;
1747}
1748
1749const char *user_record_file_system_type(UserRecord *h) {
1750 assert(h);
1751
caf6bd16 1752 return h->file_system_type ?: "btrfs";
71d0b9d4
LP
1753}
1754
1755const char *user_record_skeleton_directory(UserRecord *h) {
1756 assert(h);
1757
1758 return h->skeleton_directory ?: "/etc/skel";
1759}
1760
1761mode_t user_record_access_mode(UserRecord *h) {
1762 assert(h);
1763
f5fbe71d 1764 return h->access_mode != MODE_INVALID ? h->access_mode : 0700;
71d0b9d4
LP
1765}
1766
1767const char* user_record_home_directory(UserRecord *h) {
1768 assert(h);
1769
1770 if (h->home_directory)
1771 return h->home_directory;
1772 if (h->home_directory_auto)
1773 return h->home_directory_auto;
1774
1775 /* The root user is special, hence be special about it */
1776 if (streq_ptr(h->user_name, "root"))
1777 return "/root";
1778
1779 return "/";
1780}
1781
1782const char *user_record_image_path(UserRecord *h) {
1783 assert(h);
1784
1785 if (h->image_path)
1786 return h->image_path;
1787 if (h->image_path_auto)
1788 return h->image_path_auto;
1789
1790 return IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT) ? user_record_home_directory(h) : NULL;
1791}
1792
1793const char *user_record_cifs_user_name(UserRecord *h) {
1794 assert(h);
1795
1796 return h->cifs_user_name ?: h->user_name;
1797}
1798
1799unsigned long user_record_mount_flags(UserRecord *h) {
1800 assert(h);
1801
1802 return (h->nosuid ? MS_NOSUID : 0) |
1803 (h->noexec ? MS_NOEXEC : 0) |
1804 (h->nodev ? MS_NODEV : 0);
1805}
1806
1807const char *user_record_shell(UserRecord *h) {
1808 assert(h);
1809
1810 if (h->shell)
1811 return h->shell;
1812
1813 if (streq_ptr(h->user_name, "root"))
1814 return "/bin/sh";
1815
1816 if (user_record_disposition(h) == USER_REGULAR)
1817 return "/bin/bash";
1818
1819 return NOLOGIN;
1820}
1821
1822const char *user_record_real_name(UserRecord *h) {
1823 assert(h);
1824
1825 return h->real_name ?: h->user_name;
1826}
1827
1828bool user_record_luks_discard(UserRecord *h) {
1829 const char *ip;
1830
1831 assert(h);
1832
1833 if (h->luks_discard >= 0)
1834 return h->luks_discard;
1835
1836 ip = user_record_image_path(h);
1837 if (!ip)
1838 return false;
1839
1840 /* Use discard by default if we are referring to a real block device, but not when operating on a
1841 * loopback device. We want to optimize for SSD and flash storage after all, but we should be careful
1842 * when storing stuff on top of regular file systems in loopback files as doing discard then would
1843 * mean thin provisioning and we should not do that willy-nilly since it means we'll risk EIO later
1844 * on should the disk space to back our file systems not be available. */
1845
1846 return path_startswith(ip, "/dev/");
1847}
1848
5e86c82a
LP
1849bool user_record_luks_offline_discard(UserRecord *h) {
1850 const char *ip;
1851
1852 assert(h);
1853
1854 if (h->luks_offline_discard >= 0)
1855 return h->luks_offline_discard;
1856
1857 /* Discard while we are logged out should generally be a good idea, except when operating directly on
1858 * physical media, where we should just bind it to the online discard mode. */
1859
1860 ip = user_record_image_path(h);
1861 if (!ip)
1862 return false;
1863
1864 if (path_startswith(ip, "/dev/"))
1865 return user_record_luks_discard(h);
1866
1867 return true;
1868}
1869
71d0b9d4
LP
1870const char *user_record_luks_cipher(UserRecord *h) {
1871 assert(h);
1872
1873 return h->luks_cipher ?: "aes";
1874}
1875
1876const char *user_record_luks_cipher_mode(UserRecord *h) {
1877 assert(h);
1878
1879 return h->luks_cipher_mode ?: "xts-plain64";
1880}
1881
1882uint64_t user_record_luks_volume_key_size(UserRecord *h) {
1883 assert(h);
1884
1885 /* We return a value here that can be cast without loss into size_t which is what libcrypsetup expects */
1886
1887 if (h->luks_volume_key_size == UINT64_MAX)
1888 return 256 / 8;
1889
1890 return MIN(h->luks_volume_key_size, SIZE_MAX);
1891}
1892
1893const char* user_record_luks_pbkdf_type(UserRecord *h) {
1894 assert(h);
1895
1896 return h->luks_pbkdf_type ?: "argon2i";
1897}
1898
1899uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
1900 assert(h);
1901
1902 /* Returns a value with ms granularity, since that's what libcryptsetup expects */
1903
1904 if (h->luks_pbkdf_time_cost_usec == UINT64_MAX)
1905 return 500 * USEC_PER_MSEC; /* We default to 500ms, in contrast to libcryptsetup's 2s, which is just awfully slow on every login */
1906
1907 return MIN(DIV_ROUND_UP(h->luks_pbkdf_time_cost_usec, USEC_PER_MSEC), UINT32_MAX) * USEC_PER_MSEC;
1908}
1909
1910uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h) {
1911 assert(h);
1912
1913 /* Returns a value with kb granularity, since that's what libcryptsetup expects */
1914
1915 if (h->luks_pbkdf_memory_cost == UINT64_MAX)
1916 return 64*1024*1024; /* We default to 64M, since this should work on smaller systems too */
1917
1918 return MIN(DIV_ROUND_UP(h->luks_pbkdf_memory_cost, 1024), UINT32_MAX) * 1024;
1919}
1920
1921uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h) {
1922 assert(h);
1923
1924 if (h->luks_pbkdf_memory_cost == UINT64_MAX)
1925 return 1; /* We default to 1, since this should work on smaller systems too */
1926
1927 return MIN(h->luks_pbkdf_parallel_threads, UINT32_MAX);
1928}
1929
1930const char *user_record_luks_pbkdf_hash_algorithm(UserRecord *h) {
1931 assert(h);
1932
1933 return h->luks_pbkdf_hash_algorithm ?: "sha512";
1934}
1935
1936gid_t user_record_gid(UserRecord *h) {
1937 assert(h);
1938
1939 if (gid_is_valid(h->gid))
1940 return h->gid;
1941
1942 return (gid_t) h->uid;
1943}
1944
1945UserDisposition user_record_disposition(UserRecord *h) {
1946 assert(h);
1947
1948 if (h->disposition >= 0)
1949 return h->disposition;
1950
1951 /* If not declared, derive from UID */
1952
1953 if (!uid_is_valid(h->uid))
1954 return _USER_DISPOSITION_INVALID;
1955
1956 if (h->uid == 0 || h->uid == UID_NOBODY)
1957 return USER_INTRINSIC;
1958
1959 if (uid_is_system(h->uid))
1960 return USER_SYSTEM;
1961
1962 if (uid_is_dynamic(h->uid))
1963 return USER_DYNAMIC;
1964
1965 if (uid_is_container(h->uid))
1966 return USER_CONTAINER;
1967
1968 if (h->uid > INT32_MAX)
1969 return USER_RESERVED;
1970
1971 return USER_REGULAR;
1972}
1973
1974int user_record_removable(UserRecord *h) {
1975 UserStorage storage;
1976 assert(h);
1977
1978 if (h->removable >= 0)
1979 return h->removable;
1980
1981 /* Refuse to decide for classic records */
1982 storage = user_record_storage(h);
1983 if (h->storage < 0 || h->storage == USER_CLASSIC)
1984 return -1;
1985
1986 /* For now consider only LUKS home directories with a reference by path as removable */
1987 return storage == USER_LUKS && path_startswith(user_record_image_path(h), "/dev/");
1988}
1989
1990uint64_t user_record_ratelimit_interval_usec(UserRecord *h) {
1991 assert(h);
1992
1993 if (h->ratelimit_interval_usec == UINT64_MAX)
1994 return DEFAULT_RATELIMIT_INTERVAL_USEC;
1995
1996 return h->ratelimit_interval_usec;
1997}
1998
1999uint64_t user_record_ratelimit_burst(UserRecord *h) {
2000 assert(h);
2001
2002 if (h->ratelimit_burst == UINT64_MAX)
2003 return DEFAULT_RATELIMIT_BURST;
2004
2005 return h->ratelimit_burst;
2006}
2007
2008bool user_record_can_authenticate(UserRecord *h) {
2009 assert(h);
2010
2011 /* Returns true if there's some form of property configured that the user can authenticate against */
2012
2013 if (h->n_pkcs11_encrypted_key > 0)
2014 return true;
2015
5e4fa456
LP
2016 if (h->n_fido2_hmac_salt > 0)
2017 return true;
2018
71d0b9d4
LP
2019 return !strv_isempty(h->hashed_password);
2020}
2021
2022uint64_t user_record_ratelimit_next_try(UserRecord *h) {
2023 assert(h);
2024
2025 /* Calculates when the it's possible to login next. Returns:
2026 *
2027 * UINT64_MAX → Nothing known
2028 * 0 → Right away
2029 * Any other → Next time in CLOCK_REALTIME in usec (which could be in the past)
2030 */
2031
2032 if (h->ratelimit_begin_usec == UINT64_MAX ||
2033 h->ratelimit_count == UINT64_MAX)
2034 return UINT64_MAX;
2035
61a29a02
LP
2036 if (h->ratelimit_begin_usec > now(CLOCK_REALTIME)) /* If the ratelimit time is in the future, then
2037 * the local clock is probably incorrect. Let's
2038 * not refuse login then. */
2039 return UINT64_MAX;
2040
71d0b9d4
LP
2041 if (h->ratelimit_count < user_record_ratelimit_burst(h))
2042 return 0;
2043
2044 return usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h));
2045}
2046
2047bool user_record_equal(UserRecord *a, UserRecord *b) {
2048 assert(a);
2049 assert(b);
2050
2051 /* We assume that when a record is modified its JSON data is updated at the same time, hence it's
2052 * sufficient to compare the JSON data. */
2053
2054 return json_variant_equal(a->json, b->json);
2055}
2056
2057bool user_record_compatible(UserRecord *a, UserRecord *b) {
2058 assert(a);
2059 assert(b);
2060
d51c4fca 2061 /* If either lacks the regular section, we can't really decide, let's hence say they are
71d0b9d4
LP
2062 * incompatible. */
2063 if (!(a->mask & b->mask & USER_RECORD_REGULAR))
2064 return false;
2065
2066 return streq_ptr(a->user_name, b->user_name) &&
2067 streq_ptr(a->realm, b->realm);
2068}
2069
2070int user_record_compare_last_change(UserRecord *a, UserRecord *b) {
2071 assert(a);
2072 assert(b);
2073
2074 if (a->last_change_usec == b->last_change_usec)
2075 return 0;
2076
2077 /* Always consider a record with a timestamp newer than one without */
2078 if (a->last_change_usec == UINT64_MAX)
2079 return -1;
2080 if (b->last_change_usec == UINT64_MAX)
2081 return 1;
2082
2083 return CMP(a->last_change_usec, b->last_change_usec);
2084}
2085
2086int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret) {
2087 _cleanup_(user_record_unrefp) UserRecord *c = NULL;
2088 int r;
2089
2090 assert(h);
2091 assert(ret);
2092
2093 c = user_record_new();
2094 if (!c)
2095 return -ENOMEM;
2096
2097 r = user_record_load(c, h->json, flags);
2098 if (r < 0)
2099 return r;
2100
2101 *ret = TAKE_PTR(c);
2102 return 0;
2103}
2104
2105int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask) {
2106 _cleanup_(user_record_unrefp) UserRecord *x = NULL, *y = NULL;
2107 int r;
2108
2109 assert(a);
2110 assert(b);
2111
2112 /* Compares the two records, but ignores anything not listed in the specified mask */
2113
2114 if ((a->mask & ~mask) != 0) {
bfc0cc1a 2115 r = user_record_clone(a, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &x);
71d0b9d4
LP
2116 if (r < 0)
2117 return r;
2118
2119 a = x;
2120 }
2121
2122 if ((b->mask & ~mask) != 0) {
bfc0cc1a 2123 r = user_record_clone(b, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &y);
71d0b9d4
LP
2124 if (r < 0)
2125 return r;
2126
2127 b = y;
2128 }
2129
2130 return user_record_equal(a, b);
2131}
2132
2133int user_record_test_blocked(UserRecord *h) {
2134 usec_t n;
2135
2136 /* Checks whether access to the specified user shall be allowed at the moment. Returns:
2137 *
2138 * -ESTALE: Record is from the future
2139 * -ENOLCK: Record is blocked
2140 * -EL2HLT: Record is not valid yet
2141 * -EL3HLT: Record is not valid anymore
2142 *
2143 */
2144
2145 assert(h);
2146
71d0b9d4
LP
2147 if (h->locked > 0)
2148 return -ENOLCK;
2149
51a95db6
LP
2150 n = now(CLOCK_REALTIME);
2151
71d0b9d4
LP
2152 if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
2153 return -EL2HLT;
2154 if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
2155 return -EL3HLT;
2156
51a95db6
LP
2157 if (h->last_change_usec != UINT64_MAX &&
2158 h->last_change_usec > n) /* Complain during log-ins when the record is from the future */
2159 return -ESTALE;
2160
71d0b9d4
LP
2161 return 0;
2162}
2163
2164int user_record_test_password_change_required(UserRecord *h) {
2165 bool change_permitted;
2166 usec_t n;
2167
2168 assert(h);
2169
2170 /* Checks whether the user must change the password when logging in
2171
2172 -EKEYREVOKED: Change password now because admin said so
2173 -EOWNERDEAD: Change password now because it expired
2174 -EKEYREJECTED: Password is expired, no changing is allowed
2175 -EKEYEXPIRED: Password is about to expire, warn user
2176 -ENETDOWN: Record has expiration info but no password change timestamp
2177 -EROFS: No password change required nor permitted
3e0b5486 2178 -ESTALE: RTC likely incorrect, last password change is in the future
71d0b9d4
LP
2179 0: No password change required, but permitted
2180 */
2181
162392b7 2182 /* If a password change request has been set explicitly, it overrides everything */
71d0b9d4
LP
2183 if (h->password_change_now > 0)
2184 return -EKEYREVOKED;
2185
2186 n = now(CLOCK_REALTIME);
2187
3e0b5486
LP
2188 /* Password change in the future? Then our RTC is likely incorrect */
2189 if (h->last_password_change_usec != UINT64_MAX &&
2190 h->last_password_change_usec > n &&
2191 (h->password_change_min_usec != UINT64_MAX ||
2192 h->password_change_max_usec != UINT64_MAX ||
2193 h->password_change_inactive_usec != UINT64_MAX))
2194 return -ESTALE;
2195
71d0b9d4
LP
2196 /* Then, let's check if password changing is currently allowed at all */
2197 if (h->password_change_min_usec != UINT64_MAX) {
2198
2199 /* Expiry configured but no password change timestamp known? */
2200 if (h->last_password_change_usec == UINT64_MAX)
2201 return -ENETDOWN;
2202
2203 if (h->password_change_min_usec >= UINT64_MAX - h->last_password_change_usec)
2204 change_permitted = false;
2205 else
2206 change_permitted = n >= h->last_password_change_usec + h->password_change_min_usec;
2207
2208 } else
2209 change_permitted = true;
2210
2211 /* Let's check whether the password has expired. */
2212 if (!(h->password_change_max_usec == UINT64_MAX ||
2213 h->password_change_max_usec >= UINT64_MAX - h->last_password_change_usec)) {
2214
2215 uint64_t change_before;
2216
2217 /* Expiry configured but no password change timestamp known? */
2218 if (h->last_password_change_usec == UINT64_MAX)
2219 return -ENETDOWN;
2220
2221 /* Password is in inactive phase? */
2222 if (h->password_change_inactive_usec != UINT64_MAX &&
2223 h->password_change_inactive_usec < UINT64_MAX - h->password_change_max_usec) {
2224 usec_t added;
2225
2226 added = h->password_change_inactive_usec + h->password_change_max_usec;
2227 if (added < UINT64_MAX - h->last_password_change_usec &&
2228 n >= h->last_password_change_usec + added)
2229 return -EKEYREJECTED;
2230 }
2231
2232 /* Password needs to be changed now? */
2233 change_before = h->last_password_change_usec + h->password_change_max_usec;
2234 if (n >= change_before)
2235 return change_permitted ? -EOWNERDEAD : -EKEYREJECTED;
2236
2237 /* Warn user? */
2238 if (h->password_change_warn_usec != UINT64_MAX &&
2239 (change_before < h->password_change_warn_usec ||
2240 n >= change_before - h->password_change_warn_usec))
2241 return change_permitted ? -EKEYEXPIRED : -EROFS;
2242 }
2243
2244 /* No password changing necessary */
2245 return change_permitted ? 0 : -EROFS;
2246}
2247
2248static const char* const user_storage_table[_USER_STORAGE_MAX] = {
2249 [USER_CLASSIC] = "classic",
2250 [USER_LUKS] = "luks",
2251 [USER_DIRECTORY] = "directory",
2252 [USER_SUBVOLUME] = "subvolume",
2253 [USER_FSCRYPT] = "fscrypt",
2254 [USER_CIFS] = "cifs",
2255};
2256
2257DEFINE_STRING_TABLE_LOOKUP(user_storage, UserStorage);
2258
2259static const char* const user_disposition_table[_USER_DISPOSITION_MAX] = {
2260 [USER_INTRINSIC] = "intrinsic",
2261 [USER_SYSTEM] = "system",
2262 [USER_DYNAMIC] = "dynamic",
2263 [USER_REGULAR] = "regular",
2264 [USER_CONTAINER] = "container",
2265 [USER_RESERVED] = "reserved",
2266};
2267
2268DEFINE_STRING_TABLE_LOOKUP(user_disposition, UserDisposition);