• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 20151578145

11 Dec 2025 05:38AM UTC coverage: 72.698% (-0.02%) from 72.713%
20151578145

push

github

web-flow
core: gracefully skip unknown policy designators in RootImagePolicy et al (#40060)

Usually we gracefully ignore unknown configuration parameters, so that
service files can be written by upstreams and used across a variegated
range of distributions with various versions of systemd, to avoid
forcing users to the minimum common denominator and only adding settings
that are supported by the oldest distro supported.

Image policies do not behave like this, and any unknown partition or
policy designator causes the whole unit to fail to parse and a hard
error.

Change it so that parsing RootImagePolicy and friends via unit file or
D-Bus logs but otherwise ignores unknown specifiers, like other options
do.

This allows us to add new specifiers in the future, and users to adopt
them immediately.

Follow-up for d452335aa

44 of 49 new or added lines in 7 files covered. (89.8%)

297 existing lines in 37 files now uncovered.

309479 of 425707 relevant lines covered (72.7%)

1150153.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

54.05
/src/shared/user-record.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <sys/mount.h>
4

5
#include "alloc-util.h"
6
#include "bitfield.h"
7
#include "capability-list.h"
8
#include "cgroup-util.h"
9
#include "dns-domain.h"
10
#include "glyph-util.h"
11
#include "hashmap.h"
12
#include "hostname-setup.h"
13
#include "json-util.h"
14
#include "locale-util.h"
15
#include "log.h"
16
#include "memory-util.h"
17
#include "path-util.h"
18
#include "percent-util.h"
19
#include "pkcs11-util.h"
20
#include "rlimit-util.h"
21
#include "sha256.h"
22
#include "string-table.h"
23
#include "string-util.h"
24
#include "strv.h"
25
#include "time-util.h"
26
#include "uid-classification.h"
27
#include "user-record.h"
28
#include "user-util.h"
29

30
#define DEFAULT_RATELIMIT_BURST 30
31
#define DEFAULT_RATELIMIT_INTERVAL_USEC (1*USEC_PER_MINUTE)
32

33
UserRecord* user_record_new(void) {
15,152✔
34
        UserRecord *h;
15,152✔
35

36
        h = new(UserRecord, 1);
15,152✔
37
        if (!h)
15,152✔
38
                return NULL;
39

40
        *h = (UserRecord) {
15,152✔
41
                .n_ref = 1,
42
                .disposition = _USER_DISPOSITION_INVALID,
43
                .last_change_usec = UINT64_MAX,
44
                .last_password_change_usec = UINT64_MAX,
45
                .umask = MODE_INVALID,
46
                .nice_level = INT_MAX,
47
                .not_before_usec = UINT64_MAX,
48
                .not_after_usec = UINT64_MAX,
49
                .locked = -1,
50
                .storage = _USER_STORAGE_INVALID,
51
                .access_mode = MODE_INVALID,
52
                .disk_size = UINT64_MAX,
53
                .disk_size_relative = UINT64_MAX,
54
                .tasks_max = UINT64_MAX,
55
                .memory_high = UINT64_MAX,
56
                .memory_max = UINT64_MAX,
57
                .cpu_weight = UINT64_MAX,
58
                .io_weight = UINT64_MAX,
59
                .uid = UID_INVALID,
60
                .gid = GID_INVALID,
61
                .nodev = true,
62
                .nosuid = true,
63
                .luks_discard = -1,
64
                .luks_offline_discard = -1,
65
                .luks_volume_key_size = UINT64_MAX,
66
                .luks_pbkdf_force_iterations = UINT64_MAX,
67
                .luks_pbkdf_time_cost_usec = UINT64_MAX,
68
                .luks_pbkdf_memory_cost = UINT64_MAX,
69
                .luks_pbkdf_parallel_threads = UINT64_MAX,
70
                .luks_sector_size = UINT64_MAX,
71
                .disk_usage = UINT64_MAX,
72
                .disk_free = UINT64_MAX,
73
                .disk_ceiling = UINT64_MAX,
74
                .disk_floor = UINT64_MAX,
75
                .signed_locally = -1,
76
                .good_authentication_counter = UINT64_MAX,
77
                .bad_authentication_counter = UINT64_MAX,
78
                .last_good_authentication_usec = UINT64_MAX,
79
                .last_bad_authentication_usec = UINT64_MAX,
80
                .ratelimit_begin_usec = UINT64_MAX,
81
                .ratelimit_count = UINT64_MAX,
82
                .ratelimit_interval_usec = UINT64_MAX,
83
                .ratelimit_burst = UINT64_MAX,
84
                .removable = -1,
85
                .enforce_password_policy = -1,
86
                .auto_login = -1,
87
                .stop_delay_usec = UINT64_MAX,
88
                .kill_processes = -1,
89
                .password_change_min_usec = UINT64_MAX,
90
                .password_change_max_usec = UINT64_MAX,
91
                .password_change_warn_usec = UINT64_MAX,
92
                .password_change_inactive_usec = UINT64_MAX,
93
                .password_change_now = -1,
94
                .pkcs11_protected_authentication_path_permitted = -1,
95
                .fido2_user_presence_permitted = -1,
96
                .fido2_user_verification_permitted = -1,
97
                .drop_caches = -1,
98
                .auto_resize_mode = _AUTO_RESIZE_MODE_INVALID,
99
                .rebalance_weight = REBALANCE_WEIGHT_UNSET,
100
                .tmp_limit = TMPFS_LIMIT_NULL,
101
                .dev_shm_limit = TMPFS_LIMIT_NULL,
102
        };
103

104
        return h;
15,152✔
105
}
106

107
sd_json_dispatch_flags_t USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(UserRecordLoadFlags flags) {
46,748✔
108
        return (FLAGS_SET(flags, USER_RECORD_LOG) ? SD_JSON_LOG : 0) |
46,748✔
109
                (FLAGS_SET(flags, USER_RECORD_PERMISSIVE) ? SD_JSON_PERMISSIVE : 0);
46,748✔
110
}
111

112
static void pkcs11_encrypted_key_done(Pkcs11EncryptedKey *k) {
×
113
        if (!k)
×
114
                return;
115

116
        free(k->uri);
×
117
        erase_and_free(k->data);
×
118
        erase_and_free(k->hashed_password);
×
119
}
120

121
static void fido2_hmac_credential_done(Fido2HmacCredential *c) {
×
122
        if (!c)
×
123
                return;
124

125
        free(c->id);
×
126
}
127

128
static void fido2_hmac_salt_done(Fido2HmacSalt *s) {
×
129
        if (!s)
×
130
                return;
131

132
        fido2_hmac_credential_done(&s->credential);
×
133
        erase_and_free(s->salt);
×
134
        erase_and_free(s->hashed_password);
×
135
}
136

137
static void recovery_key_done(RecoveryKey *k) {
×
138
        if (!k)
×
139
                return;
140

141
        free(k->type);
×
142
        erase_and_free(k->hashed_password);
×
143
}
144

145
static UserRecord* user_record_free(UserRecord *h) {
15,117✔
146
        if (!h)
15,117✔
147
                return NULL;
148

149
        free(h->user_name);
15,117✔
150
        free(h->realm);
15,117✔
151
        free(h->user_name_and_realm_auto);
15,117✔
152
        strv_free(h->aliases);
15,117✔
153
        free(h->real_name);
15,117✔
154
        free(h->email_address);
15,117✔
155
        erase_and_free(h->password_hint);
15,117✔
156
        free(h->location);
15,117✔
157
        free(h->icon_name);
15,117✔
158

159
        free(h->blob_directory);
15,117✔
160
        hashmap_free(h->blob_manifest);
15,117✔
161

162
        free(h->shell);
15,117✔
163

164
        strv_free(h->environment);
15,117✔
165
        free(h->time_zone);
15,117✔
166
        free(h->preferred_language);
15,117✔
167
        strv_free(h->additional_languages);
15,117✔
168
        rlimit_free_all(h->rlimits);
15,117✔
169

170
        free(h->skeleton_directory);
15,117✔
171

172
        strv_free_erase(h->hashed_password);
15,117✔
173
        strv_free_erase(h->ssh_authorized_keys);
15,117✔
174
        strv_free_erase(h->password);
15,117✔
175
        strv_free_erase(h->token_pin);
15,117✔
176

177
        free(h->cifs_service);
15,117✔
178
        free(h->cifs_user_name);
15,117✔
179
        free(h->cifs_domain);
15,117✔
180
        free(h->cifs_extra_mount_options);
15,117✔
181

182
        free(h->image_path);
15,117✔
183
        free(h->image_path_auto);
15,117✔
184
        free(h->home_directory);
15,117✔
185
        free(h->home_directory_auto);
15,117✔
186

187
        free(h->fallback_shell);
15,117✔
188
        free(h->fallback_home_directory);
15,117✔
189

190
        strv_free(h->member_of);
15,117✔
191
        strv_free(h->capability_bounding_set);
15,117✔
192
        strv_free(h->capability_ambient_set);
15,117✔
193

194
        free(h->file_system_type);
15,117✔
195
        free(h->luks_cipher);
15,117✔
196
        free(h->luks_cipher_mode);
15,117✔
197
        free(h->luks_pbkdf_hash_algorithm);
15,117✔
198
        free(h->luks_pbkdf_type);
15,117✔
199
        free(h->luks_extra_mount_options);
15,117✔
200

201
        free(h->state);
15,117✔
202
        free(h->service);
15,117✔
203

204
        free(h->preferred_session_type);
15,117✔
205
        free(h->preferred_session_launcher);
15,117✔
206

207
        strv_free(h->pkcs11_token_uri);
15,117✔
208
        for (size_t i = 0; i < h->n_pkcs11_encrypted_key; i++)
15,117✔
209
                pkcs11_encrypted_key_done(h->pkcs11_encrypted_key + i);
×
210
        free(h->pkcs11_encrypted_key);
15,117✔
211

212
        for (size_t i = 0; i < h->n_fido2_hmac_credential; i++)
15,117✔
213
                fido2_hmac_credential_done(h->fido2_hmac_credential + i);
×
214
        for (size_t i = 0; i < h->n_fido2_hmac_salt; i++)
15,117✔
215
                fido2_hmac_salt_done(h->fido2_hmac_salt + i);
×
216

217
        strv_free(h->recovery_key_type);
15,117✔
218
        for (size_t i = 0; i < h->n_recovery_key; i++)
15,117✔
219
                recovery_key_done(h->recovery_key + i);
×
220

221
        strv_free(h->self_modifiable_fields);
15,117✔
222
        strv_free(h->self_modifiable_blobs);
15,117✔
223
        strv_free(h->self_modifiable_privileged);
15,117✔
224

225
        free(h->default_area);
15,117✔
226

227
        sd_json_variant_unref(h->json);
15,117✔
228

229
        return mfree(h);
15,117✔
230
}
231

232
DEFINE_TRIVIAL_REF_UNREF_FUNC(UserRecord, user_record, user_record_free);
29,155✔
233

234
int json_dispatch_realm(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
94✔
235
        char **s = userdata;
94✔
236
        const char *n;
94✔
237
        int r;
94✔
238

239
        if (sd_json_variant_is_null(variant)) {
94✔
240
                *s = mfree(*s);
×
241
                return 0;
×
242
        }
243

244
        if (!sd_json_variant_is_string(variant))
94✔
245
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
246

247
        n = sd_json_variant_string(variant);
94✔
248
        r = dns_name_is_valid(n);
94✔
249
        if (r < 0)
94✔
250
                return json_log(variant, flags, r, "Failed to check if JSON field '%s' is a valid DNS domain.", strna(name));
×
251
        if (r == 0)
94✔
252
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid DNS domain.", strna(name));
×
253

254
        r = free_and_strdup(s, n);
94✔
255
        if (r < 0)
94✔
256
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
257

258
        return 0;
259
}
260

261
int json_dispatch_gecos(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
7,293✔
262
        char **s = userdata;
7,293✔
263
        const char *n;
7,293✔
264

265
        if (sd_json_variant_is_null(variant)) {
7,293✔
266
                *s = mfree(*s);
×
267
                return 0;
×
268
        }
269

270
        if (!sd_json_variant_is_string(variant))
7,293✔
271
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
272

273
        n = sd_json_variant_string(variant);
7,293✔
274
        if (valid_gecos(n)) {
7,293✔
275
                if (free_and_strdup(s, n) < 0)
7,293✔
276
                        return json_log_oom(variant, flags);
×
277
        } else {
278
                _cleanup_free_ char *m = NULL;
×
279

280
                json_log(variant, flags|SD_JSON_DEBUG, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid GECOS compatible string, mangling.", strna(name));
×
281

282
                m = mangle_gecos(n);
×
283
                if (!m)
×
284
                        return json_log_oom(variant, flags);
×
285

286
                free_and_replace(*s, m);
×
287
        }
288

289
        return 0;
290
}
291

292
static int json_dispatch_nice(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
169✔
293
        int *nl = userdata;
169✔
294
        int64_t m;
169✔
295

296
        if (sd_json_variant_is_null(variant)) {
169✔
297
                *nl = INT_MAX;
×
298
                return 0;
×
299
        }
300

301
        if (!sd_json_variant_is_integer(variant))
169✔
302
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
303

304
        m = sd_json_variant_integer(variant);
169✔
305
        if (m < PRIO_MIN || m >= PRIO_MAX)
169✔
306
                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' is not a valid nice level.", strna(name));
×
307

308
        *nl = m;
169✔
309
        return 0;
169✔
310
}
311

312
static int json_dispatch_rlimit_value(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
313
        rlim_t *ret = userdata;
×
314

315
        if (sd_json_variant_is_null(variant))
×
316
                *ret = RLIM_INFINITY;
×
317
        else if (sd_json_variant_is_unsigned(variant)) {
×
318
                uint64_t w;
×
319

320
                w = sd_json_variant_unsigned(variant);
×
321
                if (w == RLIM_INFINITY || w != sd_json_variant_unsigned(variant))
×
322
                        return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "Resource limit value '%s' is out of range.", name);
×
323

324
                *ret = (rlim_t) w;
×
325
        } else
326
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit value '%s' is not an unsigned integer.", name);
×
327

328
        return 0;
329
}
330

331
static int json_dispatch_rlimits(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
332
        struct rlimit** limits = userdata;
×
333
        sd_json_variant *value;
×
334
        const char *key;
×
335
        int r;
×
336

337
        assert_se(limits);
×
338

339
        if (sd_json_variant_is_null(variant)) {
×
340
                rlimit_free_all(limits);
×
341
                return 0;
×
342
        }
343

344
        if (!sd_json_variant_is_object(variant))
×
345
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
346

347
        JSON_VARIANT_OBJECT_FOREACH(key, value, variant) {
×
348
                sd_json_variant *jcur, *jmax;
×
349
                struct rlimit rl;
×
350
                const char *p;
×
351
                int l;
×
352

353
                p = startswith(key, "RLIMIT_");
×
354
                if (!p)
×
355
                        l = -SYNTHETIC_ERRNO(EINVAL);
356
                else
357
                        l = rlimit_from_string(p);
×
358
                if (l < 0)
×
359
                        return json_log(variant, flags, l, "Resource limit '%s' not known.", key);
×
360

361
                if (!sd_json_variant_is_object(value))
×
362
                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' has invalid value.", key);
×
363

364
                if (sd_json_variant_elements(value) != 4)
×
365
                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' value is does not have two fields as expected.", key);
×
366

367
                jcur = sd_json_variant_by_key(value, "cur");
×
368
                if (!jcur)
×
369
                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'cur' field.", key);
×
370
                r = json_dispatch_rlimit_value("cur", jcur, flags, &rl.rlim_cur);
×
371
                if (r < 0)
×
372
                        return r;
373

374
                jmax = sd_json_variant_by_key(value, "max");
×
375
                if (!jmax)
×
376
                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Resource limit '%s' lacks 'max' field.", key);
×
377
                r = json_dispatch_rlimit_value("max", jmax, flags, &rl.rlim_max);
×
378
                if (r < 0)
×
379
                        return r;
380

381
                if (limits[l])
×
382
                        *(limits[l]) = rl;
×
383
                else {
384
                        limits[l] = newdup(struct rlimit, &rl, 1);
×
385
                        if (!limits[l])
×
386
                                return log_oom();
×
387
                }
388
        }
389

390
        return 0;
×
391
}
392

393
static int json_dispatch_filename_or_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
10,824✔
394
        char **s = ASSERT_PTR(userdata);
10,824✔
395
        const char *n;
10,824✔
396
        int r;
10,824✔
397

398
        if (sd_json_variant_is_null(variant)) {
10,824✔
399
                *s = mfree(*s);
×
400
                return 0;
×
401
        }
402

403
        if (!sd_json_variant_is_string(variant))
10,824✔
404
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
405

406
        n = sd_json_variant_string(variant);
10,824✔
407
        if (!filename_is_valid(n) && !path_is_normalized(n))
10,824✔
408
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid file name or normalized path.", strna(name));
×
409

410
        r = free_and_strdup(s, n);
10,824✔
411
        if (r < 0)
10,824✔
412
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
413

414
        return 0;
415
}
416

417
static int json_dispatch_home_directory(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
4,734✔
418
        char **s = userdata;
4,734✔
419
        const char *n;
4,734✔
420
        int r;
4,734✔
421

422
        if (sd_json_variant_is_null(variant)) {
4,734✔
423
                *s = mfree(*s);
×
424
                return 0;
×
425
        }
426

427
        if (!sd_json_variant_is_string(variant))
4,734✔
428
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
429

430
        n = sd_json_variant_string(variant);
4,734✔
431
        if (!valid_home(n))
4,734✔
432
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid home directory path.", strna(name));
×
433

434
        r = free_and_strdup(s, n);
4,734✔
435
        if (r < 0)
4,734✔
436
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
437

438
        return 0;
439
}
440

441
static int json_dispatch_image_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,647✔
442
        char **s = userdata;
1,647✔
443
        const char *n;
1,647✔
444
        int r;
1,647✔
445

446
        if (sd_json_variant_is_null(variant)) {
1,647✔
447
                *s = mfree(*s);
×
448
                return 0;
×
449
        }
450

451
        if (!sd_json_variant_is_string(variant))
1,647✔
452
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
453

454
        n = sd_json_variant_string(variant);
1,647✔
455
        if (empty_or_root(n) || !path_is_valid(n) || !path_is_absolute(n))
4,941✔
456
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid image path.", strna(name));
×
457

458
        r = free_and_strdup(s, n);
1,647✔
459
        if (r < 0)
1,647✔
460
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
461

462
        return 0;
463
}
464

465
static int json_dispatch_umask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
466
        mode_t *m = userdata;
×
467
        uint64_t k;
×
468

469
        if (sd_json_variant_is_null(variant)) {
×
470
                *m = MODE_INVALID;
×
471
                return 0;
×
472
        }
473

474
        if (!sd_json_variant_is_unsigned(variant))
×
475
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
×
476

477
        k = sd_json_variant_unsigned(variant);
×
478
        if (k > 0777)
×
479
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
×
480
                                "JSON field '%s' outside of valid range 0%s0777.",
481
                                strna(name), glyph(GLYPH_ELLIPSIS));
482

483
        *m = (mode_t) k;
×
484
        return 0;
×
485
}
486

487
static int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
488
        mode_t *m = userdata;
×
489
        uint64_t k;
×
490

491
        if (sd_json_variant_is_null(variant)) {
×
492
                *m = MODE_INVALID;
×
493
                return 0;
×
494
        }
495

496
        if (!sd_json_variant_is_unsigned(variant))
×
497
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
×
498

499
        k = sd_json_variant_unsigned(variant);
×
500
        if (k > 07777)
×
501
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
×
502
                                "JSON field '%s' outside of valid range 0%s07777.",
503
                                strna(name), glyph(GLYPH_ELLIPSIS));
504

505
        *m = (mode_t) k;
×
506
        return 0;
×
507
}
508

509
static int json_dispatch_locale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
510
        char **s = userdata;
×
511
        const char *n;
×
512
        int r;
×
513

514
        if (sd_json_variant_is_null(variant)) {
×
515
                *s = mfree(*s);
×
516
                return 0;
×
517
        }
518

519
        if (!sd_json_variant_is_string(variant))
×
520
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
521

522
        n = sd_json_variant_string(variant);
×
523

524
        if (!locale_is_valid(n))
×
525
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid locale.", strna(name));
×
526

527
        r = free_and_strdup(s, n);
×
528
        if (r < 0)
×
529
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
530

531
        return 0;
532
}
533

534
static int json_dispatch_locales(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
535
        _cleanup_strv_free_ char **n = NULL;
×
536
        char ***l = userdata;
×
537
        const char *locale;
×
538
        sd_json_variant *e;
×
539
        int r;
×
540

541
        if (sd_json_variant_is_null(variant)) {
×
542
                *l = strv_free(*l);
×
543
                return 0;
×
544
        }
545

546
        if (!sd_json_variant_is_array(variant))
×
547
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
548

549
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
550
                if (!sd_json_variant_is_string(e))
×
551
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
552

553
                locale = sd_json_variant_string(e);
×
554
                if (!locale_is_valid(locale))
×
555
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of valid locales.", strna(name));
×
556

557
                r = strv_extend(&n, locale);
×
558
                if (r < 0)
×
559
                        return json_log_oom(variant, flags);
×
560
        }
561

562
        return strv_free_and_replace(*l, n);
×
563
}
564

565
JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_disposition, UserDisposition, user_disposition_from_string);
16,472✔
566
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_storage, UserStorage, user_storage_from_string);
2,811✔
567

568
static int json_dispatch_tasks_or_memory_max(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
569
        uint64_t *limit = userdata, k;
×
570

571
        if (sd_json_variant_is_null(variant)) {
×
572
                *limit = UINT64_MAX;
×
573
                return 0;
×
574
        }
575

576
        if (!sd_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 = sd_json_variant_unsigned(variant);
×
580
        if (k <= 0 || k >= UINT64_MAX)
×
581
                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
×
582
                                "JSON field '%s' is not in valid range %" PRIu64 "%s%" PRIu64 ".",
583
                                strna(name), (uint64_t) 1, glyph(GLYPH_ELLIPSIS), UINT64_MAX-1);
584

585
        *limit = k;
×
586
        return 0;
×
587
}
588

589
static int json_dispatch_weight(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
590
        uint64_t *weight = userdata, k;
×
591

592
        if (sd_json_variant_is_null(variant)) {
×
593
                *weight = UINT64_MAX;
×
594
                return 0;
×
595
        }
596

597
        if (!sd_json_variant_is_unsigned(variant))
×
598
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
×
599

600
        k = sd_json_variant_unsigned(variant);
×
601
        if (k <= CGROUP_WEIGHT_MIN || k >= CGROUP_WEIGHT_MAX)
×
602
                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
×
603
                                "JSON field '%s' is not in valid range %" PRIu64 "%s%" PRIu64 ".",
604
                                strna(name), (uint64_t) CGROUP_WEIGHT_MIN,
605
                                glyph(GLYPH_ELLIPSIS), (uint64_t) CGROUP_WEIGHT_MAX);
606

607
        *weight = k;
×
608
        return 0;
×
609
}
610

611
int json_dispatch_user_group_list(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
233✔
612
        char ***list = ASSERT_PTR(userdata);
233✔
613
        _cleanup_strv_free_ char **l = NULL;
233✔
614
        int r;
233✔
615

616
        if (!sd_json_variant_is_array(variant))
233✔
617
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
618

619
        sd_json_variant *e;
233✔
620
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
462✔
621
                if (!sd_json_variant_is_string(e))
229✔
622
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
×
623

624
                if (!valid_user_group_name(sd_json_variant_string(e), FLAGS_SET(flags, SD_JSON_RELAX) ? VALID_USER_RELAX : 0))
229✔
625
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a valid user/group name: %s", sd_json_variant_string(e));
×
626

627
                r = strv_extend(&l, sd_json_variant_string(e));
229✔
628
                if (r < 0)
229✔
629
                        return json_log(e, flags, r, "Failed to append array element: %m");
×
630
        }
631

632
        r = strv_extend_strv_consume(list, TAKE_PTR(l), /* filter_duplicates = */ true);
233✔
633
        if (r < 0)
233✔
634
                return json_log(variant, flags, r, "Failed to merge user/group arrays: %m");
×
635

636
        return 0;
637
}
638

639
static int dispatch_secret(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
149✔
640

641
        static const sd_json_dispatch_field secret_dispatch_table[] = {
149✔
642
                { "password",                                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,     offsetof(UserRecord, password),                                       0 },
643
                { "tokenPin",                                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
644
                { "pkcs11Pin",   /* legacy alias */             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
645
                { "pkcs11ProtectedAuthenticationPathPermitted", SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
646
                { "fido2UserPresencePermitted",                 SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate, offsetof(UserRecord, fido2_user_presence_permitted),                  0 },
647
                { "fido2UserVerificationPermitted",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate, offsetof(UserRecord, fido2_user_verification_permitted),              0 },
648
                {},
649
        };
650

651
        return sd_json_dispatch(variant, secret_dispatch_table, flags, userdata);
149✔
652
}
653

654
static int dispatch_pkcs11_uri(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
655
        char **s = userdata;
×
656
        const char *n;
×
657
        int r;
×
658

659
        if (sd_json_variant_is_null(variant)) {
×
660
                *s = mfree(*s);
×
661
                return 0;
×
662
        }
663

664
        if (!sd_json_variant_is_string(variant))
×
665
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
666

667
        n = sd_json_variant_string(variant);
×
668
        if (!pkcs11_uri_valid(n))
×
669
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
×
670

671
        r = free_and_strdup(s, n);
×
672
        if (r < 0)
×
673
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
674

675
        return 0;
676
}
677

678
static int dispatch_pkcs11_uri_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
679
        _cleanup_strv_free_ char **z = NULL;
×
680
        char ***l = userdata;
×
681
        sd_json_variant *e;
×
682
        int r;
×
683

684
        if (sd_json_variant_is_null(variant)) {
×
685
                *l = strv_free(*l);
×
686
                return 0;
×
687
        }
688

689
        if (sd_json_variant_is_string(variant)) {
×
690
                const char *n;
×
691

692
                n = sd_json_variant_string(variant);
×
693
                if (!pkcs11_uri_valid(n))
×
694
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid RFC7512 PKCS#11 URI.", strna(name));
×
695

696
                z = strv_new(n);
×
697
                if (!z)
×
698
                        return log_oom();
×
699

700
        } else {
701

702
                if (!sd_json_variant_is_array(variant))
×
703
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string or array of strings.", strna(name));
×
704

705
                JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
706
                        const char *n;
×
707

708
                        if (!sd_json_variant_is_string(e))
×
709
                                return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
×
710

711
                        n = sd_json_variant_string(e);
×
712
                        if (!pkcs11_uri_valid(n))
×
713
                                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);
×
714

715
                        r = strv_extend(&z, n);
×
716
                        if (r < 0)
×
717
                                return log_oom();
×
718
                }
719
        }
720

721
        strv_free_and_replace(*l, z);
×
722
        return 0;
×
723
}
724

725
static int dispatch_pkcs11_key_data(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
726
        Pkcs11EncryptedKey *k = userdata;
×
727
        size_t l;
×
728
        void *b;
×
729
        int r;
×
730

731
        if (sd_json_variant_is_null(variant)) {
×
732
                k->data = erase_and_free(k->data);
×
733
                k->size = 0;
×
734
                return 0;
×
735
        }
736

737
        r = sd_json_variant_unbase64(variant, &b, &l);
×
738
        if (r < 0)
×
739
                return json_log(variant, flags, r, "Failed to decode encrypted PKCS#11 key: %m");
×
740

741
        erase_and_free(k->data);
×
742
        k->data = b;
×
743
        k->size = l;
×
744

745
        return 0;
×
746
}
747

748
static int dispatch_pkcs11_key(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
749
        UserRecord *h = userdata;
×
750
        sd_json_variant *e;
×
751
        int r;
×
752

753
        if (!sd_json_variant_is_array(variant))
×
754
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
×
755

756
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
757
                static const sd_json_dispatch_field pkcs11_key_dispatch_table[] = {
×
758
                        { "uri",            SD_JSON_VARIANT_STRING, dispatch_pkcs11_uri,      offsetof(Pkcs11EncryptedKey, uri),             SD_JSON_MANDATORY },
759
                        { "data",           SD_JSON_VARIANT_STRING, dispatch_pkcs11_key_data, 0,                                             SD_JSON_MANDATORY },
760
                        { "hashedPassword", SD_JSON_VARIANT_STRING, sd_json_dispatch_string,  offsetof(Pkcs11EncryptedKey, hashed_password), SD_JSON_MANDATORY },
761
                        {},
762
                };
763

764
                if (!sd_json_variant_is_object(e))
×
765
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
×
766

767
                if (!GREEDY_REALLOC(h->pkcs11_encrypted_key, h->n_pkcs11_encrypted_key + 1))
×
768
                        return log_oom();
×
769

770
                Pkcs11EncryptedKey *k = h->pkcs11_encrypted_key + h->n_pkcs11_encrypted_key;
×
771
                *k = (Pkcs11EncryptedKey) {};
×
772

773
                r = sd_json_dispatch(e, pkcs11_key_dispatch_table, flags, k);
×
774
                if (r < 0) {
×
775
                        pkcs11_encrypted_key_done(k);
×
776
                        return r;
×
777
                }
778

779
                h->n_pkcs11_encrypted_key++;
×
780
        }
781

782
        return 0;
×
783
}
784

785
static int dispatch_fido2_hmac_credential(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
786
        Fido2HmacCredential *k = userdata;
×
787
        size_t l;
×
788
        void *b;
×
789
        int r;
×
790

791
        if (sd_json_variant_is_null(variant)) {
×
792
                k->id = mfree(k->id);
×
793
                k->size = 0;
×
794
                return 0;
×
795
        }
796

797
        r = sd_json_variant_unbase64(variant, &b, &l);
×
798
        if (r < 0)
×
799
                return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
×
800

801
        free_and_replace(k->id, b);
×
802
        k->size = l;
×
803

804
        return 0;
×
805
}
806

807
static int dispatch_fido2_hmac_credential_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
808
        UserRecord *h = userdata;
×
809
        sd_json_variant *e;
×
810
        int r;
×
811

812
        if (!sd_json_variant_is_array(variant))
×
813
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
814

815
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
816
                size_t l;
×
817
                void *b;
×
818

819
                if (!GREEDY_REALLOC(h->fido2_hmac_credential, h->n_fido2_hmac_credential + 1))
×
820
                        return log_oom();
×
821

822
                r = sd_json_variant_unbase64(e, &b, &l);
×
823
                if (r < 0)
×
824
                        return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
×
825

826
                h->fido2_hmac_credential[h->n_fido2_hmac_credential++] = (Fido2HmacCredential) {
×
827
                        .id = b,
828
                        .size = l,
829
                };
830
        }
831

832
        return 0;
×
833
}
834

835
static int dispatch_fido2_hmac_salt_value(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
836
        Fido2HmacSalt *k = userdata;
×
837
        size_t l;
×
838
        void *b;
×
839
        int r;
×
840

841
        if (sd_json_variant_is_null(variant)) {
×
842
                k->salt = erase_and_free(k->salt);
×
843
                k->salt_size = 0;
×
844
                return 0;
×
845
        }
846

847
        r = sd_json_variant_unbase64(variant, &b, &l);
×
848
        if (r < 0)
×
849
                return json_log(variant, flags, r, "Failed to decode FIDO2 salt: %m");
×
850

851
        erase_and_free(k->salt);
×
852
        k->salt = b;
×
853
        k->salt_size = l;
×
854

855
        return 0;
×
856
}
857

858
static int dispatch_fido2_hmac_salt(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
859
        UserRecord *h = userdata;
×
860
        sd_json_variant *e;
×
861
        int r;
×
862

863
        if (!sd_json_variant_is_array(variant))
×
864
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
×
865

866
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
867
                static const sd_json_dispatch_field fido2_hmac_salt_dispatch_table[] = {
×
868
                        { "credential",     SD_JSON_VARIANT_STRING,  dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential),      SD_JSON_MANDATORY },
869
                        { "salt",           SD_JSON_VARIANT_STRING,  dispatch_fido2_hmac_salt_value, 0,                                        SD_JSON_MANDATORY },
870
                        { "hashedPassword", SD_JSON_VARIANT_STRING,  sd_json_dispatch_string,        offsetof(Fido2HmacSalt, hashed_password), SD_JSON_MANDATORY },
871
                        { "up",             SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate,      offsetof(Fido2HmacSalt, up),              0                 },
872
                        { "uv",             SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate,      offsetof(Fido2HmacSalt, uv),              0                 },
873
                        { "clientPin",      SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate,      offsetof(Fido2HmacSalt, client_pin),      0                 },
874
                        {},
875
                };
876

877
                if (!sd_json_variant_is_object(e))
×
878
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
×
879

880
                if (!GREEDY_REALLOC(h->fido2_hmac_salt, h->n_fido2_hmac_salt + 1))
×
881
                        return log_oom();
×
882

883
                Fido2HmacSalt *k = h->fido2_hmac_salt + h->n_fido2_hmac_salt;
×
884
                *k = (Fido2HmacSalt) {
×
885
                        .uv = -1,
886
                        .up = -1,
887
                        .client_pin = -1,
888
                };
889

890
                r = sd_json_dispatch(e, fido2_hmac_salt_dispatch_table, flags, k);
×
891
                if (r < 0) {
×
892
                        fido2_hmac_salt_done(k);
×
893
                        return r;
×
894
                }
895

896
                h->n_fido2_hmac_salt++;
×
897
        }
898

899
        return 0;
×
900
}
901

902
static int dispatch_recovery_key(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
903
        UserRecord *h = userdata;
×
904
        sd_json_variant *e;
×
905
        int r;
×
906

907
        if (!sd_json_variant_is_array(variant))
×
908
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
×
909

910
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
911
                static const sd_json_dispatch_field recovery_key_dispatch_table[] = {
×
912
                        { "type",           SD_JSON_VARIANT_STRING, sd_json_dispatch_string, 0,                                      SD_JSON_MANDATORY },
913
                        { "hashedPassword", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(RecoveryKey, hashed_password), SD_JSON_MANDATORY },
914
                        {},
915
                };
916

917
                if (!sd_json_variant_is_object(e))
×
918
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
×
919

920
                if (!GREEDY_REALLOC(h->recovery_key, h->n_recovery_key + 1))
×
921
                        return log_oom();
×
922

923
                RecoveryKey *k = h->recovery_key + h->n_recovery_key;
×
924
                *k = (RecoveryKey) {};
×
925

926
                r = sd_json_dispatch(e, recovery_key_dispatch_table, flags, k);
×
927
                if (r < 0) {
×
928
                        recovery_key_done(k);
×
929
                        return r;
×
930
                }
931

932
                h->n_recovery_key++;
×
933
        }
934

935
        return 0;
×
936
}
937

938
static int dispatch_auto_resize_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
939
        AutoResizeMode *mode = userdata, m;
×
940

941
        assert_se(mode);
×
942

943
        if (sd_json_variant_is_null(variant)) {
×
944
                *mode = _AUTO_RESIZE_MODE_INVALID;
×
945
                return 0;
×
946
        }
947

948
        if (sd_json_variant_is_boolean(variant)) {
×
949
                *mode = sd_json_variant_boolean(variant) ? AUTO_RESIZE_SHRINK_AND_GROW : AUTO_RESIZE_OFF;
×
950
                return 0;
×
951
        }
952

953
        if (!sd_json_variant_is_string(variant))
×
954
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string, boolean or null.", strna(name));
×
955

956
        m = auto_resize_mode_from_string(sd_json_variant_string(variant));
×
957
        if (m < 0)
×
958
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid automatic resize mode.", strna(name));
×
959

960
        *mode = m;
×
961
        return 0;
×
962
}
963

964
static int dispatch_rebalance_weight(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
780✔
965
        uint64_t *rebalance_weight = userdata;
780✔
966
        uintmax_t u;
780✔
967

968
        assert_se(rebalance_weight);
780✔
969

970
        if (sd_json_variant_is_null(variant)) {
780✔
971
                *rebalance_weight = REBALANCE_WEIGHT_UNSET;
×
972
                return 0;
×
973
        }
974

975
        if (sd_json_variant_is_boolean(variant)) {
780✔
976
                *rebalance_weight = sd_json_variant_boolean(variant) ? REBALANCE_WEIGHT_DEFAULT : REBALANCE_WEIGHT_OFF;
×
977
                return 0;
×
978
        }
979

980
        if (!sd_json_variant_is_unsigned(variant))
780✔
981
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer, boolean or null.", strna(name));
×
982

983
        u = sd_json_variant_unsigned(variant);
780✔
984
        if (u >= REBALANCE_WEIGHT_MIN && u <= REBALANCE_WEIGHT_MAX)
780✔
985
                *rebalance_weight = (uint64_t) u;
×
986
        else if (u == 0)
780✔
987
                *rebalance_weight = REBALANCE_WEIGHT_OFF;
780✔
988
        else
989
                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
×
990
                                "Rebalance weight is out of valid range %" PRIu64 "%s%" PRIu64 ".",
991
                                REBALANCE_WEIGHT_MIN, glyph(GLYPH_ELLIPSIS), REBALANCE_WEIGHT_MAX);
992

993
        return 0;
994
}
995

996
static int dispatch_tmpfs_limit(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
438✔
997
        TmpfsLimit *limit = ASSERT_PTR(userdata);
438✔
998
        int r;
438✔
999

1000
        if (sd_json_variant_is_null(variant)) {
438✔
1001
                *limit = TMPFS_LIMIT_NULL;
×
1002
                return 0;
×
1003
        }
1004

1005
        r = sd_json_dispatch_uint64(name, variant, flags, &limit->limit);
438✔
1006
        if (r < 0)
438✔
1007
                return r;
1008

1009
        limit->is_set = true;
438✔
1010
        return 0;
438✔
1011
}
1012

1013
static int dispatch_tmpfs_limit_scale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
1014
        TmpfsLimit *limit = ASSERT_PTR(userdata);
×
1015
        int r;
×
1016

1017
        if (sd_json_variant_is_null(variant)) {
×
1018
                *limit = TMPFS_LIMIT_NULL;
×
1019
                return 0;
×
1020
        }
1021

1022
        r = sd_json_dispatch_uint32(name, variant, flags, &limit->limit_scale);
×
1023
        if (r < 0)
×
1024
                return r;
1025

1026
        limit->is_set = true;
×
1027
        return 0;
×
1028
}
1029

1030
static int dispatch_privileged(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
9,326✔
1031

1032
        static const sd_json_dispatch_field privileged_dispatch_table[] = {
9,326✔
1033
                { "passwordHint",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,  offsetof(UserRecord, password_hint),        0              },
1034
                { "hashedPassword",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,    offsetof(UserRecord, hashed_password),      SD_JSON_STRICT },
1035
                { "sshAuthorizedKeys",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,    offsetof(UserRecord, ssh_authorized_keys),  0              },
1036
                { "pkcs11EncryptedKey", SD_JSON_VARIANT_ARRAY,         dispatch_pkcs11_key,      0,                                          0              },
1037
                { "fido2HmacSalt",      SD_JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_salt, 0,                                          0              },
1038
                { "recoveryKey",        SD_JSON_VARIANT_ARRAY,         dispatch_recovery_key,    0,                                          0              },
1039
                {},
1040
        };
1041

1042
        return sd_json_dispatch(variant, privileged_dispatch_table, flags, userdata);
9,326✔
1043
}
1044

1045
static int dispatch_binding(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
14,549✔
1046

1047
        static const sd_json_dispatch_field binding_dispatch_table[] = {
14,549✔
1048
                { "blobDirectory",     SD_JSON_VARIANT_STRING,        json_dispatch_path,           offsetof(UserRecord, blob_directory),       SD_JSON_STRICT },
1049
                { "imagePath",         SD_JSON_VARIANT_STRING,        json_dispatch_image_path,     offsetof(UserRecord, image_path),           0              },
1050
                { "homeDirectory",     SD_JSON_VARIANT_STRING,        json_dispatch_home_directory, offsetof(UserRecord, home_directory),       0              },
1051
                { "partitionUuid",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,       offsetof(UserRecord, partition_uuid),       0              },
1052
                { "luksUuid",          SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,       offsetof(UserRecord, luks_uuid),            0              },
1053
                { "fileSystemUuid",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,       offsetof(UserRecord, file_system_uuid),     0              },
1054
                { "uid",               SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,     offsetof(UserRecord, uid),                  0              },
1055
                { "gid",               SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,     offsetof(UserRecord, gid),                  0              },
1056
                { "storage",           SD_JSON_VARIANT_STRING,        json_dispatch_user_storage,   offsetof(UserRecord, storage),              0              },
1057
                { "fileSystemType",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,      offsetof(UserRecord, file_system_type),     SD_JSON_STRICT },
1058
                { "luksCipher",        SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,      offsetof(UserRecord, luks_cipher),          SD_JSON_STRICT },
1059
                { "luksCipherMode",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,      offsetof(UserRecord, luks_cipher_mode),     SD_JSON_STRICT },
1060
                { "luksVolumeKeySize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,      offsetof(UserRecord, luks_volume_key_size), 0              },
1061
                {},
1062
        };
1063

1064
        sd_json_variant *m;
14,549✔
1065
        sd_id128_t mid;
14,549✔
1066
        int r;
14,549✔
1067

1068
        if (!variant)
14,549✔
1069
                return 0;
14,549✔
1070

1071
        if (!sd_json_variant_is_object(variant))
1,680✔
1072
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
1073

1074
        r = sd_id128_get_machine(&mid);
1,680✔
1075
        if (r < 0)
1,680✔
1076
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
1077

1078
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
1,680✔
1079
        if (!m)
1,680✔
1080
                return 0;
1081

1082
        return sd_json_dispatch(m, binding_dispatch_table, flags, userdata);
1,680✔
1083
}
1084

1085
static int dispatch_blob_manifest(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
166✔
1086
        _cleanup_hashmap_free_ Hashmap *manifest = NULL;
166✔
1087
        Hashmap **ret = ASSERT_PTR(userdata);
166✔
1088
        sd_json_variant *value;
166✔
1089
        const char *key;
166✔
1090
        int r;
166✔
1091

1092
        if (!variant)
166✔
1093
                return 0;
1094

1095
        if (!sd_json_variant_is_object(variant))
166✔
1096
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
1097

1098
        JSON_VARIANT_OBJECT_FOREACH(key, value, variant) {
528✔
1099
                _cleanup_free_ char *filename = NULL;
×
1100
                _cleanup_free_ uint8_t *hash = NULL;
362✔
1101

1102
                if (!sd_json_variant_is_string(value))
362✔
1103
                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Blob entry '%s' has invalid hash.", key);
×
1104

1105
                if (!suitable_blob_filename(key))
362✔
1106
                        return json_log(value, flags, SYNTHETIC_ERRNO(EINVAL), "Blob entry '%s' has invalid filename.", key);
×
1107

1108
                filename = strdup(key);
362✔
1109
                if (!filename)
362✔
1110
                        return json_log_oom(value, flags);
×
1111

1112
                hash = malloc(SHA256_DIGEST_SIZE);
362✔
1113
                if (!hash)
362✔
1114
                        return json_log_oom(value, flags);
×
1115

1116
                r = parse_sha256(sd_json_variant_string(value), hash);
362✔
1117
                if (r < 0)
362✔
1118
                        return json_log(value, flags, r, "Blob entry '%s' has invalid hash: %s", filename, sd_json_variant_string(value));
×
1119

1120
                r = hashmap_ensure_put(&manifest, &path_hash_ops_free_free, filename, hash);
362✔
1121
                if (r < 0)
362✔
1122
                        return json_log(value, flags, r, "Failed to insert blob manifest entry '%s': %m", filename);
×
1123
                TAKE_PTR(filename); /* Ownership transfers to hashmap */
362✔
1124
                TAKE_PTR(hash);
362✔
1125
        }
1126

1127
        hashmap_free_and_replace(*ret, manifest);
166✔
1128
        return 0;
166✔
1129
}
1130

1131
int per_machine_id_match(sd_json_variant *ids, sd_json_dispatch_flags_t flags) {
1,938✔
1132
        sd_id128_t mid;
1,938✔
1133
        int r;
1,938✔
1134

1135
        assert(ids);
1,938✔
1136

1137
        r = sd_id128_get_machine(&mid);
1,938✔
1138
        if (r < 0)
1,938✔
1139
                return json_log(ids, flags, r, "Failed to acquire machine ID: %m");
×
1140

1141
        if (sd_json_variant_is_string(ids)) {
1,938✔
1142
                sd_id128_t k;
1,938✔
1143

1144
                r = sd_id128_from_string(sd_json_variant_string(ids), &k);
1,938✔
1145
                if (r < 0) {
1,938✔
1146
                        json_log(ids, flags, r, "%s is not a valid machine ID, ignoring: %m", sd_json_variant_string(ids));
×
1147
                        return 0;
1,938✔
1148
                }
1149

1150
                return sd_id128_equal(mid, k);
3,876✔
1151
        }
1152

1153
        if (sd_json_variant_is_array(ids)) {
×
1154
                sd_json_variant *e;
×
1155

1156
                JSON_VARIANT_ARRAY_FOREACH(e, ids) {
×
1157
                        sd_id128_t k;
×
1158

1159
                        if (!sd_json_variant_is_string(e)) {
×
1160
                                json_log(e, flags, 0, "Machine ID is not a string, ignoring: %m");
×
1161
                                continue;
×
1162
                        }
1163

1164
                        r = sd_id128_from_string(sd_json_variant_string(e), &k);
×
1165
                        if (r < 0) {
×
1166
                                json_log(e, flags, r, "%s is not a valid machine ID, ignoring: %m", sd_json_variant_string(e));
×
1167
                                continue;
×
1168
                        }
1169

1170
                        if (sd_id128_equal(mid, k))
×
1171
                                return true;
×
1172
                }
1173

1174
                return false;
×
1175
        }
1176

1177
        json_log(ids, flags, 0, "Machine ID is not a string or array of strings, ignoring: %m");
×
1178
        return false;
1179
}
1180

1181
int per_machine_hostname_match(sd_json_variant *hns, sd_json_dispatch_flags_t flags) {
×
1182
        _cleanup_free_ char *hn = NULL;
×
1183
        int r;
×
1184

1185
        assert(hns);
×
1186

1187
        r = gethostname_strict(&hn);
×
1188
        if (r == -ENXIO) {
×
1189
                json_log(hns, flags, r, "No hostname set, not matching perMachine hostname record: %m");
×
1190
                return false;
×
1191
        }
1192
        if (r < 0)
×
1193
                return json_log(hns, flags, r, "Failed to acquire hostname: %m");
×
1194

1195
        if (sd_json_variant_is_string(hns))
×
1196
                return streq(sd_json_variant_string(hns), hn);
×
1197

1198
        if (sd_json_variant_is_array(hns)) {
×
1199
                sd_json_variant *e;
×
1200

1201
                JSON_VARIANT_ARRAY_FOREACH(e, hns) {
×
1202

1203
                        if (!sd_json_variant_is_string(e)) {
×
1204
                                json_log(e, flags, 0, "Hostname is not a string, ignoring: %m");
×
1205
                                continue;
×
1206
                        }
1207

1208
                        if (streq(sd_json_variant_string(hns), hn))
×
1209
                                return true;
×
1210
                }
1211

1212
                return false;
×
1213
        }
1214

1215
        json_log(hns, flags, 0, "Hostname is not a string or array of strings, ignoring: %m");
×
1216
        return false;
1217
}
1218

1219
int per_machine_match(sd_json_variant *entry, sd_json_dispatch_flags_t flags) {
1,938✔
1220
        sd_json_variant *m;
1,938✔
1221
        int r;
1,938✔
1222

1223
        assert(sd_json_variant_is_object(entry));
1,938✔
1224

1225
        m = sd_json_variant_by_key(entry, "matchMachineId");
1,938✔
1226
        if (m) {
1,938✔
1227
                r = per_machine_id_match(m, flags);
1,856✔
1228
                if (r < 0)
1,856✔
1229
                        return r;
1230
                if (r > 0)
1,856✔
1231
                        return true;
1232
        }
1233

1234
        m = sd_json_variant_by_key(entry, "matchNotMachineId");
82✔
1235
        if (m) {
82✔
1236
                r = per_machine_id_match(m, flags);
82✔
1237
                if (r < 0)
82✔
1238
                        return r;
1239
                if (r == 0)
82✔
1240
                        return true;
1241
        }
1242

1243
        m = sd_json_variant_by_key(entry, "matchHostname");
82✔
1244
        if (m) {
82✔
1245
                r = per_machine_hostname_match(m, flags);
×
1246
                if (r < 0)
×
1247
                        return r;
1248
                if (r > 0)
×
1249
                        return true;
1250
        }
1251

1252
        m = sd_json_variant_by_key(entry, "matchNotHostname");
82✔
1253
        if (m) {
82✔
1254
                r = per_machine_hostname_match(m, flags);
×
1255
                if (r < 0)
×
1256
                        return r;
1257
                if (r == 0)
×
1258
                        return true;
×
1259
        }
1260

1261
        return false;
1262
}
1263

1264
static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
14,549✔
1265

1266
        static const sd_json_dispatch_field per_machine_dispatch_table[] = {
14,549✔
1267
                { "matchMachineId",             _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1268
                { "matchNotMachineId",          _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1269
                { "matchHostname",              _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1270
                { "matchNotHostname",           _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1271
                { "blobDirectory",              SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, blob_directory),                SD_JSON_STRICT },
1272
                { "blobManifest",               SD_JSON_VARIANT_OBJECT,        dispatch_blob_manifest,               offsetof(UserRecord, blob_manifest),                 0              },
1273
                { "iconName",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, icon_name),                     SD_JSON_STRICT },
1274
                { "location",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, location),                      0              },
1275
                { "shell",                      SD_JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0              },
1276
                { "umask",                      SD_JSON_VARIANT_UNSIGNED,      json_dispatch_umask,                  offsetof(UserRecord, umask),                         0              },
1277
                { "environment",                SD_JSON_VARIANT_ARRAY,         json_dispatch_strv_environment,       offsetof(UserRecord, environment),                   0              },
1278
                { "timeZone",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, time_zone),                     SD_JSON_STRICT },
1279
                { "preferredLanguage",          SD_JSON_VARIANT_STRING,        json_dispatch_locale,                 offsetof(UserRecord, preferred_language),            0              },
1280
                { "additionalLanguages",        SD_JSON_VARIANT_ARRAY,         json_dispatch_locales,                offsetof(UserRecord, additional_languages),          0              },
1281
                { "niceLevel",                  _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0              },
1282
                { "resourceLimits",             _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0              },
1283
                { "locked",                     SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, locked),                        0              },
1284
                { "notBeforeUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0              },
1285
                { "notAfterUSec",               _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0              },
1286
                { "storage",                    SD_JSON_VARIANT_STRING,        json_dispatch_user_storage,           offsetof(UserRecord, storage),                       0              },
1287
                { "diskSize",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size),                     0              },
1288
                { "diskSizeRelative",           _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0              },
1289
                { "skeletonDirectory",          SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            SD_JSON_STRICT },
1290
                { "accessMode",                 SD_JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0              },
1291
                { "tasksMax",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0              },
1292
                { "memoryHigh",                 SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0              },
1293
                { "memoryMax",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0              },
1294
                { "cpuWeight",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0              },
1295
                { "ioWeight",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0              },
1296
                { "mountNoDevices",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nodev),                         0              },
1297
                { "mountNoSuid",                SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nosuid),                        0              },
1298
                { "mountNoExecute",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, noexec),                        0              },
1299
                { "cifsDomain",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   SD_JSON_STRICT },
1300
                { "cifsUserName",               SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                SD_JSON_STRICT },
1301
                { "cifsService",                SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_service),                  SD_JSON_STRICT },
1302
                { "cifsExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_extra_mount_options),      0              },
1303
                { "imagePath",                  SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    SD_JSON_STRICT },
1304
                { "uid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0              },
1305
                { "gid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0              },
1306
                { "memberOf",                   SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     SD_JSON_RELAX  },
1307
                { "capabilityBoundingSet",      SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_bounding_set),       SD_JSON_STRICT },
1308
                { "capabilityAmbientSet",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_ambient_set),        SD_JSON_STRICT },
1309
                { "fileSystemType",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, file_system_type),              SD_JSON_STRICT },
1310
                { "partitionUuid",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0              },
1311
                { "luksUuid",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0              },
1312
                { "fileSystemUuid",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0              },
1313
                { "luksDiscard",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0,             },
1314
                { "luksOfflineDiscard",         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_offline_discard),          0,             },
1315
                { "luksCipher",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   SD_JSON_STRICT },
1316
                { "luksCipherMode",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              SD_JSON_STRICT },
1317
                { "luksVolumeKeySize",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0              },
1318
                { "luksPbkdfHashAlgorithm",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     SD_JSON_STRICT },
1319
                { "luksPbkdfType",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               SD_JSON_STRICT },
1320
                { "luksPbkdfForceIterations",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_force_iterations),   0              },
1321
                { "luksPbkdfTimeCostUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0              },
1322
                { "luksPbkdfMemoryCost",        _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0              },
1323
                { "luksPbkdfParallelThreads",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0              },
1324
                { "luksSectorSize",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_sector_size),              0              },
1325
                { "luksExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_extra_mount_options),      0              },
1326
                { "dropCaches",                 SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, drop_caches),                   0              },
1327
                { "autoResizeMode",             _SD_JSON_VARIANT_TYPE_INVALID, dispatch_auto_resize_mode,            offsetof(UserRecord, auto_resize_mode),              0              },
1328
                { "rebalanceWeight",            _SD_JSON_VARIANT_TYPE_INVALID, dispatch_rebalance_weight,            offsetof(UserRecord, rebalance_weight),              0              },
1329
                { "rateLimitIntervalUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0              },
1330
                { "rateLimitBurst",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0              },
1331
                { "enforcePasswordPolicy",      SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0              },
1332
                { "autoLogin",                  SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0              },
1333
                { "preferredSessionType",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_type),        SD_JSON_STRICT },
1334
                { "preferredSessionLauncher",   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_launcher),    SD_JSON_STRICT },
1335
                { "stopDelayUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0              },
1336
                { "killProcesses",              SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0              },
1337
                { "passwordChangeMinUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0              },
1338
                { "passwordChangeMaxUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0              },
1339
                { "passwordChangeWarnUSec",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0              },
1340
                { "passwordChangeInactiveUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0              },
1341
                { "passwordChangeNow",          SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0              },
1342
                { "pkcs11TokenUri",             SD_JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0              },
1343
                { "fido2HmacCredential",        SD_JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0              },
1344
                { "selfModifiableFields",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_fields),        SD_JSON_STRICT },
1345
                { "selfModifiableBlobs",        SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_blobs),         SD_JSON_STRICT },
1346
                { "selfModifiablePrivileged",   SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_privileged),    SD_JSON_STRICT },
1347
                { "tmpLimit",                   _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, tmp_limit),                     0,             },
1348
                { "tmpLimitScale",              _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, tmp_limit),                     0,             },
1349
                { "devShmLimit",                _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, dev_shm_limit),                 0,             },
1350
                { "devShmLimitScale",           _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, dev_shm_limit),                 0,             },
1351
                { "defaultArea",                SD_JSON_VARIANT_STRING,        json_dispatch_filename,               offsetof(UserRecord, default_area),                  0              },
1352
                {},
1353
        };
1354

1355
        sd_json_variant *e;
14,549✔
1356
        int r;
14,549✔
1357

1358
        if (!variant)
14,549✔
1359
                return 0;
1360

1361
        if (!sd_json_variant_is_array(variant))
1,828✔
1362
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
×
1363

1364
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
3,731✔
1365
                if (!sd_json_variant_is_object(e))
1,903✔
1366
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
×
1367

1368
                r = per_machine_match(e, flags);
1,903✔
1369
                if (r < 0)
1,903✔
1370
                        return r;
1371
                if (r == 0)
1,903✔
1372
                        continue;
75✔
1373

1374
                r = sd_json_dispatch(e, per_machine_dispatch_table, flags, userdata);
1,828✔
1375
                if (r < 0)
1,828✔
1376
                        return r;
1377
        }
1378

1379
        return 0;
1,828✔
1380
}
1381

1382
static int dispatch_status(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
14,549✔
1383

1384
        static const sd_json_dispatch_field status_dispatch_table[] = {
14,549✔
1385
                { "diskUsage",                  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_usage),                    0              },
1386
                { "diskFree",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_free),                     0              },
1387
                { "diskSize",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_size),                     0              },
1388
                { "diskCeiling",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_ceiling),                  0              },
1389
                { "diskFloor",                  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_floor),                    0              },
1390
                { "state",                      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(UserRecord, state),                         SD_JSON_STRICT },
1391
                { "service",                    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(UserRecord, service),                       SD_JSON_STRICT },
1392
                { "signedLocally",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,      offsetof(UserRecord, signed_locally),                0              },
1393
                { "goodAuthenticationCounter",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, good_authentication_counter),   0              },
1394
                { "badAuthenticationCounter",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, bad_authentication_counter),    0              },
1395
                { "lastGoodAuthenticationUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, last_good_authentication_usec), 0              },
1396
                { "lastBadAuthenticationUSec",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, last_bad_authentication_usec),  0              },
1397
                { "rateLimitBeginUSec",         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, ratelimit_begin_usec),          0              },
1398
                { "rateLimitCount",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, ratelimit_count),               0              },
1399
                { "removable",                  SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,      offsetof(UserRecord, removable),                     0              },
1400
                { "accessMode",                 SD_JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,      offsetof(UserRecord, access_mode),                   0              },
1401
                { "fileSystemType",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(UserRecord, file_system_type),              SD_JSON_STRICT },
1402
                { "fallbackShell",              SD_JSON_VARIANT_STRING,        json_dispatch_filename_or_path, offsetof(UserRecord, fallback_shell),                0              },
1403
                { "fallbackHomeDirectory",      SD_JSON_VARIANT_STRING,        json_dispatch_home_directory,   offsetof(UserRecord, fallback_home_directory),       0              },
1404
                { "useFallback",                SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,       offsetof(UserRecord, use_fallback),                  0              },
1405
                { "defaultArea",                SD_JSON_VARIANT_STRING,        json_dispatch_filename,         offsetof(UserRecord, default_area),                  0              },
1406
                { "aliases",                    SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(UserRecord, aliases),                       SD_JSON_RELAX  },
1407
                {},
1408
        };
1409

1410
        sd_json_variant *m;
14,549✔
1411
        sd_id128_t mid;
14,549✔
1412
        int r;
14,549✔
1413

1414
        if (!variant)
14,549✔
1415
                return 0;
14,549✔
1416

1417
        if (!sd_json_variant_is_object(variant))
4,555✔
1418
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
1419

1420
        r = sd_id128_get_machine(&mid);
4,555✔
1421
        if (r < 0)
4,555✔
1422
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
1423

1424
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
4,555✔
1425
        if (!m)
4,555✔
1426
                return 0;
1427

1428
        return sd_json_dispatch(m, status_dispatch_table, flags, userdata);
4,555✔
1429
}
1430

1431
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret) {
7,949✔
1432
        const char *suffix;
7,949✔
1433
        char *z;
7,949✔
1434

1435
        assert(storage >= 0);
7,949✔
1436
        assert(user_name_and_realm);
7,949✔
1437
        assert(ret);
7,949✔
1438

1439
        if (storage == USER_LUKS)
7,949✔
1440
                suffix = ".home";
1441
        else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
7,949✔
1442
                suffix = ".homedir";
1443
        else {
1444
                *ret = NULL;
7,811✔
1445
                return 0;
7,811✔
1446
        }
1447

1448
        z = strjoin(get_home_root(), "/", user_name_and_realm, suffix);
138✔
1449
        if (!z)
138✔
1450
                return -ENOMEM;
1451

1452
        *ret = path_simplify(z);
138✔
1453
        return 1;
138✔
1454
}
1455

1456
static int user_record_augment(UserRecord *h, sd_json_dispatch_flags_t json_flags) {
14,549✔
1457
        int r;
14,549✔
1458

1459
        assert(h);
14,549✔
1460

1461
        if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
14,549✔
1462
                return 0;
1463

1464
        assert(h->user_name);
14,366✔
1465

1466
        if (!h->user_name_and_realm_auto && h->realm) {
14,366✔
1467
                h->user_name_and_realm_auto = strjoin(h->user_name, "@", h->realm);
58✔
1468
                if (!h->user_name_and_realm_auto)
58✔
1469
                        return json_log_oom(h->json, json_flags);
×
1470
        }
1471

1472
        /* Let's add in the following automatisms only for regular users, they don't make sense for any others */
1473
        if (user_record_disposition(h) != USER_REGULAR)
14,366✔
1474
                return 0;
1475

1476
        if (!h->home_directory && !h->home_directory_auto) {
9,702✔
1477
                h->home_directory_auto = path_join(get_home_root(), h->user_name);
8,069✔
1478
                if (!h->home_directory_auto)
8,069✔
1479
                        return json_log_oom(h->json, json_flags);
×
1480
        }
1481

1482
        if (!h->image_path && !h->image_path_auto) {
9,702✔
1483
                r = user_record_build_image_path(user_record_storage(h), user_record_user_name_and_realm(h), &h->image_path_auto);
7,947✔
1484
                if (r < 0)
7,947✔
1485
                        return json_log(h->json, json_flags, r, "Failed to determine default image path: %m");
×
1486
        }
1487

1488
        return 0;
1489
}
1490

1491
int user_group_record_mangle(
23,396✔
1492
                sd_json_variant *v,
1493
                UserRecordLoadFlags load_flags,
1494
                sd_json_variant **ret_variant,
1495
                UserRecordMask *ret_mask) {
1496

1497
        static const struct {
23,396✔
1498
                UserRecordMask mask;
1499
                const char *name;
1500
        } mask_field[] = {
1501
                { USER_RECORD_PRIVILEGED,  "privileged" },
1502
                { USER_RECORD_SECRET,      "secret"     },
1503
                { USER_RECORD_BINDING,     "binding"    },
1504
                { USER_RECORD_PER_MACHINE, "perMachine" },
1505
                { USER_RECORD_STATUS,      "status"     },
1506
                { USER_RECORD_SIGNATURE,   "signature"  },
1507
        };
1508

1509
        sd_json_dispatch_flags_t json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
23,396✔
1510
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL;
23,396✔
1511
        sd_json_variant *array[ELEMENTSOF(mask_field) * 2];
23,396✔
1512
        size_t n_retain = 0;
23,396✔
1513
        UserRecordMask m = 0;
23,396✔
1514
        int r;
23,396✔
1515

1516
        assert((load_flags & _USER_RECORD_MASK_MAX) == 0); /* detect mistakes when accidentally passing
23,396✔
1517
                                                            * UserRecordMask bit masks as UserRecordLoadFlags
1518
                                                            * value */
1519

1520
        assert(v);
23,396✔
1521
        assert(ret_variant);
23,396✔
1522

1523
        /* Note that this function is shared with the group record parser, hence we try to be generic in our
1524
         * log message wording here, to cover both cases. */
1525

1526
        if (!sd_json_variant_is_object(v))
23,396✔
1527
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is not a JSON object, refusing.");
×
1528

1529
        if (USER_RECORD_ALLOW_MASK(load_flags) == 0) /* allow nothing? */
23,396✔
1530
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Nothing allowed in record, refusing.");
×
1531

1532
        if (USER_RECORD_STRIP_MASK(load_flags) == _USER_RECORD_MASK_MAX) /* strip everything? */
23,396✔
1533
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Stripping everything from record, refusing.");
×
1534

1535
        /* Extra safety: mark the "secret" part (that contains literal passwords and such) as sensitive, so
1536
         * that it is not included in debug output and erased from memory when we are done. We do this for
1537
         * any record that passes through here. */
1538
        sd_json_variant_sensitive(sd_json_variant_by_key(v, "secret"));
23,396✔
1539

1540
        /* Check if we have the special sections and if they match our flags set */
1541
        FOREACH_ELEMENT(i, mask_field) {
163,772✔
1542
                sd_json_variant *e, *k;
140,376✔
1543

1544
                if (FLAGS_SET(USER_RECORD_STRIP_MASK(load_flags), i->mask)) {
140,376✔
1545
                        if (!w)
9,555✔
1546
                                w = sd_json_variant_ref(v);
7,853✔
1547

1548
                        r = sd_json_variant_filter(&w, STRV_MAKE(i->name));
9,555✔
1549
                        if (r < 0)
9,555✔
1550
                                return json_log(w, json_flags, r, "Failed to remove field from variant: %m");
×
1551

1552
                        continue;
9,555✔
1553
                }
1554

1555
                e = sd_json_variant_by_key_full(v, i->name, &k);
130,821✔
1556
                if (e) {
130,821✔
1557
                        if (!FLAGS_SET(USER_RECORD_ALLOW_MASK(load_flags), i->mask))
22,812✔
1558
                                return json_log(e, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", i->name);
×
1559

1560
                        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
22,812✔
1561
                                array[n_retain++] = k;
148✔
1562
                                array[n_retain++] = e;
148✔
1563
                        }
1564

1565
                        m |= i->mask;
22,812✔
1566
                } else {
1567
                        if (FLAGS_SET(USER_RECORD_REQUIRE_MASK(load_flags), i->mask))
108,009✔
1568
                                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks '%s' field, which is required.", i->name);
×
1569
                }
1570
        }
1571

1572
        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
23,396✔
1573
                /* If we are supposed to strip regular items, then let's instead just allocate a new object
1574
                 * with just the stuff we need. */
1575

1576
                w = sd_json_variant_unref(w);
284✔
1577
                r = sd_json_variant_new_object(&w, array, n_retain);
284✔
1578
                if (r < 0)
284✔
1579
                        return json_log(v, json_flags, r, "Failed to allocate new object: %m");
×
1580
        } else
1581
                /* And now check if there's anything else in the record */
1582
                for (size_t i = 0; i < sd_json_variant_elements(v); i += 2) {
23,147✔
1583
                        const char *f;
23,112✔
1584
                        bool special = false;
23,112✔
1585

1586
                        assert_se(f = sd_json_variant_string(sd_json_variant_by_index(v, i)));
23,112✔
1587

1588
                        FOREACH_ELEMENT(j, mask_field)
161,784✔
1589
                                if (streq(f, j->name)) { /* already covered in the loop above */
138,672✔
1590
                                        special = true;
35✔
1591
                                        continue;
35✔
1592
                                }
1593

1594
                        if (!special) {
23,112✔
1595
                                if ((load_flags & (USER_RECORD_ALLOW_REGULAR|USER_RECORD_REQUIRE_REGULAR)) == 0)
23,077✔
1596
                                        return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", f);
×
1597

1598
                                m |= USER_RECORD_REGULAR;
23,077✔
1599
                                break;
23,077✔
1600
                        }
1601
                }
1602

1603
        if (FLAGS_SET(load_flags, USER_RECORD_REQUIRE_REGULAR) && !FLAGS_SET(m, USER_RECORD_REGULAR))
23,396✔
1604
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks basic identity fields, which are required.");
×
1605

1606
        if (!FLAGS_SET(load_flags, USER_RECORD_EMPTY_OK) && m == 0)
23,396✔
1607
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is empty.");
×
1608

1609
        if (w)
23,396✔
1610
                *ret_variant = TAKE_PTR(w);
7,853✔
1611
        else
1612
                *ret_variant = sd_json_variant_ref(v);
15,543✔
1613

1614
        if (ret_mask)
23,396✔
1615
                *ret_mask = m;
23,352✔
1616
        return 0;
1617
}
1618

1619
int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load_flags) {
14,549✔
1620

1621
        static const sd_json_dispatch_field user_dispatch_table[] = {
14,549✔
1622
                { "userName",                   SD_JSON_VARIANT_STRING,        json_dispatch_user_group_name,        offsetof(UserRecord, user_name),                     SD_JSON_RELAX  },
1623
                { "aliases",                    SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, aliases),                       SD_JSON_RELAX  },
1624
                { "realm",                      SD_JSON_VARIANT_STRING,        json_dispatch_realm,                  offsetof(UserRecord, realm),                         0              },
1625
                { "uuid",                       SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, uuid),                          0              },
1626
                { "blobDirectory",              SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, blob_directory),                SD_JSON_STRICT },
1627
                { "blobManifest",               SD_JSON_VARIANT_OBJECT,        dispatch_blob_manifest,               offsetof(UserRecord, blob_manifest),                 0              },
1628
                { "realName",                   SD_JSON_VARIANT_STRING,        json_dispatch_gecos,                  offsetof(UserRecord, real_name),                     0              },
1629
                { "emailAddress",               SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, email_address),                 SD_JSON_STRICT },
1630
                { "iconName",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, icon_name),                     SD_JSON_STRICT },
1631
                { "location",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, location),                      0              },
1632
                { "disposition",                SD_JSON_VARIANT_STRING,        json_dispatch_user_disposition,       offsetof(UserRecord, disposition),                   0              },
1633
                { "lastChangeUSec",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, last_change_usec),              0              },
1634
                { "lastPasswordChangeUSec",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, last_password_change_usec),     0              },
1635
                { "shell",                      SD_JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0              },
1636
                { "umask",                      SD_JSON_VARIANT_UNSIGNED,      json_dispatch_umask,                  offsetof(UserRecord, umask),                         0              },
1637
                { "environment",                SD_JSON_VARIANT_ARRAY,         json_dispatch_strv_environment,       offsetof(UserRecord, environment),                   0              },
1638
                { "timeZone",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, time_zone),                     SD_JSON_STRICT },
1639
                { "preferredLanguage",          SD_JSON_VARIANT_STRING,        json_dispatch_locale,                 offsetof(UserRecord, preferred_language),            0              },
1640
                { "additionalLanguages",        SD_JSON_VARIANT_ARRAY,         json_dispatch_locales,                offsetof(UserRecord, additional_languages),          0              },
1641
                { "niceLevel",                  _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0              },
1642
                { "resourceLimits",             _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0              },
1643
                { "locked",                     SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, locked),                        0              },
1644
                { "notBeforeUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0              },
1645
                { "notAfterUSec",               _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0              },
1646
                { "storage",                    SD_JSON_VARIANT_STRING,        json_dispatch_user_storage,           offsetof(UserRecord, storage),                       0              },
1647
                { "diskSize",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size),                     0              },
1648
                { "diskSizeRelative",           _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0              },
1649
                { "skeletonDirectory",          SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            SD_JSON_STRICT },
1650
                { "accessMode",                 SD_JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0              },
1651
                { "tasksMax",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0              },
1652
                { "memoryHigh",                 SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0              },
1653
                { "memoryMax",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0              },
1654
                { "cpuWeight",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0              },
1655
                { "ioWeight",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0              },
1656
                { "mountNoDevices",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nodev),                         0              },
1657
                { "mountNoSuid",                SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nosuid),                        0              },
1658
                { "mountNoExecute",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, noexec),                        0              },
1659
                { "cifsDomain",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   SD_JSON_STRICT },
1660
                { "cifsUserName",               SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                SD_JSON_STRICT },
1661
                { "cifsService",                SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_service),                  SD_JSON_STRICT },
1662
                { "cifsExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_extra_mount_options),      0              },
1663
                { "imagePath",                  SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    SD_JSON_STRICT },
1664
                { "homeDirectory",              SD_JSON_VARIANT_STRING,        json_dispatch_home_directory,         offsetof(UserRecord, home_directory),                0              },
1665
                { "uid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0              },
1666
                { "gid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0              },
1667
                { "memberOf",                   SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     SD_JSON_RELAX  },
1668
                { "capabilityBoundingSet",      SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_bounding_set),       SD_JSON_STRICT },
1669
                { "capabilityAmbientSet",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_ambient_set),        SD_JSON_STRICT },
1670
                { "fileSystemType",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, file_system_type),              SD_JSON_STRICT },
1671
                { "partitionUuid",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0              },
1672
                { "luksUuid",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0              },
1673
                { "fileSystemUuid",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0              },
1674
                { "luksDiscard",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0              },
1675
                { "luksOfflineDiscard",         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_offline_discard),          0              },
1676
                { "luksCipher",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   SD_JSON_STRICT },
1677
                { "luksCipherMode",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              SD_JSON_STRICT },
1678
                { "luksVolumeKeySize",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0              },
1679
                { "luksPbkdfHashAlgorithm",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     SD_JSON_STRICT },
1680
                { "luksPbkdfType",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               SD_JSON_STRICT },
1681
                { "luksPbkdfForceIterations",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_force_iterations),   0              },
1682
                { "luksPbkdfTimeCostUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0              },
1683
                { "luksPbkdfMemoryCost",        _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0              },
1684
                { "luksPbkdfParallelThreads",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0              },
1685
                { "luksSectorSize",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_sector_size),              0              },
1686
                { "luksExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_extra_mount_options),      0              },
1687
                { "dropCaches",                 SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, drop_caches),                   0              },
1688
                { "autoResizeMode",             _SD_JSON_VARIANT_TYPE_INVALID, dispatch_auto_resize_mode,            offsetof(UserRecord, auto_resize_mode),              0              },
1689
                { "rebalanceWeight",            _SD_JSON_VARIANT_TYPE_INVALID, dispatch_rebalance_weight,            offsetof(UserRecord, rebalance_weight),              0              },
1690
                { "service",                    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, service),                       SD_JSON_STRICT },
1691
                { "rateLimitIntervalUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0              },
1692
                { "rateLimitBurst",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0              },
1693
                { "enforcePasswordPolicy",      SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0              },
1694
                { "autoLogin",                  SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0              },
1695
                { "preferredSessionType",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_type),        SD_JSON_STRICT },
1696
                { "preferredSessionLauncher",   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_launcher),    SD_JSON_STRICT },
1697
                { "stopDelayUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0              },
1698
                { "killProcesses",              SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0              },
1699
                { "passwordChangeMinUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0              },
1700
                { "passwordChangeMaxUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0              },
1701
                { "passwordChangeWarnUSec",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0              },
1702
                { "passwordChangeInactiveUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0              },
1703
                { "passwordChangeNow",          SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0              },
1704
                { "pkcs11TokenUri",             SD_JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0              },
1705
                { "fido2HmacCredential",        SD_JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0              },
1706
                { "recoveryKeyType",            SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, recovery_key_type),             0              },
1707
                { "selfModifiableFields",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_fields),        SD_JSON_STRICT },
1708
                { "selfModifiableBlobs",        SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_blobs),         SD_JSON_STRICT },
1709
                { "selfModifiablePrivileged",   SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_privileged),    SD_JSON_STRICT },
1710
                { "tmpLimit",                   _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, tmp_limit),                     0,             },
1711
                { "tmpLimitScale",              _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, tmp_limit),                     0,             },
1712
                { "devShmLimit",                _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, dev_shm_limit),                 0,             },
1713
                { "devShmLimitScale",           _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, dev_shm_limit),                 0,             },
1714
                { "defaultArea",                SD_JSON_VARIANT_STRING,        json_dispatch_filename,               offsetof(UserRecord, default_area),                  0              },
1715

1716
                { "secret",                     SD_JSON_VARIANT_OBJECT,        dispatch_secret,                      0,                                                   0              },
1717
                { "privileged",                 SD_JSON_VARIANT_OBJECT,        dispatch_privileged,                  0,                                                   0              },
1718

1719
                /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */
1720
                { "perMachine",                 SD_JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0              },
1721
                { "binding",                    SD_JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0              },
1722
                { "status",                     SD_JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0              },
1723

1724
                /* Ignore 'signature', we check it with explicit accessors instead */
1725
                { "signature",                  SD_JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0              },
1726
                {},
1727
        };
1728

1729
        sd_json_dispatch_flags_t json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
14,549✔
1730
        int r;
14,549✔
1731

1732
        assert(h);
14,549✔
1733
        assert(!h->json);
14,549✔
1734

1735
        /* Note that this call will leave a half-initialized record around on failure! */
1736

1737
        r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
14,549✔
1738
        if (r < 0)
14,549✔
1739
                return r;
1740

1741
        r = sd_json_dispatch(h->json, user_dispatch_table, json_flags | SD_JSON_ALLOW_EXTENSIONS, h);
14,549✔
1742
        if (r < 0)
14,549✔
1743
                return r;
1744

1745
        /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields,
1746
         * since we want them to override the global options. Let's process them now. */
1747

1748
        r = dispatch_per_machine("perMachine", sd_json_variant_by_key(h->json, "perMachine"), json_flags, h);
14,549✔
1749
        if (r < 0)
14,549✔
1750
                return r;
1751

1752
        r = dispatch_binding("binding", sd_json_variant_by_key(h->json, "binding"), json_flags, h);
14,549✔
1753
        if (r < 0)
14,549✔
1754
                return r;
1755

1756
        r = dispatch_status("status", sd_json_variant_by_key(h->json, "status"), json_flags, h);
14,549✔
1757
        if (r < 0)
14,549✔
1758
                return r;
1759

1760
        if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->user_name)
14,549✔
1761
                return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "User name field missing, refusing.");
×
1762

1763
        r = user_record_augment(h, json_flags);
14,549✔
1764
        if (r < 0)
14,549✔
1765
                return r;
×
1766

1767
        return 0;
1768
}
1769

1770
int user_record_build(UserRecord **ret, ...) {
3,003✔
1771
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
3,003✔
1772
        _cleanup_(user_record_unrefp) UserRecord *u = NULL;
3,003✔
1773
        va_list ap;
3,003✔
1774
        int r;
3,003✔
1775

1776
        assert(ret);
3,003✔
1777

1778
        va_start(ap, ret);
3,003✔
1779
        r = sd_json_buildv(&v, ap);
3,003✔
1780
        va_end(ap);
3,003✔
1781

1782
        if (r < 0)
3,003✔
1783
                return r;
1784

1785
        u = user_record_new();
3,003✔
1786
        if (!u)
3,003✔
1787
                return -ENOMEM;
1788

1789
        r = user_record_load(u, v, USER_RECORD_LOAD_FULL);
3,003✔
1790
        if (r < 0)
3,003✔
1791
                return r;
1792

1793
        *ret = TAKE_PTR(u);
3,003✔
1794
        return 0;
3,003✔
1795
}
1796

1797
const char* user_record_user_name_and_realm(UserRecord *h) {
8,097✔
1798
        assert(h);
8,097✔
1799

1800
        /* Return the pre-initialized joined string if it is defined */
1801
        if (h->user_name_and_realm_auto)
8,097✔
1802
                return h->user_name_and_realm_auto;
1803

1804
        /* If it's not defined then we cannot have a realm */
1805
        assert(!h->realm);
8,076✔
1806
        return h->user_name;
8,076✔
1807
}
1808

1809
UserStorage user_record_storage(UserRecord *h) {
21,351✔
1810
        assert(h);
21,351✔
1811

1812
        if (h->storage >= 0)
21,351✔
1813
                return h->storage;
2,416✔
1814

1815
        return USER_CLASSIC;
1816
}
1817

1818
const char* user_record_file_system_type(UserRecord *h) {
×
1819
        assert(h);
×
1820

1821
        return h->file_system_type ?: "btrfs";
×
1822
}
1823

1824
const char* user_record_skeleton_directory(UserRecord *h) {
146✔
1825
        assert(h);
146✔
1826

1827
        return h->skeleton_directory ?: "/etc/skel";
146✔
1828
}
1829

1830
mode_t user_record_access_mode(UserRecord *h) {
9✔
1831
        assert(h);
9✔
1832

1833
        return h->access_mode != MODE_INVALID ? h->access_mode : 0700;
9✔
1834
}
1835

1836
static const char *user_record_home_directory_real(UserRecord *h) {
4,573✔
1837
        assert(h);
4,573✔
1838

1839
        if (h->home_directory)
4,573✔
1840
                return h->home_directory;
1841
        if (h->home_directory_auto)
1,554✔
1842
                return h->home_directory_auto;
1843

1844
        /* The root user is special, hence be special about it */
1845
        if (user_record_is_root(h))
46✔
1846
                return "/root";
×
1847

1848
        return "/";
1849
}
1850

1851
const char* user_record_home_directory(UserRecord *h) {
4,652✔
1852
        assert(h);
4,652✔
1853

1854
        if (h->use_fallback && h->fallback_home_directory)
4,652✔
1855
                return h->fallback_home_directory;
1856

1857
        return user_record_home_directory_real(h);
4,514✔
1858
}
1859

1860
const char* user_record_image_path(UserRecord *h) {
1,168✔
1861
        assert(h);
1,168✔
1862

1863
        if (h->image_path)
1,168✔
1864
                return h->image_path;
1865
        if (h->image_path_auto)
112✔
1866
                return h->image_path_auto;
1867

1868
        /* For some storage types the image is the home directory itself. (But let's ignore the fallback logic for it) */
1869
        return IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT) ?
59✔
1870
                user_record_home_directory_real(h) : NULL;
59✔
1871
}
1872

1873
const char* user_record_cifs_user_name(UserRecord *h) {
×
1874
        assert(h);
×
1875

1876
        return h->cifs_user_name ?: h->user_name;
×
1877
}
1878

1879
unsigned long user_record_mount_flags(UserRecord *h) {
42✔
1880
        assert(h);
42✔
1881

1882
        return (h->nosuid ? MS_NOSUID : 0) |
42✔
1883
                (h->noexec ? MS_NOEXEC : 0) |
42✔
1884
                (h->nodev ? MS_NODEV : 0);
42✔
1885
}
1886

1887
static const char *user_record_shell_real(UserRecord *h) {
1,622✔
1888
        assert(h);
1,622✔
1889

1890
        if (h->shell)
1,622✔
1891
                return h->shell;
1892

1893
        if (user_record_is_root(h))
398✔
1894
                return "/bin/sh";
1895

1896
        if (user_record_disposition(h) == USER_REGULAR)
393✔
1897
                return DEFAULT_USER_SHELL;
375✔
1898

1899
        return NOLOGIN;
1900
}
1901

1902
const char* user_record_shell(UserRecord *h) {
1,622✔
1903
        const char *shell;
1,622✔
1904

1905
        assert(h);
1,622✔
1906

1907
        shell = user_record_shell_real(h);
1,622✔
1908

1909
        /* Return fallback shall if we are told so — except if the primary shell is already a nologin shell,
1910
         * then let's not risk anything. */
1911
        if (h->use_fallback && h->fallback_shell)
1,622✔
1912
                return is_nologin_shell(shell) ? NOLOGIN : h->fallback_shell;
396✔
1913

1914
        return shell;
1915
}
1916

1917
const char* user_record_real_name(UserRecord *h) {
1,118✔
1918
        assert(h);
1,118✔
1919

1920
        return h->real_name ?: h->user_name;
1,118✔
1921
}
1922

1923
bool user_record_luks_discard(UserRecord *h) {
×
1924
        const char *ip;
×
1925

1926
        assert(h);
×
1927

1928
        if (h->luks_discard >= 0)
×
1929
                return h->luks_discard;
×
1930

1931
        ip = user_record_image_path(h);
×
1932
        if (!ip)
×
1933
                return false;
1934

1935
        /* Use discard by default if we are referring to a real block device, but not when operating on a
1936
         * loopback device. We want to optimize for SSD and flash storage after all, but we should be careful
1937
         * when storing stuff on top of regular file systems in loopback files as doing discard then would
1938
         * mean thin provisioning and we should not do that willy-nilly since it means we'll risk EIO later
1939
         * on should the disk space to back our file systems not be available. */
1940

1941
        return path_startswith(ip, "/dev/");
×
1942
}
1943

1944
bool user_record_luks_offline_discard(UserRecord *h) {
×
1945
        const char *ip;
×
1946

1947
        assert(h);
×
1948

1949
        if (h->luks_offline_discard >= 0)
×
1950
                return h->luks_offline_discard;
×
1951

1952
        /* Discard while we are logged out should generally be a good idea, except when operating directly on
1953
         * physical media, where we should just bind it to the online discard mode. */
1954

1955
        ip = user_record_image_path(h);
×
1956
        if (!ip)
×
1957
                return false;
1958

1959
        if (path_startswith(ip, "/dev/"))
×
1960
                return user_record_luks_discard(h);
×
1961

1962
        return true;
1963
}
1964

1965
const char* user_record_luks_cipher(UserRecord *h) {
×
1966
        assert(h);
×
1967

1968
        return h->luks_cipher ?: "aes";
×
1969
}
1970

1971
const char* user_record_luks_cipher_mode(UserRecord *h) {
×
1972
        assert(h);
×
1973

1974
        return h->luks_cipher_mode ?: "xts-plain64";
×
1975
}
1976

1977
uint64_t user_record_luks_volume_key_size(UserRecord *h) {
×
1978
        assert(h);
×
1979

1980
        /* We return a value here that can be cast without loss into size_t which is what libcrypsetup expects */
1981

1982
        if (h->luks_volume_key_size == UINT64_MAX)
×
1983
                return 256 / 8;
×
1984

1985
        return MIN(h->luks_volume_key_size, SIZE_MAX);
1986
}
1987

1988
const char* user_record_luks_pbkdf_type(UserRecord *h) {
×
1989
        assert(h);
×
1990

1991
        return h->luks_pbkdf_type ?: "argon2id";
×
1992
}
1993

1994
uint64_t user_record_luks_pbkdf_force_iterations(UserRecord *h) {
×
1995
        assert(h);
×
1996

1997
        /* propagate default "benchmark" mode as itself */
1998
        if (h->luks_pbkdf_force_iterations == UINT64_MAX)
×
1999
                return UINT64_MAX;
2000

2001
        /* clamp everything else to actually accepted number of iterations of libcryptsetup */
2002
        return CLAMP(h->luks_pbkdf_force_iterations, 1U, UINT32_MAX);
×
2003
}
2004

2005
uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
×
2006
        assert(h);
×
2007

2008
        /* Returns a value with ms granularity, since that's what libcryptsetup expects */
2009

2010
        if (h->luks_pbkdf_time_cost_usec == UINT64_MAX)
×
2011
                return 500 * USEC_PER_MSEC; /* We default to 500ms, in contrast to libcryptsetup's 2s, which is just awfully slow on every login */
2012

2013
        return MIN(DIV_ROUND_UP(h->luks_pbkdf_time_cost_usec, USEC_PER_MSEC), UINT32_MAX) * USEC_PER_MSEC;
×
2014
}
2015

2016
uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h) {
×
2017
        assert(h);
×
2018

2019
        /* Returns a value with kb granularity, since that's what libcryptsetup expects */
2020
        if (h->luks_pbkdf_memory_cost == UINT64_MAX)
×
2021
                return streq(user_record_luks_pbkdf_type(h), "pbkdf2") ? 0 : /* doesn't apply for simple pbkdf2 */
×
2022
                        64*1024*1024; /* We default to 64M, since this should work on smaller systems too */
2023

2024
        return MIN(DIV_ROUND_UP(h->luks_pbkdf_memory_cost, 1024), UINT32_MAX) * 1024;
×
2025
}
2026

2027
uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h) {
×
2028
        assert(h);
×
2029

2030
        if (h->luks_pbkdf_parallel_threads == UINT64_MAX)
×
2031
                return streq(user_record_luks_pbkdf_type(h), "pbkdf2") ? 0 : /* doesn't apply for simple pbkdf2 */
×
2032
                        1; /* We default to 1, since this should work on smaller systems too */
2033

2034
        return MIN(h->luks_pbkdf_parallel_threads, UINT32_MAX);
×
2035
}
2036

2037
uint64_t user_record_luks_sector_size(UserRecord *h) {
×
2038
        assert(h);
×
2039

2040
        if (h->luks_sector_size == UINT64_MAX)
×
2041
                return 512;
2042

2043
        /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */
2044
        return CLAMP(UINT64_C(1) << (63 - __builtin_clzll(h->luks_sector_size)), 512U, 4096U);
×
2045
}
2046

2047
const char* user_record_luks_pbkdf_hash_algorithm(UserRecord *h) {
×
2048
        assert(h);
×
2049

2050
        return h->luks_pbkdf_hash_algorithm ?: "sha512";
×
2051
}
2052

2053
gid_t user_record_gid(UserRecord *h) {
3,857✔
2054
        assert(h);
3,857✔
2055

2056
        if (gid_is_valid(h->gid))
3,857✔
2057
                return h->gid;
2058

2059
        return (gid_t) h->uid;
1,594✔
2060
}
2061

2062
UserDisposition user_record_disposition(UserRecord *h) {
24,368✔
2063
        assert(h);
24,368✔
2064

2065
        if (h->disposition >= 0)
24,368✔
2066
                return h->disposition;
2067

2068
        /* If not declared, derive from UID */
2069

2070
        if (!uid_is_valid(h->uid))
3,883✔
2071
                return _USER_DISPOSITION_INVALID;
2072

2073
        if (user_record_is_root(h) || user_record_is_nobody(h))
3,883✔
2074
                return USER_INTRINSIC;
1,604✔
2075

2076
        if (uid_is_system(h->uid))
2,279✔
2077
                return USER_SYSTEM;
2078

2079
        if (uid_is_dynamic(h->uid) || uid_is_greeter(h->uid))
274✔
2080
                return USER_DYNAMIC;
2081

2082
        if (uid_is_container(h->uid))
274✔
2083
                return USER_CONTAINER;
2084

2085
        if (uid_is_foreign(h->uid))
177✔
2086
                return USER_FOREIGN;
2087

2088
        if (h->uid > INT32_MAX)
177✔
2089
                return USER_RESERVED;
×
2090

2091
        return USER_REGULAR;
2092
}
2093

2094
int user_record_removable(UserRecord *h) {
11,317✔
2095
        UserStorage storage;
11,317✔
2096
        assert(h);
11,317✔
2097

2098
        if (h->removable >= 0)
11,317✔
2099
                return h->removable;
2100

2101
        /* Refuse to decide for classic records */
2102
        storage = user_record_storage(h);
11,317✔
2103
        if (h->storage < 0 || h->storage == USER_CLASSIC)
11,317✔
2104
                return -1;
2105

2106
        /* For now consider only LUKS home directories with a reference by path as removable */
2107
        return storage == USER_LUKS && path_startswith(user_record_image_path(h), "/dev/");
373✔
2108
}
2109

2110
uint64_t user_record_ratelimit_interval_usec(UserRecord *h) {
128✔
2111
        assert(h);
128✔
2112

2113
        if (h->ratelimit_interval_usec == UINT64_MAX)
128✔
2114
                return DEFAULT_RATELIMIT_INTERVAL_USEC;
43✔
2115

2116
        return h->ratelimit_interval_usec;
2117
}
2118

2119
uint64_t user_record_ratelimit_burst(UserRecord *h) {
300✔
2120
        assert(h);
300✔
2121

2122
        if (h->ratelimit_burst == UINT64_MAX)
300✔
2123
                return DEFAULT_RATELIMIT_BURST;
123✔
2124

2125
        return h->ratelimit_burst;
2126
}
2127

2128
bool user_record_can_authenticate(UserRecord *h) {
×
2129
        assert(h);
×
2130

2131
        /* Returns true if there's some form of property configured that the user can authenticate against */
2132

2133
        if (h->n_pkcs11_encrypted_key > 0)
×
2134
                return true;
2135

2136
        if (h->n_fido2_hmac_salt > 0)
×
2137
                return true;
2138

2139
        return !strv_isempty(h->hashed_password);
×
2140
}
2141

2142
bool user_record_drop_caches(UserRecord *h) {
213✔
2143
        assert(h);
213✔
2144

2145
        if (h->drop_caches >= 0)
213✔
2146
                return h->drop_caches;
×
2147

2148
        /* By default drop caches on fscrypt, not otherwise. */
2149
        return user_record_storage(h) == USER_FSCRYPT;
213✔
2150
}
2151

2152
AutoResizeMode user_record_auto_resize_mode(UserRecord *h) {
×
2153
        assert(h);
×
2154

2155
        if (h->auto_resize_mode >= 0)
×
2156
                return h->auto_resize_mode;
2157

2158
        return user_record_storage(h) == USER_LUKS ? AUTO_RESIZE_SHRINK_AND_GROW : AUTO_RESIZE_OFF;
×
2159
}
2160

2161
uint64_t user_record_rebalance_weight(UserRecord *h) {
142✔
2162
        assert(h);
142✔
2163

2164
        if (h->rebalance_weight == REBALANCE_WEIGHT_UNSET)
142✔
2165
                return REBALANCE_WEIGHT_DEFAULT;
33✔
2166

2167
        return h->rebalance_weight;
2168
}
2169

2170
static uint64_t parse_caps_strv(char **l) {
×
2171
        uint64_t c = 0;
×
2172
        int r;
×
2173

2174
        STRV_FOREACH(i, l) {
×
2175
                r = capability_from_name(*i);
×
2176
                if (r < 0)
×
2177
                        log_debug_errno(r, "Don't know capability '%s', ignoring: %m", *i);
×
2178
                else
2179
                        c |= UINT64_C(1) << r;
×
2180
        }
2181

2182
        return c;
×
2183
}
2184

2185
uint64_t user_record_capability_bounding_set(UserRecord *h) {
594✔
2186
        assert(h);
594✔
2187

2188
        /* Returns UINT64_MAX if no bounding set is configured (!) */
2189

2190
        if (!h->capability_bounding_set)
594✔
2191
                return UINT64_MAX;
2192

2193
        return parse_caps_strv(h->capability_bounding_set);
×
2194
}
2195

2196
uint64_t user_record_capability_ambient_set(UserRecord *h) {
594✔
2197
        assert(h);
594✔
2198

2199
        /* Returns UINT64_MAX if no ambient set is configured (!) */
2200

2201
        if (!h->capability_ambient_set)
594✔
2202
                return UINT64_MAX;
2203

2204
        return parse_caps_strv(h->capability_ambient_set) & user_record_capability_bounding_set(h);
×
2205
}
2206

2207
int user_record_languages(UserRecord *h, char ***ret) {
594✔
2208
        _cleanup_strv_free_ char **l = NULL;
594✔
2209
        int r;
594✔
2210

2211
        assert(h);
594✔
2212
        assert(ret);
594✔
2213

2214
        if (h->preferred_language) {
594✔
2215
                l = strv_new(h->preferred_language);
×
2216
                if (!l)
×
2217
                        return -ENOMEM;
2218
        }
2219

2220
        r = strv_extend_strv(&l, h->additional_languages, /* filter_duplicates= */ true);
594✔
2221
        if (r < 0)
594✔
2222
                return r;
2223

2224
        *ret = TAKE_PTR(l);
594✔
2225
        return 0;
594✔
2226
}
2227

2228
uint32_t user_record_tmp_limit_scale(UserRecord *h) {
263✔
2229
        assert(h);
263✔
2230

2231
        if (h->tmp_limit.is_set)
263✔
2232
                return h->tmp_limit.limit_scale;
2✔
2233

2234
        /* By default grant regular users only 80% quota */
2235
        if (user_record_disposition(h) == USER_REGULAR)
261✔
2236
                return UINT32_SCALE_FROM_PERCENT(80);
209✔
2237

2238
        return UINT32_MAX;
2239
}
2240

2241
uint32_t user_record_dev_shm_limit_scale(UserRecord *h) {
263✔
2242
        assert(h);
263✔
2243

2244
        if (h->dev_shm_limit.is_set)
263✔
2245
                return h->dev_shm_limit.limit_scale;
2✔
2246

2247
        /* By default grant regular users only 80% quota */
2248
        if (user_record_disposition(h) == USER_REGULAR)
261✔
2249
                return UINT32_SCALE_FROM_PERCENT(80);
209✔
2250

2251
        return UINT32_MAX;
2252
}
2253

2254
const char** user_record_self_modifiable_fields(UserRecord *h) {
217✔
2255
        /* As a rule of thumb: a setting is safe if it cannot be used by a
2256
         * user to give themselves some unfair advantage over other users on
2257
         * a given system. */
2258
        static const char *const default_fields[] = {
217✔
2259
                /* For display purposes */
2260
                "realName",
2261
                "emailAddress", /* Just the $EMAIL env var */
2262
                "iconName",
2263
                "location",
2264

2265
                /* Basic account settings */
2266
                "shell",
2267
                "umask",
2268
                "environment",
2269
                "timeZone",
2270
                "preferredLanguage",
2271
                "additionalLanguages",
2272
                "preferredSessionLauncher",
2273
                "preferredSessionType",
2274
                "defaultArea",
2275

2276
                /* Authentication methods */
2277
                "pkcs11TokenUri",
2278
                "fido2HmacCredential",
2279
                "recoveryKeyType",
2280

2281
                "lastChangeUSec", /* Necessary to be able to change record at all */
2282
                "lastPasswordChangeUSec", /* Ditto, but for authentication methods */
2283
                NULL
2284
        };
2285

2286
        assert(h);
217✔
2287

2288
        /* Note: if the self_modifiable_fields field in UserRecord is NULL we'll apply a default, if we have
2289
         * one. If it is a non-NULL empty strv, we'll report it as explicit empty list. When the field is
2290
         * NULL and we have no default list we'll return NULL. */
2291

2292
        /* Note that we intentionally distinguish between NULL and an empty array here */
2293
        if (h->self_modifiable_fields)
217✔
2294
                return (const char**) h->self_modifiable_fields;
2295

2296
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_fields : NULL;
215✔
2297
}
2298

2299
const char** user_record_self_modifiable_blobs(UserRecord *h) {
155✔
2300
        static const char *const default_blobs[] = {
155✔
2301
                /* For display purposes */
2302
                "avatar",
2303
                "login-background",
2304
                NULL
2305
        };
2306

2307
        assert(h);
155✔
2308

2309
        /* Note that we intentionally distinguish between NULL and an empty array here */
2310
        if (h->self_modifiable_blobs)
155✔
2311
                return (const char**) h->self_modifiable_blobs;
2312

2313
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_blobs : NULL;
155✔
2314
}
2315

2316
const char** user_record_self_modifiable_privileged(UserRecord *h) {
179✔
2317
        static const char *const default_fields[] = {
179✔
2318
                /* For display purposes */
2319
                "passwordHint",
2320

2321
                /* Authentication methods */
2322
                "hashedPassword",
2323
                "pkcs11EncryptedKey",
2324
                "fido2HmacSalt",
2325
                "recoveryKey",
2326

2327
                "sshAuthorizedKeys", /* Basically just ~/.ssh/authorized_keys */
2328
                NULL
2329
        };
2330

2331
        assert(h);
179✔
2332

2333
        /* Note that we intentionally distinguish between NULL and an empty array here */
2334
        if (h->self_modifiable_privileged)
179✔
2335
                return (const char**) h->self_modifiable_privileged;
2336

2337
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_fields : NULL;
177✔
2338
}
2339

2340
static int remove_self_modifiable_json_fields_common(UserRecord *current, sd_json_variant **target) {
72✔
2341
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *blobs = NULL;
72✔
2342
        char **allowed;
72✔
2343
        int r;
72✔
2344

2345
        assert(current);
72✔
2346
        assert(target);
72✔
2347

2348
        if (!sd_json_variant_is_object(*target))
72✔
2349
                return -EINVAL;
2350

2351
        v = sd_json_variant_ref(*target);
72✔
2352

2353
        /* Handle basic fields */
2354
        allowed = (char**) user_record_self_modifiable_fields(current);
72✔
2355
        r = sd_json_variant_filter(&v, allowed);
72✔
2356
        if (r < 0)
72✔
2357
                return r;
2358

2359
        /* Handle blobs */
2360
        blobs = sd_json_variant_ref(sd_json_variant_by_key(v, "blobManifest"));
72✔
2361
        if (blobs) {
72✔
2362
                /* The blobManifest contains the sha256 hashes of the blobs,
2363
                 * which are enforced by the service managing the user. So, by
2364
                 * comparing the blob manifests like this, we're actually comparing
2365
                 * the contents of the blob directories & files */
2366

2367
                allowed = (char**) user_record_self_modifiable_blobs(current);
10✔
2368
                r = sd_json_variant_filter(&blobs, allowed);
10✔
2369
                if (r < 0)
10✔
2370
                        return r;
2371

2372
                if (sd_json_variant_is_blank_object(blobs))
10✔
2373
                        r = sd_json_variant_filter(&v, STRV_MAKE("blobManifest"));
×
2374
                else
2375
                        r = sd_json_variant_set_field(&v, "blobManifest", blobs);
10✔
2376
                if (r < 0)
10✔
2377
                        return r;
2378
        }
2379

2380
        JSON_VARIANT_REPLACE(*target, TAKE_PTR(v));
72✔
2381
        return 0;
72✔
2382
}
2383

2384
static int remove_self_modifiable_json_fields(UserRecord *current, UserRecord *h, sd_json_variant **ret) {
44✔
2385
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *privileged = NULL;
44✔
2386
        sd_json_variant *per_machine;
44✔
2387
        char **allowed;
44✔
2388
        int r;
44✔
2389

2390
        assert(current);
44✔
2391
        assert(h);
44✔
2392
        assert(ret);
44✔
2393

2394
        r = user_group_record_mangle(h->json, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &v, NULL);
44✔
2395
        if (r < 0)
44✔
2396
                return r;
2397

2398
        /* Handle the regular section */
2399
        r = remove_self_modifiable_json_fields_common(current, &v);
44✔
2400
        if (r < 0)
44✔
2401
                return r;
2402

2403
        /* Handle the perMachine section */
2404
        per_machine = sd_json_variant_by_key(v, "perMachine");
44✔
2405
        if (per_machine) {
44✔
2406
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *new_per_machine = NULL;
28✔
2407
                sd_json_variant *e;
28✔
2408

2409
                if (!sd_json_variant_is_array(per_machine))
28✔
2410
                        return -EINVAL;
2411

2412
                JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
63✔
2413
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *z = NULL;
35✔
2414

2415
                        if (!sd_json_variant_is_object(e))
35✔
2416
                                return -EINVAL;
2417

2418
                        r = per_machine_match(e, 0);
35✔
2419
                        if (r < 0)
35✔
2420
                                return r;
2421
                        if (r == 0) {
35✔
2422
                                /* It's only permissible to change anything inside of matching perMachine sections */
2423
                                r = sd_json_variant_append_array(&new_per_machine, e);
7✔
2424
                                if (r < 0)
7✔
2425
                                        return r;
2426
                                continue;
7✔
2427
                        }
2428

2429
                        z = sd_json_variant_ref(e);
28✔
2430

2431
                        r = remove_self_modifiable_json_fields_common(current, &z);
28✔
2432
                        if (r < 0)
28✔
2433
                                return r;
2434

2435
                        if (!sd_json_variant_is_blank_object(z)) {
28✔
2436
                                r = sd_json_variant_append_array(&new_per_machine, z);
28✔
2437
                                if (r < 0)
28✔
2438
                                        return r;
2439
                        }
2440
                }
2441

2442
                if (sd_json_variant_is_blank_array(new_per_machine))
28✔
2443
                        r = sd_json_variant_filter(&v, STRV_MAKE("perMachine"));
×
2444
                else
2445
                        r = sd_json_variant_set_field(&v, "perMachine", new_per_machine);
28✔
2446
                if (r < 0)
28✔
2447
                        return r;
2448
        }
2449

2450
        /* Handle the privileged section */
2451
        privileged = sd_json_variant_ref(sd_json_variant_by_key(v, "privileged"));
44✔
2452
        if (privileged) {
44✔
2453
                allowed = (char**) user_record_self_modifiable_privileged(current);
34✔
2454
                r = sd_json_variant_filter(&privileged, allowed);
34✔
2455
                if (r < 0)
34✔
2456
                        return r;
2457

2458
                if (sd_json_variant_is_blank_object(privileged))
34✔
2459
                        r = sd_json_variant_filter(&v, STRV_MAKE("privileged"));
32✔
2460
                else
2461
                        r = sd_json_variant_set_field(&v, "privileged", privileged);
2✔
2462
                if (r < 0)
34✔
2463
                        return r;
2464
        }
2465

2466
        JSON_VARIANT_REPLACE(*ret, TAKE_PTR(v));
44✔
2467
        return 0;
44✔
2468
}
2469

2470
int user_record_self_changes_allowed(UserRecord *current, UserRecord *incoming) {
22✔
2471
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *vc = NULL, *vi = NULL;
44✔
2472
        int r;
22✔
2473

2474
        assert(current);
22✔
2475
        assert(incoming);
22✔
2476

2477
        /* We remove the fields that the user is allowed to change and then
2478
         * compare the resulting JSON records. If they are not equal, that
2479
         * means a disallowed field has been changed and thus we should
2480
         * require administrator permission to apply the changes. */
2481

2482
        r = remove_self_modifiable_json_fields(current, current, &vc);
22✔
2483
        if (r < 0)
22✔
2484
                return r;
2485

2486
        /* Note that we use `current` as the source of the allowlist, and not
2487
         * `incoming`. This prevents the user from adding fields. Consider a
2488
         * scenario that would've been possible if we had messed up this check:
2489
         *
2490
         * 1) A user starts out with no group memberships and no custom allowlist.
2491
         *    Thus, this user is not an administrator, and the `memberOf` and
2492
         *    `selfModifiableFields` fields are unset in their record.
2493
         * 2) This user crafts a request to add the following to their record:
2494
         *    { "memberOf": ["wheel"], "selfModifiableFields": ["memberOf", "selfModifiableFields"] }
2495
         * 3) We remove the `mebmerOf` and `selfModifiabileFields` fields from `incoming`
2496
         * 4) `current` and `incoming` compare as equal, so we let the change happen
2497
         * 5) the user has granted themselves administrator privileges
2498
         */
2499
        r = remove_self_modifiable_json_fields(current, incoming, &vi);
22✔
2500
        if (r < 0)
22✔
2501
                return r;
2502

2503
        return sd_json_variant_equal(vc, vi);
22✔
2504
}
2505

2506
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
300✔
2507
        assert(h);
300✔
2508

2509
        /* Calculates when the it's possible to login next. Returns:
2510
         *
2511
         * UINT64_MAX → Nothing known
2512
         * 0          → Right away
2513
         * Any other  → Next time in CLOCK_REALTIME in usec (which could be in the past)
2514
         */
2515

2516
        if (h->ratelimit_begin_usec == UINT64_MAX ||
300✔
2517
            h->ratelimit_count == UINT64_MAX)
179✔
2518
                return UINT64_MAX;
2519

2520
        if (h->ratelimit_begin_usec > now(CLOCK_REALTIME)) /* If the ratelimit time is in the future, then
179✔
2521
                                                            * the local clock is probably incorrect. Let's
2522
                                                            * not refuse login then. */
2523
                return UINT64_MAX;
2524

2525
        if (h->ratelimit_count < user_record_ratelimit_burst(h))
179✔
2526
                return 0;
2527

2528
        return usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h));
×
2529
}
2530

2531
bool user_record_equal(UserRecord *a, UserRecord *b) {
149✔
2532
        assert(a);
149✔
2533
        assert(b);
149✔
2534

2535
        /* We assume that when a record is modified its JSON data is updated at the same time, hence it's
2536
         * sufficient to compare the JSON data. */
2537

2538
        return sd_json_variant_equal(a->json, b->json);
149✔
2539
}
2540

2541
bool user_record_compatible(UserRecord *a, UserRecord *b) {
133✔
2542
        assert(a);
133✔
2543
        assert(b);
133✔
2544

2545
        /* If either lacks the regular section, we can't really decide, let's hence say they are
2546
         * incompatible. */
2547
        if (!(a->mask & b->mask & USER_RECORD_REGULAR))
133✔
2548
                return false;
2549

2550
        return streq_ptr(a->user_name, b->user_name) &&
133✔
2551
                streq_ptr(a->realm, b->realm);
133✔
2552
}
2553

2554
int user_record_compare_last_change(UserRecord *a, UserRecord *b) {
16✔
2555
        assert(a);
16✔
2556
        assert(b);
16✔
2557

2558
        if (a->last_change_usec == b->last_change_usec)
16✔
2559
                return 0;
2560

2561
        /* Always consider a record with a timestamp newer than one without */
2562
        if (a->last_change_usec == UINT64_MAX)
16✔
2563
                return -1;
2564
        if (b->last_change_usec == UINT64_MAX)
16✔
2565
                return 1;
2566

2567
        return CMP(a->last_change_usec, b->last_change_usec);
16✔
2568
}
2569

2570
int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret) {
3,906✔
2571
        _cleanup_(user_record_unrefp) UserRecord *c = NULL;
3,906✔
2572
        int r;
3,906✔
2573

2574
        assert(h);
3,906✔
2575
        assert(ret);
3,906✔
2576

2577
        c = user_record_new();
3,906✔
2578
        if (!c)
3,906✔
2579
                return -ENOMEM;
2580

2581
        r = user_record_load(c, h->json, flags);
3,906✔
2582
        if (r < 0)
3,906✔
2583
                return r;
2584

2585
        *ret = TAKE_PTR(c);
3,906✔
2586
        return 0;
3,906✔
2587
}
2588

2589
int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask) {
24✔
2590
        _cleanup_(user_record_unrefp) UserRecord *x = NULL, *y = NULL;
24✔
2591
        int r;
24✔
2592

2593
        assert(a);
24✔
2594
        assert(b);
24✔
2595

2596
        /* Compares the two records, but ignores anything not listed in the specified mask */
2597

2598
        if ((a->mask & ~mask) != 0) {
24✔
2599
                r = user_record_clone(a, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &x);
24✔
2600
                if (r < 0)
24✔
2601
                        return r;
2602

2603
                a = x;
24✔
2604
        }
2605

2606
        if ((b->mask & ~mask) != 0) {
24✔
2607
                r = user_record_clone(b, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &y);
24✔
2608
                if (r < 0)
24✔
2609
                        return r;
2610

2611
                b = y;
24✔
2612
        }
2613

2614
        return user_record_equal(a, b);
24✔
2615
}
2616

2617
int user_record_test_blocked(UserRecord *h) {
195✔
2618
        usec_t n;
195✔
2619

2620
        /* Checks whether access to the specified user shall be allowed at the moment. Returns:
2621
         *
2622
         *          -ESTALE: Record is from the future
2623
         *          -ENOLCK: Record is blocked
2624
         *          -EL2HLT: Record is not valid yet
2625
         *          -EL3HLT: Record is not valid anymore
2626
         *
2627
         */
2628

2629
        assert(h);
195✔
2630

2631
        if (h->locked > 0)
195✔
2632
                return -ENOLCK;
2633

2634
        n = now(CLOCK_REALTIME);
166✔
2635

2636
        if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
166✔
2637
                return -EL2HLT;
2638
        if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
166✔
2639
                return -EL3HLT;
2640

2641
        if (h->last_change_usec != UINT64_MAX &&
166✔
2642
            h->last_change_usec > n) /* Complain during log-ins when the record is from the future */
2643
                return -ESTALE;
×
2644

2645
        return 0;
2646
}
2647

2648
int user_record_test_password_change_required(UserRecord *h) {
197✔
2649
        bool change_permitted;
197✔
2650
        usec_t n;
197✔
2651

2652
        assert(h);
197✔
2653

2654
        /* Checks whether the user must change the password when logging in
2655

2656
            -EKEYREVOKED: Change password now because admin said so
2657
             -EOWNERDEAD: Change password now because it expired
2658
           -EKEYREJECTED: Password is expired, no changing is allowed
2659
            -EKEYEXPIRED: Password is about to expire, warn user
2660
               -ENETDOWN: Record has expiration info but no password change timestamp
2661
                  -EROFS: No password change required nor permitted
2662
                 -ESTALE: RTC likely incorrect, last password change is in the future
2663
                       0: No password change required, but permitted
2664
         */
2665

2666
        /* If a password change request has been set explicitly, it overrides everything */
2667
        if (h->password_change_now > 0)
197✔
2668
                return -EKEYREVOKED;
2669

2670
        n = now(CLOCK_REALTIME);
197✔
2671

2672
        /* Password change in the future? Then our RTC is likely incorrect */
2673
        if (h->last_password_change_usec != UINT64_MAX &&
197✔
2674
            h->last_password_change_usec > n &&
×
2675
            (h->password_change_min_usec != UINT64_MAX ||
×
2676
             h->password_change_max_usec != UINT64_MAX ||
×
2677
             h->password_change_inactive_usec != UINT64_MAX))
×
2678
            return -ESTALE;
2679

2680
        /* Then, let's check if password changing is currently allowed at all */
2681
        if (h->password_change_min_usec != UINT64_MAX) {
197✔
2682

2683
                /* Expiry configured but no password change timestamp known? */
2684
                if (h->last_password_change_usec == UINT64_MAX)
×
2685
                        return -ENETDOWN;
2686

2687
                if (h->password_change_min_usec >= UINT64_MAX - h->last_password_change_usec)
×
2688
                        change_permitted = false;
2689
                else
2690
                        change_permitted = n >= h->last_password_change_usec + h->password_change_min_usec;
×
2691

2692
        } else
2693
                change_permitted = true;
2694

2695
        /* Let's check whether the password has expired.  */
2696
        if (!(h->password_change_max_usec == UINT64_MAX ||
197✔
2697
              h->password_change_max_usec >= UINT64_MAX - h->last_password_change_usec)) {
×
2698

2699
                uint64_t change_before;
×
2700

2701
                /* Expiry configured but no password change timestamp known? */
2702
                if (h->last_password_change_usec == UINT64_MAX)
×
2703
                        return -ENETDOWN;
2704

2705
                /* Password is in inactive phase? */
2706
                if (h->password_change_inactive_usec != UINT64_MAX &&
×
2707
                    h->password_change_inactive_usec < UINT64_MAX - h->password_change_max_usec) {
×
2708
                        usec_t added;
×
2709

2710
                        added = h->password_change_inactive_usec + h->password_change_max_usec;
×
2711
                        if (added < UINT64_MAX - h->last_password_change_usec &&
×
2712
                            n >= h->last_password_change_usec + added)
×
2713
                                return -EKEYREJECTED;
2714
                }
2715

2716
                /* Password needs to be changed now? */
2717
                change_before = h->last_password_change_usec + h->password_change_max_usec;
×
2718
                if (n >= change_before)
×
2719
                        return change_permitted ? -EOWNERDEAD : -EKEYREJECTED;
×
2720

2721
                /* Warn user? */
2722
                if (h->password_change_warn_usec != UINT64_MAX &&
×
2723
                    (change_before < h->password_change_warn_usec ||
×
2724
                     n >= change_before - h->password_change_warn_usec))
×
2725
                        return change_permitted ? -EKEYEXPIRED : -EROFS;
×
2726
        }
2727

2728
        /* No password changing necessary */
2729
        return change_permitted ? 0 : -EROFS;
197✔
2730
}
2731

2732
bool user_record_is_root(const UserRecord *u) {
4,808✔
2733
        assert(u);
4,808✔
2734

2735
        return u->uid == 0 || streq_ptr(u->user_name, "root");
4,808✔
2736
}
2737

2738
bool user_record_is_nobody(const UserRecord *u) {
2,518✔
2739
        assert(u);
2,518✔
2740

2741
        return u->uid == UID_NOBODY || STRPTR_IN_SET(u->user_name, NOBODY_USER_NAME, "nobody");
2,518✔
2742
}
2743

2744
bool user_record_matches_user_name(const UserRecord *u, const char *user_name) {
5,416✔
2745
        assert(u);
5,416✔
2746
        assert(user_name);
5,416✔
2747

2748
        if (streq_ptr(u->user_name, user_name))
5,416✔
2749
                return true;
2750

2751
        if (streq_ptr(u->user_name_and_realm_auto, user_name))
27✔
2752
                return true;
2753

2754
        if (strv_contains(u->aliases, user_name))
21✔
2755
                return true;
2756

2757
        const char *realm = strrchr(user_name, '@');
15✔
2758
        if (realm && streq_ptr(realm+1, u->realm))
15✔
2759
                STRV_FOREACH(a, u->aliases)
18✔
2760
                        if (startswith(user_name, *a) == realm)
18✔
2761
                                return true;
2762

2763
        return false;
2764
}
2765

2766
int suitable_blob_filename(const char *name) {
429✔
2767
        /* Enforces filename requirements as described in docs/USER_RECORD_BULK_DIRS.md */
2768
        return filename_is_valid(name) &&
858✔
2769
               in_charset(name, URI_UNRESERVED) &&
429✔
2770
               name[0] != '.';
423✔
2771
}
2772

2773
bool userdb_match_is_set(const UserDBMatch *match) {
52,628✔
2774
        if (!match)
52,628✔
2775
                return false;
2776

2777
        return !strv_isempty(match->fuzzy_names) ||
33,844✔
2778
                !FLAGS_SET(match->disposition_mask, USER_DISPOSITION_MASK_ALL) ||
33,836✔
2779
                match->uid_min > 0 ||
25,443✔
2780
                match->uid_max < UID_INVALID-1 ||
51,149✔
2781
                !sd_id128_is_null(match->uuid);
17,305✔
2782
}
2783

2784
void userdb_match_done(UserDBMatch *match) {
24,885✔
2785
        assert(match);
24,885✔
2786
        strv_free(match->fuzzy_names);
24,885✔
2787
}
24,885✔
2788

2789
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches) {
6✔
2790
        assert(names || n_names == 0);
6✔
2791

2792
        /* Checks if any of the user record strings in the names[] array matches any of the search strings in
2793
         * the matches** strv fuzzily. */
2794

2795
        FOREACH_ARRAY(n, names, n_names) {
22✔
2796
                if (!*n)
16✔
2797
                        continue;
8✔
2798

2799
                _cleanup_free_ char *lcn = strdup(*n);
8✔
2800
                if (!lcn)
8✔
2801
                        return -ENOMEM;
2802

2803
                ascii_strlower(lcn);
8✔
2804

2805
                STRV_FOREACH(i, matches) {
16✔
2806
                        _cleanup_free_ char *lc = strdup(*i);
8✔
2807
                        if (!lc)
8✔
2808
                                return -ENOMEM;
2809

2810
                        ascii_strlower(lc);
8✔
2811

2812
                        /* First do substring check */
2813
                        if (strstr(lcn, lc))
8✔
2814
                                return true;
2815

2816
                        /* Then do some fuzzy string comparison (but only if the needle is non-trivially long) */
2817
                        if (strlen(lc) >= 5 && strlevenshtein(lcn, lc) < 3)
8✔
2818
                                return true;
2819
                }
2820
        }
2821

2822
        return false;
2823
}
2824

2825
bool user_record_match(UserRecord *u, const UserDBMatch *match) {
9,605✔
2826
        assert(u);
9,605✔
2827

2828
        if (!match)
9,605✔
2829
                return true;
2830

2831
        if (!uid_is_valid(u->uid))
7,052✔
2832
                return false;
2833

2834
        if (u->uid < match->uid_min || u->uid > match->uid_max)
7,052✔
2835
                return false;
2836

2837
        if (!BIT_SET(match->disposition_mask, user_record_disposition(u)))
7,050✔
2838
                return false;
2839

2840
        if (!sd_id128_is_null(match->uuid) && !sd_id128_equal(match->uuid, u->uuid))
13,920✔
2841
                return false;
×
2842

2843
        if (!strv_isempty(match->fuzzy_names)) {
6,960✔
2844

2845
                /* Note this array of names is sparse, i.e. various entries listed in it will be
2846
                 * NULL. Because of that we are not using a NULL terminated strv here, but a regular
2847
                 * array. */
2848
                const char* names[] = {
4✔
2849
                        u->user_name,
2✔
2850
                        user_record_user_name_and_realm(u),
2✔
2851
                        u->real_name,
2✔
2852
                        u->email_address,
2✔
2853
                        u->cifs_user_name,
2✔
2854
                };
2855

2856
                if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names) &&
4✔
2857
                    !user_name_fuzzy_match((const char**) u->aliases, strv_length(u->aliases), match->fuzzy_names))
2✔
2858
                        return false;
2✔
2859
        }
2860

2861
        return true;
2862
}
2863

UNCOV
2864
int json_dispatch_dispositions_mask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
UNCOV
2865
        uint64_t *mask = ASSERT_PTR(userdata);
×
2866

UNCOV
2867
        if (sd_json_variant_is_null(variant)) {
×
2868
                *mask = UINT64_MAX;
×
2869
                return 0;
×
2870
        }
2871

UNCOV
2872
        if (!sd_json_variant_is_array(variant))
×
2873
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
×
2874

2875
        uint64_t m = 0;
UNCOV
2876
        for (size_t i = 0; i < sd_json_variant_elements(variant); i++) {
×
UNCOV
2877
                sd_json_variant *e;
×
UNCOV
2878
                const char *a;
×
2879

UNCOV
2880
                e = sd_json_variant_by_index(variant, i);
×
UNCOV
2881
                if (!sd_json_variant_is_string(e))
×
2882
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
2883

UNCOV
2884
                assert_se(a = sd_json_variant_string(e));
×
2885

UNCOV
2886
                UserDisposition d = user_disposition_from_string(a);
×
UNCOV
2887
                if (d < 0)
×
2888
                        return json_log(e, flags, d, "JSON field '%s' contains an invalid user disposition type: %s", strna(name), a);
×
2889

UNCOV
2890
                m |= INDEX_TO_MASK(uint64_t, d);
×
2891
        }
2892

UNCOV
2893
        *mask = m;
×
UNCOV
2894
        return 0;
×
2895
}
2896

2897
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
2898
        [USER_CLASSIC]   = "classic",
2899
        [USER_LUKS]      = "luks",
2900
        [USER_DIRECTORY] = "directory",
2901
        [USER_SUBVOLUME] = "subvolume",
2902
        [USER_FSCRYPT]   = "fscrypt",
2903
        [USER_CIFS]      = "cifs",
2904
};
2905

2906
DEFINE_STRING_TABLE_LOOKUP(user_storage, UserStorage);
3,029✔
2907

2908
static const char* const user_disposition_table[_USER_DISPOSITION_MAX] = {
2909
        [USER_INTRINSIC] = "intrinsic",
2910
        [USER_SYSTEM]    = "system",
2911
        [USER_DYNAMIC]   = "dynamic",
2912
        [USER_REGULAR]   = "regular",
2913
        [USER_CONTAINER] = "container",
2914
        [USER_FOREIGN]   = "foreign",
2915
        [USER_RESERVED]  = "reserved",
2916
};
2917

2918
DEFINE_STRING_TABLE_LOOKUP(user_disposition, UserDisposition);
74,520✔
2919

2920
static const char* const auto_resize_mode_table[_AUTO_RESIZE_MODE_MAX] = {
2921
        [AUTO_RESIZE_OFF]             = "off",
2922
        [AUTO_RESIZE_GROW]            = "grow",
2923
        [AUTO_RESIZE_SHRINK_AND_GROW] = "shrink-and-grow",
2924
};
2925

2926
DEFINE_STRING_TABLE_LOOKUP(auto_resize_mode, AutoResizeMode);
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc