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