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