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

systemd / systemd / 22980501960

11 Mar 2026 08:14PM UTC coverage: 72.513% (+0.1%) from 72.411%
22980501960

push

github

yuwata
hwdb/keyboard: fix Positron vendor location

Move lines without changing them.
Fixes: 9aad3336f ("hwdb/keyboard: Map FN key on Positron Proxima 15")
(https://github.com/systemd/systemd/pull/40929)

315785 of 435487 relevant lines covered (72.51%)

1194954.79 hits per line

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

56.94
/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) {
19,301✔
34
        UserRecord *h;
19,301✔
35

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

40
        *h = (UserRecord) {
19,301✔
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;
19,301✔
105
}
106

107
sd_json_dispatch_flags_t USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(UserRecordLoadFlags flags) {
56,720✔
108
        return (FLAGS_SET(flags, USER_RECORD_LOG) ? SD_JSON_LOG : 0) |
56,720✔
109
                (FLAGS_SET(flags, USER_RECORD_PERMISSIVE) ? SD_JSON_PERMISSIVE : 0);
56,720✔
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) {
19,266✔
146
        if (!h)
19,266✔
147
                return NULL;
148

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

159
        free(h->blob_directory);
19,266✔
160
        hashmap_free(h->blob_manifest);
19,266✔
161

162
        free(h->shell);
19,266✔
163

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

170
        free(h->skeleton_directory);
19,266✔
171

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

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

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

187
        free(h->fallback_shell);
19,266✔
188
        free(h->fallback_home_directory);
19,266✔
189

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

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

201
        free(h->state);
19,266✔
202
        free(h->service);
19,266✔
203

204
        free(h->preferred_session_type);
19,266✔
205
        free(h->preferred_session_launcher);
19,266✔
206

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

212
        for (size_t i = 0; i < h->n_fido2_hmac_credential; i++)
19,266✔
213
                fido2_hmac_credential_done(h->fido2_hmac_credential + i);
×
214
        free(h->fido2_hmac_credential);
19,266✔
215
        for (size_t i = 0; i < h->n_fido2_hmac_salt; i++)
19,266✔
216
                fido2_hmac_salt_done(h->fido2_hmac_salt + i);
×
217
        free(h->fido2_hmac_salt);
19,266✔
218

219
        strv_free(h->recovery_key_type);
19,266✔
220
        for (size_t i = 0; i < h->n_recovery_key; i++)
19,266✔
221
                recovery_key_done(h->recovery_key + i);
×
222
        free(h->recovery_key);
19,266✔
223

224
        strv_free(h->self_modifiable_fields);
19,266✔
225
        strv_free(h->self_modifiable_blobs);
19,266✔
226
        strv_free(h->self_modifiable_privileged);
19,266✔
227

228
        free(h->default_area);
19,266✔
229

230
        sd_json_variant_unref(h->json);
19,266✔
231

232
        return mfree(h);
19,266✔
233
}
234

235
DEFINE_TRIVIAL_REF_UNREF_FUNC(UserRecord, user_record, user_record_free);
36,111✔
236

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

242
        if (sd_json_variant_is_null(variant)) {
94✔
243
                *s = mfree(*s);
×
244
                return 0;
×
245
        }
246

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

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

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

261
        return 0;
262
}
263

264
int json_dispatch_gecos(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
7,509✔
265
        char **s = userdata;
7,509✔
266
        const char *n;
7,509✔
267

268
        if (sd_json_variant_is_null(variant)) {
7,509✔
269
                *s = mfree(*s);
×
270
                return 0;
×
271
        }
272

273
        if (!sd_json_variant_is_string(variant))
7,509✔
274
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
275

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

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

285
                m = mangle_gecos(n);
×
286
                if (!m)
×
287
                        return json_log_oom(variant, flags);
×
288

289
                free_and_replace(*s, m);
×
290
        }
291

292
        return 0;
293
}
294

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

299
        if (sd_json_variant_is_null(variant)) {
169✔
300
                *nl = INT_MAX;
×
301
                return 0;
×
302
        }
303

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

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

311
        *nl = m;
169✔
312
        return 0;
169✔
313
}
314

315
static int json_dispatch_rlimit_value(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
316
        rlim_t *ret = userdata;
×
317

318
        if (sd_json_variant_is_null(variant))
×
319
                *ret = RLIM_INFINITY;
×
320
        else if (sd_json_variant_is_unsigned(variant)) {
×
321
                uint64_t w;
×
322

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

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

331
        return 0;
332
}
333

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

340
        assert_se(limits);
×
341

342
        if (sd_json_variant_is_null(variant)) {
×
343
                rlimit_free_all(limits);
×
344
                return 0;
×
345
        }
346

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

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

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

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

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

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

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

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

393
        return 0;
×
394
}
395

396
static int json_dispatch_filename_or_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
14,685✔
397
        char **s = ASSERT_PTR(userdata);
14,685✔
398
        const char *n;
14,685✔
399
        int r;
14,685✔
400

401
        if (sd_json_variant_is_null(variant)) {
14,685✔
402
                *s = mfree(*s);
×
403
                return 0;
×
404
        }
405

406
        if (!sd_json_variant_is_string(variant))
14,685✔
407
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
408

409
        n = sd_json_variant_string(variant);
14,685✔
410
        if (!filename_is_valid(n) && !path_is_normalized(n))
14,685✔
411
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid file name or normalized path.", strna(name));
×
412

413
        r = free_and_strdup(s, n);
14,685✔
414
        if (r < 0)
14,685✔
415
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
416

417
        return 0;
418
}
419

420
static int json_dispatch_home_directory(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
5,060✔
421
        char **s = userdata;
5,060✔
422
        const char *n;
5,060✔
423
        int r;
5,060✔
424

425
        if (sd_json_variant_is_null(variant)) {
5,060✔
426
                *s = mfree(*s);
×
427
                return 0;
×
428
        }
429

430
        if (!sd_json_variant_is_string(variant))
5,060✔
431
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
432

433
        n = sd_json_variant_string(variant);
5,060✔
434
        if (!valid_home(n))
5,060✔
435
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid home directory path.", strna(name));
×
436

437
        r = free_and_strdup(s, n);
5,060✔
438
        if (r < 0)
5,060✔
439
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
440

441
        return 0;
442
}
443

444
static int json_dispatch_image_path(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,714✔
445
        char **s = userdata;
1,714✔
446
        const char *n;
1,714✔
447
        int r;
1,714✔
448

449
        if (sd_json_variant_is_null(variant)) {
1,714✔
450
                *s = mfree(*s);
×
451
                return 0;
×
452
        }
453

454
        if (!sd_json_variant_is_string(variant))
1,714✔
455
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
456

457
        n = sd_json_variant_string(variant);
1,714✔
458
        if (empty_or_root(n) || !path_is_valid(n) || !path_is_absolute(n))
5,142✔
459
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid image path.", strna(name));
×
460

461
        r = free_and_strdup(s, n);
1,714✔
462
        if (r < 0)
1,714✔
463
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
464

465
        return 0;
466
}
467

468
static int json_dispatch_locale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
469
        char **s = userdata;
×
470
        const char *n;
×
471
        int r;
×
472

473
        if (sd_json_variant_is_null(variant)) {
×
474
                *s = mfree(*s);
×
475
                return 0;
×
476
        }
477

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

481
        n = sd_json_variant_string(variant);
×
482

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

486
        r = free_and_strdup(s, n);
×
487
        if (r < 0)
×
488
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
489

490
        return 0;
491
}
492

493
static int json_dispatch_locales(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
494
        _cleanup_strv_free_ char **n = NULL;
×
495
        char ***l = userdata;
×
496
        const char *locale;
×
497
        sd_json_variant *e;
×
498
        int r;
×
499

500
        if (sd_json_variant_is_null(variant)) {
×
501
                *l = strv_free(*l);
×
502
                return 0;
×
503
        }
504

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

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

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

516
                r = strv_extend(&n, locale);
×
517
                if (r < 0)
×
518
                        return json_log_oom(variant, flags);
×
519
        }
520

521
        return strv_free_and_replace(*l, n);
×
522
}
523

524
JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_disposition, UserDisposition, user_disposition_from_string);
20,883✔
525
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_storage, UserStorage, user_storage_from_string);
2,942✔
526

527
static int json_dispatch_tasks_or_memory_max(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
528
        uint64_t *limit = userdata, k;
×
529

530
        if (sd_json_variant_is_null(variant)) {
×
531
                *limit = UINT64_MAX;
×
532
                return 0;
×
533
        }
534

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

538
        k = sd_json_variant_unsigned(variant);
×
539
        if (k <= 0 || k >= UINT64_MAX)
×
540
                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
×
541
                                "JSON field '%s' is not in valid range %" PRIu64 "%s%" PRIu64 ".",
542
                                strna(name), (uint64_t) 1, glyph(GLYPH_ELLIPSIS), UINT64_MAX-1);
543

544
        *limit = k;
×
545
        return 0;
×
546
}
547

548
static int json_dispatch_weight(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
4✔
549
        uint64_t *weight = userdata, k;
4✔
550

551
        if (sd_json_variant_is_null(variant)) {
4✔
552
                *weight = UINT64_MAX;
×
553
                return 0;
×
554
        }
555

556
        if (!sd_json_variant_is_unsigned(variant))
4✔
557
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name));
×
558

559
        k = sd_json_variant_unsigned(variant);
4✔
560
        if (k < CGROUP_WEIGHT_MIN || k > CGROUP_WEIGHT_MAX)
4✔
561
                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
×
562
                                "JSON field '%s' is not in valid range %" PRIu64 "%s%" PRIu64 ".",
563
                                strna(name), CGROUP_WEIGHT_MIN,
564
                                glyph(GLYPH_ELLIPSIS), CGROUP_WEIGHT_MAX);
565

566
        *weight = k;
4✔
567
        return 0;
4✔
568
}
569

570
int json_dispatch_user_group_list(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
277✔
571
        char ***list = ASSERT_PTR(userdata);
277✔
572
        _cleanup_strv_free_ char **l = NULL;
277✔
573
        int r;
277✔
574

575
        if (!sd_json_variant_is_array(variant))
277✔
576
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
577

578
        sd_json_variant *e;
277✔
579
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
550✔
580
                if (!sd_json_variant_is_string(e))
273✔
581
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
×
582

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

586
                r = strv_extend(&l, sd_json_variant_string(e));
273✔
587
                if (r < 0)
273✔
588
                        return json_log(e, flags, r, "Failed to append array element: %m");
×
589
        }
590

591
        r = strv_extend_strv_consume(list, TAKE_PTR(l), /* filter_duplicates= */ true);
277✔
592
        if (r < 0)
277✔
593
                return json_log(variant, flags, r, "Failed to merge user/group arrays: %m");
×
594

595
        return 0;
596
}
597

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

600
        static const sd_json_dispatch_field secret_dispatch_table[] = {
149✔
601
                { "password",                                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,     offsetof(UserRecord, password),                                       0 },
602
                { "tokenPin",                                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
603
                { "pkcs11Pin",   /* legacy alias */             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
604
                { "pkcs11ProtectedAuthenticationPathPermitted", SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
605
                { "fido2UserPresencePermitted",                 SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate, offsetof(UserRecord, fido2_user_presence_permitted),                  0 },
606
                { "fido2UserVerificationPermitted",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate, offsetof(UserRecord, fido2_user_verification_permitted),              0 },
607
                {},
608
        };
609

610
        return sd_json_dispatch(variant, secret_dispatch_table, flags, userdata);
149✔
611
}
612

613
static int dispatch_pkcs11_uri(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
614
        char **s = userdata;
×
615
        const char *n;
×
616
        int r;
×
617

618
        if (sd_json_variant_is_null(variant)) {
×
619
                *s = mfree(*s);
×
620
                return 0;
×
621
        }
622

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

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

630
        r = free_and_strdup(s, n);
×
631
        if (r < 0)
×
632
                return json_log(variant, flags, r, "Failed to allocate string: %m");
×
633

634
        return 0;
635
}
636

637
static int dispatch_pkcs11_uri_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
638
        _cleanup_strv_free_ char **z = NULL;
×
639
        char ***l = userdata;
×
640
        sd_json_variant *e;
×
641
        int r;
×
642

643
        if (sd_json_variant_is_null(variant)) {
×
644
                *l = strv_free(*l);
×
645
                return 0;
×
646
        }
647

648
        if (sd_json_variant_is_string(variant)) {
×
649
                const char *n;
×
650

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

655
                z = strv_new(n);
×
656
                if (!z)
×
657
                        return log_oom();
×
658

659
        } else {
660

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

664
                JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
665
                        const char *n;
×
666

667
                        if (!sd_json_variant_is_string(e))
×
668
                                return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
×
669

670
                        n = sd_json_variant_string(e);
×
671
                        if (!pkcs11_uri_valid(n))
×
672
                                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);
×
673

674
                        r = strv_extend(&z, n);
×
675
                        if (r < 0)
×
676
                                return log_oom();
×
677
                }
678
        }
679

680
        strv_free_and_replace(*l, z);
×
681
        return 0;
×
682
}
683

684
static int dispatch_pkcs11_key_data(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
685
        Pkcs11EncryptedKey *k = userdata;
×
686
        size_t l;
×
687
        void *b;
×
688
        int r;
×
689

690
        if (sd_json_variant_is_null(variant)) {
×
691
                k->data = erase_and_free(k->data);
×
692
                k->size = 0;
×
693
                return 0;
×
694
        }
695

696
        r = sd_json_variant_unbase64(variant, &b, &l);
×
697
        if (r < 0)
×
698
                return json_log(variant, flags, r, "Failed to decode encrypted PKCS#11 key: %m");
×
699

700
        erase_and_free(k->data);
×
701
        k->data = b;
×
702
        k->size = l;
×
703

704
        return 0;
×
705
}
706

707
static int dispatch_pkcs11_key(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
708
        UserRecord *h = userdata;
×
709
        sd_json_variant *e;
×
710
        int r;
×
711

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

715
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
716
                static const sd_json_dispatch_field pkcs11_key_dispatch_table[] = {
×
717
                        { "uri",            SD_JSON_VARIANT_STRING, dispatch_pkcs11_uri,      offsetof(Pkcs11EncryptedKey, uri),             SD_JSON_MANDATORY },
718
                        { "data",           SD_JSON_VARIANT_STRING, dispatch_pkcs11_key_data, 0,                                             SD_JSON_MANDATORY },
719
                        { "hashedPassword", SD_JSON_VARIANT_STRING, sd_json_dispatch_string,  offsetof(Pkcs11EncryptedKey, hashed_password), SD_JSON_MANDATORY },
720
                        {},
721
                };
722

723
                if (!sd_json_variant_is_object(e))
×
724
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
×
725

726
                if (!GREEDY_REALLOC(h->pkcs11_encrypted_key, h->n_pkcs11_encrypted_key + 1))
×
727
                        return log_oom();
×
728

729
                Pkcs11EncryptedKey *k = h->pkcs11_encrypted_key + h->n_pkcs11_encrypted_key;
×
730
                *k = (Pkcs11EncryptedKey) {};
×
731

732
                r = sd_json_dispatch(e, pkcs11_key_dispatch_table, flags, k);
×
733
                if (r < 0) {
×
734
                        pkcs11_encrypted_key_done(k);
×
735
                        return r;
×
736
                }
737

738
                h->n_pkcs11_encrypted_key++;
×
739
        }
740

741
        return 0;
×
742
}
743

744
static int dispatch_fido2_hmac_credential(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
745
        Fido2HmacCredential *k = userdata;
×
746
        size_t l;
×
747
        void *b;
×
748
        int r;
×
749

750
        if (sd_json_variant_is_null(variant)) {
×
751
                k->id = mfree(k->id);
×
752
                k->size = 0;
×
753
                return 0;
×
754
        }
755

756
        r = sd_json_variant_unbase64(variant, &b, &l);
×
757
        if (r < 0)
×
758
                return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
×
759

760
        free_and_replace(k->id, b);
×
761
        k->size = l;
×
762

763
        return 0;
×
764
}
765

766
static int dispatch_fido2_hmac_credential_array(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
767
        UserRecord *h = userdata;
×
768
        sd_json_variant *e;
×
769
        int r;
×
770

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

774
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
775
                size_t l;
×
776
                void *b;
×
777

778
                if (!GREEDY_REALLOC(h->fido2_hmac_credential, h->n_fido2_hmac_credential + 1))
×
779
                        return log_oom();
×
780

781
                r = sd_json_variant_unbase64(e, &b, &l);
×
782
                if (r < 0)
×
783
                        return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
×
784

785
                h->fido2_hmac_credential[h->n_fido2_hmac_credential++] = (Fido2HmacCredential) {
×
786
                        .id = b,
787
                        .size = l,
788
                };
789
        }
790

791
        return 0;
×
792
}
793

794
static int dispatch_fido2_hmac_salt_value(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
795
        Fido2HmacSalt *k = userdata;
×
796
        size_t l;
×
797
        void *b;
×
798
        int r;
×
799

800
        if (sd_json_variant_is_null(variant)) {
×
801
                k->salt = erase_and_free(k->salt);
×
802
                k->salt_size = 0;
×
803
                return 0;
×
804
        }
805

806
        r = sd_json_variant_unbase64(variant, &b, &l);
×
807
        if (r < 0)
×
808
                return json_log(variant, flags, r, "Failed to decode FIDO2 salt: %m");
×
809

810
        erase_and_free(k->salt);
×
811
        k->salt = b;
×
812
        k->salt_size = l;
×
813

814
        return 0;
×
815
}
816

817
static int dispatch_fido2_hmac_salt(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
818
        UserRecord *h = userdata;
×
819
        sd_json_variant *e;
×
820
        int r;
×
821

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

825
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
826
                static const sd_json_dispatch_field fido2_hmac_salt_dispatch_table[] = {
×
827
                        { "credential",     SD_JSON_VARIANT_STRING,  dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential),      SD_JSON_MANDATORY },
828
                        { "salt",           SD_JSON_VARIANT_STRING,  dispatch_fido2_hmac_salt_value, 0,                                        SD_JSON_MANDATORY },
829
                        { "hashedPassword", SD_JSON_VARIANT_STRING,  sd_json_dispatch_string,        offsetof(Fido2HmacSalt, hashed_password), SD_JSON_MANDATORY },
830
                        { "up",             SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate,      offsetof(Fido2HmacSalt, up),              0                 },
831
                        { "uv",             SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate,      offsetof(Fido2HmacSalt, uv),              0                 },
832
                        { "clientPin",      SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate,      offsetof(Fido2HmacSalt, client_pin),      0                 },
833
                        {},
834
                };
835

836
                if (!sd_json_variant_is_object(e))
×
837
                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
×
838

839
                if (!GREEDY_REALLOC(h->fido2_hmac_salt, h->n_fido2_hmac_salt + 1))
×
840
                        return log_oom();
×
841

842
                Fido2HmacSalt *k = h->fido2_hmac_salt + h->n_fido2_hmac_salt;
×
843
                *k = (Fido2HmacSalt) {
×
844
                        .uv = -1,
845
                        .up = -1,
846
                        .client_pin = -1,
847
                };
848

849
                r = sd_json_dispatch(e, fido2_hmac_salt_dispatch_table, flags, k);
×
850
                if (r < 0) {
×
851
                        fido2_hmac_salt_done(k);
×
852
                        return r;
×
853
                }
854

855
                h->n_fido2_hmac_salt++;
×
856
        }
857

858
        return 0;
×
859
}
860

861
static int dispatch_recovery_key(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
862
        UserRecord *h = userdata;
×
863
        sd_json_variant *e;
×
864
        int r;
×
865

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

869
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
870
                static const sd_json_dispatch_field recovery_key_dispatch_table[] = {
×
871
                        { "type",           SD_JSON_VARIANT_STRING, sd_json_dispatch_string, 0,                                      SD_JSON_MANDATORY },
872
                        { "hashedPassword", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(RecoveryKey, hashed_password), SD_JSON_MANDATORY },
873
                        {},
874
                };
875

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

879
                if (!GREEDY_REALLOC(h->recovery_key, h->n_recovery_key + 1))
×
880
                        return log_oom();
×
881

882
                RecoveryKey *k = h->recovery_key + h->n_recovery_key;
×
883
                *k = (RecoveryKey) {};
×
884

885
                r = sd_json_dispatch(e, recovery_key_dispatch_table, flags, k);
×
886
                if (r < 0) {
×
887
                        recovery_key_done(k);
×
888
                        return r;
×
889
                }
890

891
                h->n_recovery_key++;
×
892
        }
893

894
        return 0;
×
895
}
896

897
static int dispatch_auto_resize_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
898
        AutoResizeMode *mode = userdata, m;
×
899

900
        assert_se(mode);
×
901

902
        if (sd_json_variant_is_null(variant)) {
×
903
                *mode = _AUTO_RESIZE_MODE_INVALID;
×
904
                return 0;
×
905
        }
906

907
        if (sd_json_variant_is_boolean(variant)) {
×
908
                *mode = sd_json_variant_boolean(variant) ? AUTO_RESIZE_SHRINK_AND_GROW : AUTO_RESIZE_OFF;
×
909
                return 0;
×
910
        }
911

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

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

919
        *mode = m;
×
920
        return 0;
×
921
}
922

923
static int dispatch_rebalance_weight(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
783✔
924
        uint64_t *rebalance_weight = userdata;
783✔
925
        uintmax_t u;
783✔
926

927
        assert_se(rebalance_weight);
783✔
928

929
        if (sd_json_variant_is_null(variant)) {
783✔
930
                *rebalance_weight = REBALANCE_WEIGHT_UNSET;
×
931
                return 0;
×
932
        }
933

934
        if (sd_json_variant_is_boolean(variant)) {
783✔
935
                *rebalance_weight = sd_json_variant_boolean(variant) ? REBALANCE_WEIGHT_DEFAULT : REBALANCE_WEIGHT_OFF;
×
936
                return 0;
×
937
        }
938

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

942
        u = sd_json_variant_unsigned(variant);
783✔
943
        if (u >= REBALANCE_WEIGHT_MIN && u <= REBALANCE_WEIGHT_MAX)
783✔
944
                *rebalance_weight = (uint64_t) u;
×
945
        else if (u == 0)
783✔
946
                *rebalance_weight = REBALANCE_WEIGHT_OFF;
783✔
947
        else
948
                return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
×
949
                                "Rebalance weight is out of valid range %" PRIu64 "%s%" PRIu64 ".",
950
                                REBALANCE_WEIGHT_MIN, glyph(GLYPH_ELLIPSIS), REBALANCE_WEIGHT_MAX);
951

952
        return 0;
953
}
954

955
static int dispatch_tmpfs_limit(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
442✔
956
        TmpfsLimit *limit = ASSERT_PTR(userdata);
442✔
957
        int r;
442✔
958

959
        if (sd_json_variant_is_null(variant)) {
442✔
960
                *limit = TMPFS_LIMIT_NULL;
×
961
                return 0;
×
962
        }
963

964
        r = sd_json_dispatch_uint64(name, variant, flags, &limit->limit);
442✔
965
        if (r < 0)
442✔
966
                return r;
967

968
        limit->is_set = true;
442✔
969
        return 0;
442✔
970
}
971

972
static int dispatch_tmpfs_limit_scale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
973
        TmpfsLimit *limit = ASSERT_PTR(userdata);
×
974
        int r;
×
975

976
        if (sd_json_variant_is_null(variant)) {
×
977
                *limit = TMPFS_LIMIT_NULL;
×
978
                return 0;
×
979
        }
980

981
        r = sd_json_dispatch_uint32(name, variant, flags, &limit->limit_scale);
×
982
        if (r < 0)
×
983
                return r;
984

985
        limit->is_set = true;
×
986
        return 0;
×
987
}
988

989
static int dispatch_privileged(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
12,187✔
990

991
        static const sd_json_dispatch_field privileged_dispatch_table[] = {
12,187✔
992
                { "passwordHint",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,  offsetof(UserRecord, password_hint),        0              },
993
                { "hashedPassword",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,    offsetof(UserRecord, hashed_password),      SD_JSON_STRICT },
994
                { "sshAuthorizedKeys",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv,    offsetof(UserRecord, ssh_authorized_keys),  0              },
995
                { "pkcs11EncryptedKey", SD_JSON_VARIANT_ARRAY,         dispatch_pkcs11_key,      0,                                          0              },
996
                { "fido2HmacSalt",      SD_JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_salt, 0,                                          0              },
997
                { "recoveryKey",        SD_JSON_VARIANT_ARRAY,         dispatch_recovery_key,    0,                                          0              },
998
                {},
999
        };
1000

1001
        return sd_json_dispatch(variant, privileged_dispatch_table, flags, userdata);
12,187✔
1002
}
1003

1004
static int dispatch_binding(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
18,437✔
1005

1006
        static const sd_json_dispatch_field binding_dispatch_table[] = {
18,437✔
1007
                { "blobDirectory",     SD_JSON_VARIANT_STRING,        json_dispatch_path,           offsetof(UserRecord, blob_directory),       SD_JSON_STRICT },
1008
                { "imagePath",         SD_JSON_VARIANT_STRING,        json_dispatch_image_path,     offsetof(UserRecord, image_path),           0              },
1009
                { "homeDirectory",     SD_JSON_VARIANT_STRING,        json_dispatch_home_directory, offsetof(UserRecord, home_directory),       0              },
1010
                { "partitionUuid",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,       offsetof(UserRecord, partition_uuid),       0              },
1011
                { "luksUuid",          SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,       offsetof(UserRecord, luks_uuid),            0              },
1012
                { "fileSystemUuid",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,       offsetof(UserRecord, file_system_uuid),     0              },
1013
                { "uid",               SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,     offsetof(UserRecord, uid),                  0              },
1014
                { "gid",               SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,     offsetof(UserRecord, gid),                  0              },
1015
                { "storage",           SD_JSON_VARIANT_STRING,        json_dispatch_user_storage,   offsetof(UserRecord, storage),              0              },
1016
                { "fileSystemType",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,      offsetof(UserRecord, file_system_type),     SD_JSON_STRICT },
1017
                { "luksCipher",        SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,      offsetof(UserRecord, luks_cipher),          SD_JSON_STRICT },
1018
                { "luksCipherMode",    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,      offsetof(UserRecord, luks_cipher_mode),     SD_JSON_STRICT },
1019
                { "luksVolumeKeySize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,      offsetof(UserRecord, luks_volume_key_size), 0              },
1020
                {},
1021
        };
1022

1023
        sd_json_variant *m;
18,437✔
1024
        sd_id128_t mid;
18,437✔
1025
        int r;
18,437✔
1026

1027
        if (!variant)
18,437✔
1028
                return 0;
18,437✔
1029

1030
        if (!sd_json_variant_is_object(variant))
1,747✔
1031
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
1032

1033
        r = sd_id128_get_machine(&mid);
1,747✔
1034
        if (r < 0)
1,747✔
1035
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
1036

1037
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
1,747✔
1038
        if (!m)
1,747✔
1039
                return 0;
1040

1041
        return sd_json_dispatch(m, binding_dispatch_table, flags, userdata);
1,747✔
1042
}
1043

1044
static int dispatch_blob_manifest(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
166✔
1045
        _cleanup_hashmap_free_ Hashmap *manifest = NULL;
166✔
1046
        Hashmap **ret = ASSERT_PTR(userdata);
166✔
1047
        sd_json_variant *value;
166✔
1048
        const char *key;
166✔
1049
        int r;
166✔
1050

1051
        if (!variant)
166✔
1052
                return 0;
1053

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

1057
        JSON_VARIANT_OBJECT_FOREACH(key, value, variant) {
528✔
1058
                _cleanup_free_ char *filename = NULL;
×
1059
                _cleanup_free_ uint8_t *hash = NULL;
362✔
1060

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

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

1067
                filename = strdup(key);
362✔
1068
                if (!filename)
362✔
1069
                        return json_log_oom(value, flags);
×
1070

1071
                hash = malloc(SHA256_DIGEST_SIZE);
362✔
1072
                if (!hash)
362✔
1073
                        return json_log_oom(value, flags);
×
1074

1075
                r = parse_sha256(sd_json_variant_string(value), hash);
362✔
1076
                if (r < 0)
362✔
1077
                        return json_log(value, flags, r, "Blob entry '%s' has invalid hash: %s", filename, sd_json_variant_string(value));
×
1078

1079
                r = hashmap_ensure_put(&manifest, &path_hash_ops_free_free, filename, hash);
362✔
1080
                if (r < 0)
362✔
1081
                        return json_log(value, flags, r, "Failed to insert blob manifest entry '%s': %m", filename);
×
1082
                TAKE_PTR(filename); /* Ownership transfers to hashmap */
362✔
1083
                TAKE_PTR(hash);
362✔
1084
        }
1085

1086
        hashmap_free_and_replace(*ret, manifest);
166✔
1087
        return 0;
166✔
1088
}
1089

1090
int per_machine_id_match(sd_json_variant *ids, sd_json_dispatch_flags_t flags) {
2,005✔
1091
        sd_id128_t mid;
2,005✔
1092
        int r;
2,005✔
1093

1094
        assert(ids);
2,005✔
1095

1096
        r = sd_id128_get_machine(&mid);
2,005✔
1097
        if (r < 0)
2,005✔
1098
                return json_log(ids, flags, r, "Failed to acquire machine ID: %m");
×
1099

1100
        if (sd_json_variant_is_string(ids)) {
2,005✔
1101
                sd_id128_t k;
2,005✔
1102

1103
                r = sd_id128_from_string(sd_json_variant_string(ids), &k);
2,005✔
1104
                if (r < 0) {
2,005✔
1105
                        json_log(ids, flags, r, "%s is not a valid machine ID, ignoring: %m", sd_json_variant_string(ids));
×
1106
                        return 0;
2,005✔
1107
                }
1108

1109
                return sd_id128_equal(mid, k);
4,010✔
1110
        }
1111

1112
        if (sd_json_variant_is_array(ids)) {
×
1113
                sd_json_variant *e;
×
1114

1115
                JSON_VARIANT_ARRAY_FOREACH(e, ids) {
×
1116
                        sd_id128_t k;
×
1117

1118
                        if (!sd_json_variant_is_string(e)) {
×
1119
                                json_log(e, flags, 0, "Machine ID is not a string, ignoring: %m");
×
1120
                                continue;
×
1121
                        }
1122

1123
                        r = sd_id128_from_string(sd_json_variant_string(e), &k);
×
1124
                        if (r < 0) {
×
1125
                                json_log(e, flags, r, "%s is not a valid machine ID, ignoring: %m", sd_json_variant_string(e));
×
1126
                                continue;
×
1127
                        }
1128

1129
                        if (sd_id128_equal(mid, k))
×
1130
                                return true;
×
1131
                }
1132

1133
                return false;
×
1134
        }
1135

1136
        json_log(ids, flags, 0, "Machine ID is not a string or array of strings, ignoring: %m");
×
1137
        return false;
1138
}
1139

1140
int per_machine_hostname_match(sd_json_variant *hns, sd_json_dispatch_flags_t flags) {
×
1141
        _cleanup_free_ char *hn = NULL;
×
1142
        int r;
×
1143

1144
        assert(hns);
×
1145

1146
        r = gethostname_strict(&hn);
×
1147
        if (r == -ENXIO) {
×
1148
                json_log(hns, flags, r, "No hostname set, not matching perMachine hostname record: %m");
×
1149
                return false;
×
1150
        }
1151
        if (r < 0)
×
1152
                return json_log(hns, flags, r, "Failed to acquire hostname: %m");
×
1153

1154
        if (sd_json_variant_is_string(hns))
×
1155
                return streq(sd_json_variant_string(hns), hn);
×
1156

1157
        if (sd_json_variant_is_array(hns)) {
×
1158
                sd_json_variant *e;
×
1159

1160
                JSON_VARIANT_ARRAY_FOREACH(e, hns) {
×
1161

1162
                        if (!sd_json_variant_is_string(e)) {
×
1163
                                json_log(e, flags, 0, "Hostname is not a string, ignoring: %m");
×
1164
                                continue;
×
1165
                        }
1166

1167
                        if (streq(sd_json_variant_string(e), hn))
×
1168
                                return true;
×
1169
                }
1170

1171
                return false;
×
1172
        }
1173

1174
        json_log(hns, flags, 0, "Hostname is not a string or array of strings, ignoring: %m");
×
1175
        return false;
1176
}
1177

1178
int per_machine_match(sd_json_variant *entry, sd_json_dispatch_flags_t flags) {
2,005✔
1179
        sd_json_variant *m;
2,005✔
1180
        int r;
2,005✔
1181

1182
        assert(sd_json_variant_is_object(entry));
2,005✔
1183

1184
        m = sd_json_variant_by_key(entry, "matchMachineId");
2,005✔
1185
        if (m) {
2,005✔
1186
                r = per_machine_id_match(m, flags);
1,923✔
1187
                if (r < 0)
1,923✔
1188
                        return r;
1189
                if (r > 0)
1,923✔
1190
                        return true;
1191
        }
1192

1193
        m = sd_json_variant_by_key(entry, "matchNotMachineId");
82✔
1194
        if (m) {
82✔
1195
                r = per_machine_id_match(m, flags);
82✔
1196
                if (r < 0)
82✔
1197
                        return r;
1198
                if (r == 0)
82✔
1199
                        return true;
1200
        }
1201

1202
        m = sd_json_variant_by_key(entry, "matchHostname");
82✔
1203
        if (m) {
82✔
1204
                r = per_machine_hostname_match(m, flags);
×
1205
                if (r < 0)
×
1206
                        return r;
1207
                if (r > 0)
×
1208
                        return true;
1209
        }
1210

1211
        m = sd_json_variant_by_key(entry, "matchNotHostname");
82✔
1212
        if (m) {
82✔
1213
                r = per_machine_hostname_match(m, flags);
×
1214
                if (r < 0)
×
1215
                        return r;
1216
                if (r == 0)
×
1217
                        return true;
×
1218
        }
1219

1220
        return false;
1221
}
1222

1223
static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
18,437✔
1224

1225
        static const sd_json_dispatch_field per_machine_dispatch_table[] = {
18,437✔
1226
                { "matchMachineId",             _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1227
                { "matchNotMachineId",          _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1228
                { "matchHostname",              _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1229
                { "matchNotHostname",           _SD_JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0              },
1230
                { "blobDirectory",              SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, blob_directory),                SD_JSON_STRICT },
1231
                { "blobManifest",               SD_JSON_VARIANT_OBJECT,        dispatch_blob_manifest,               offsetof(UserRecord, blob_manifest),                 0              },
1232
                { "iconName",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, icon_name),                     SD_JSON_STRICT },
1233
                { "location",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, location),                      0              },
1234
                { "shell",                      SD_JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0              },
1235
                { "umask",                      _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode,            offsetof(UserRecord, umask),                         SD_JSON_STRICT },
1236
                { "environment",                SD_JSON_VARIANT_ARRAY,         json_dispatch_strv_environment,       offsetof(UserRecord, environment),                   0              },
1237
                { "timeZone",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, time_zone),                     SD_JSON_STRICT },
1238
                { "preferredLanguage",          SD_JSON_VARIANT_STRING,        json_dispatch_locale,                 offsetof(UserRecord, preferred_language),            0              },
1239
                { "additionalLanguages",        SD_JSON_VARIANT_ARRAY,         json_dispatch_locales,                offsetof(UserRecord, additional_languages),          0              },
1240
                { "niceLevel",                  _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0              },
1241
                { "resourceLimits",             _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0              },
1242
                { "locked",                     SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, locked),                        0              },
1243
                { "notBeforeUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0              },
1244
                { "notAfterUSec",               _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0              },
1245
                { "storage",                    SD_JSON_VARIANT_STRING,        json_dispatch_user_storage,           offsetof(UserRecord, storage),                       0              },
1246
                { "diskSize",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size),                     0              },
1247
                { "diskSizeRelative",           _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0              },
1248
                { "skeletonDirectory",          SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            SD_JSON_STRICT },
1249
                { "accessMode",                 _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0              },
1250
                { "tasksMax",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0              },
1251
                { "memoryHigh",                 SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0              },
1252
                { "memoryMax",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0              },
1253
                { "cpuWeight",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0              },
1254
                { "ioWeight",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0              },
1255
                { "mountNoDevices",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nodev),                         0              },
1256
                { "mountNoSuid",                SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nosuid),                        0              },
1257
                { "mountNoExecute",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, noexec),                        0              },
1258
                { "cifsDomain",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   SD_JSON_STRICT },
1259
                { "cifsUserName",               SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                SD_JSON_STRICT },
1260
                { "cifsService",                SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_service),                  SD_JSON_STRICT },
1261
                { "cifsExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_extra_mount_options),      0              },
1262
                { "imagePath",                  SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    SD_JSON_STRICT },
1263
                { "uid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0              },
1264
                { "gid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0              },
1265
                { "memberOf",                   SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     SD_JSON_RELAX  },
1266
                { "capabilityBoundingSet",      SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_bounding_set),       SD_JSON_STRICT },
1267
                { "capabilityAmbientSet",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_ambient_set),        SD_JSON_STRICT },
1268
                { "fileSystemType",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, file_system_type),              SD_JSON_STRICT },
1269
                { "partitionUuid",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0              },
1270
                { "luksUuid",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0              },
1271
                { "fileSystemUuid",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0              },
1272
                { "luksDiscard",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0,             },
1273
                { "luksOfflineDiscard",         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_offline_discard),          0,             },
1274
                { "luksCipher",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   SD_JSON_STRICT },
1275
                { "luksCipherMode",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              SD_JSON_STRICT },
1276
                { "luksVolumeKeySize",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0              },
1277
                { "luksPbkdfHashAlgorithm",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     SD_JSON_STRICT },
1278
                { "luksPbkdfType",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               SD_JSON_STRICT },
1279
                { "luksPbkdfForceIterations",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_force_iterations),   0              },
1280
                { "luksPbkdfTimeCostUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0              },
1281
                { "luksPbkdfMemoryCost",        _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0              },
1282
                { "luksPbkdfParallelThreads",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0              },
1283
                { "luksSectorSize",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_sector_size),              0              },
1284
                { "luksExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_extra_mount_options),      0              },
1285
                { "dropCaches",                 SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, drop_caches),                   0              },
1286
                { "autoResizeMode",             _SD_JSON_VARIANT_TYPE_INVALID, dispatch_auto_resize_mode,            offsetof(UserRecord, auto_resize_mode),              0              },
1287
                { "rebalanceWeight",            _SD_JSON_VARIANT_TYPE_INVALID, dispatch_rebalance_weight,            offsetof(UserRecord, rebalance_weight),              0              },
1288
                { "rateLimitIntervalUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0              },
1289
                { "rateLimitBurst",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0              },
1290
                { "enforcePasswordPolicy",      SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0              },
1291
                { "autoLogin",                  SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0              },
1292
                { "preferredSessionType",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_type),        SD_JSON_STRICT },
1293
                { "preferredSessionLauncher",   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_launcher),    SD_JSON_STRICT },
1294
                { "stopDelayUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0              },
1295
                { "killProcesses",              SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0              },
1296
                { "passwordChangeMinUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0              },
1297
                { "passwordChangeMaxUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0              },
1298
                { "passwordChangeWarnUSec",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0              },
1299
                { "passwordChangeInactiveUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0              },
1300
                { "passwordChangeNow",          SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0              },
1301
                { "pkcs11TokenUri",             SD_JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0              },
1302
                { "fido2HmacCredential",        SD_JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0              },
1303
                { "selfModifiableFields",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_fields),        SD_JSON_STRICT },
1304
                { "selfModifiableBlobs",        SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_blobs),         SD_JSON_STRICT },
1305
                { "selfModifiablePrivileged",   SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_privileged),    SD_JSON_STRICT },
1306
                { "tmpLimit",                   _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, tmp_limit),                     0,             },
1307
                { "tmpLimitScale",              _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, tmp_limit),                     0,             },
1308
                { "devShmLimit",                _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, dev_shm_limit),                 0,             },
1309
                { "devShmLimitScale",           _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, dev_shm_limit),                 0,             },
1310
                { "defaultArea",                SD_JSON_VARIANT_STRING,        json_dispatch_filename,               offsetof(UserRecord, default_area),                  0              },
1311
                {},
1312
        };
1313

1314
        sd_json_variant *e;
18,437✔
1315
        int r;
18,437✔
1316

1317
        if (!variant)
18,437✔
1318
                return 0;
1319

1320
        if (!sd_json_variant_is_array(variant))
1,895✔
1321
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
×
1322

1323
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
3,865✔
1324
                if (!sd_json_variant_is_object(e))
1,970✔
1325
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
×
1326

1327
                r = per_machine_match(e, flags);
1,970✔
1328
                if (r < 0)
1,970✔
1329
                        return r;
1330
                if (r == 0)
1,970✔
1331
                        continue;
75✔
1332

1333
                r = sd_json_dispatch(e, per_machine_dispatch_table, flags, userdata);
1,895✔
1334
                if (r < 0)
1,895✔
1335
                        return r;
1336
        }
1337

1338
        return 0;
1,895✔
1339
}
1340

1341
static int dispatch_status(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
18,437✔
1342

1343
        static const sd_json_dispatch_field status_dispatch_table[] = {
18,437✔
1344
                { "diskUsage",                  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_usage),                    0              },
1345
                { "diskFree",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_free),                     0              },
1346
                { "diskSize",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_size),                     0              },
1347
                { "diskCeiling",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_ceiling),                  0              },
1348
                { "diskFloor",                  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, disk_floor),                    0              },
1349
                { "state",                      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(UserRecord, state),                         SD_JSON_STRICT },
1350
                { "service",                    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(UserRecord, service),                       SD_JSON_STRICT },
1351
                { "signedLocally",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,      offsetof(UserRecord, signed_locally),                0              },
1352
                { "goodAuthenticationCounter",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, good_authentication_counter),   0              },
1353
                { "badAuthenticationCounter",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, bad_authentication_counter),    0              },
1354
                { "lastGoodAuthenticationUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, last_good_authentication_usec), 0              },
1355
                { "lastBadAuthenticationUSec",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, last_bad_authentication_usec),  0              },
1356
                { "rateLimitBeginUSec",         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, ratelimit_begin_usec),          0              },
1357
                { "rateLimitCount",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(UserRecord, ratelimit_count),               0              },
1358
                { "removable",                  SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,      offsetof(UserRecord, removable),                     0              },
1359
                { "accessMode",                 _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode,      offsetof(UserRecord, access_mode),                   0              },
1360
                { "fileSystemType",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(UserRecord, file_system_type),              SD_JSON_STRICT },
1361
                { "fallbackShell",              SD_JSON_VARIANT_STRING,        json_dispatch_filename_or_path, offsetof(UserRecord, fallback_shell),                0              },
1362
                { "fallbackHomeDirectory",      SD_JSON_VARIANT_STRING,        json_dispatch_home_directory,   offsetof(UserRecord, fallback_home_directory),       0              },
1363
                { "useFallback",                SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,       offsetof(UserRecord, use_fallback),                  0              },
1364
                { "defaultArea",                SD_JSON_VARIANT_STRING,        json_dispatch_filename,         offsetof(UserRecord, default_area),                  0              },
1365
                { "aliases",                    SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(UserRecord, aliases),                       SD_JSON_RELAX  },
1366
                {},
1367
        };
1368

1369
        sd_json_variant *m;
18,437✔
1370
        sd_id128_t mid;
18,437✔
1371
        int r;
18,437✔
1372

1373
        if (!variant)
18,437✔
1374
                return 0;
18,437✔
1375

1376
        if (!sd_json_variant_is_object(variant))
5,398✔
1377
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
1378

1379
        r = sd_id128_get_machine(&mid);
5,398✔
1380
        if (r < 0)
5,398✔
1381
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
1382

1383
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
5,398✔
1384
        if (!m)
5,398✔
1385
                return 0;
1386

1387
        return sd_json_dispatch(m, status_dispatch_table, flags, userdata);
5,398✔
1388
}
1389

1390
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret) {
11,571✔
1391
        const char *suffix;
11,571✔
1392
        char *z;
11,571✔
1393

1394
        assert(storage >= 0);
11,571✔
1395
        assert(user_name_and_realm);
11,571✔
1396
        assert(ret);
11,571✔
1397

1398
        if (storage == USER_LUKS)
11,571✔
1399
                suffix = ".home";
1400
        else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
11,571✔
1401
                suffix = ".homedir";
1402
        else {
1403
                *ret = NULL;
11,433✔
1404
                return 0;
11,433✔
1405
        }
1406

1407
        z = strjoin(get_home_root(), "/", user_name_and_realm, suffix);
138✔
1408
        if (!z)
138✔
1409
                return -ENOMEM;
1410

1411
        *ret = path_simplify(z);
138✔
1412
        return 1;
138✔
1413
}
1414

1415
static int user_record_augment(UserRecord *h, sd_json_dispatch_flags_t json_flags) {
18,437✔
1416
        int r;
18,437✔
1417

1418
        assert(h);
18,437✔
1419

1420
        if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
18,437✔
1421
                return 0;
1422

1423
        assert(h->user_name);
18,256✔
1424

1425
        if (!h->user_name_and_realm_auto && h->realm) {
18,256✔
1426
                h->user_name_and_realm_auto = strjoin(h->user_name, "@", h->realm);
58✔
1427
                if (!h->user_name_and_realm_auto)
58✔
1428
                        return json_log_oom(h->json, json_flags);
×
1429
        }
1430

1431
        /* Let's add in the following automatisms only for regular users, they don't make sense for any others */
1432
        if (user_record_disposition(h) != USER_REGULAR)
18,256✔
1433
                return 0;
1434

1435
        if (!h->home_directory && !h->home_directory_auto) {
13,391✔
1436
                h->home_directory_auto = path_join(get_home_root(), h->user_name);
11,678✔
1437
                if (!h->home_directory_auto)
11,678✔
1438
                        return json_log_oom(h->json, json_flags);
×
1439
        }
1440

1441
        if (!h->image_path && !h->image_path_auto) {
13,391✔
1442
                r = user_record_build_image_path(user_record_storage(h), user_record_user_name_and_realm(h), &h->image_path_auto);
11,569✔
1443
                if (r < 0)
11,569✔
1444
                        return json_log(h->json, json_flags, r, "Failed to determine default image path: %m");
×
1445
        }
1446

1447
        return 0;
1448
}
1449

1450
int user_group_record_mangle(
28,382✔
1451
                sd_json_variant *v,
1452
                UserRecordLoadFlags load_flags,
1453
                sd_json_variant **ret_variant,
1454
                UserRecordMask *ret_mask) {
1455

1456
        static const struct {
28,382✔
1457
                UserRecordMask mask;
1458
                const char *name;
1459
        } mask_field[] = {
1460
                { USER_RECORD_PRIVILEGED,  "privileged" },
1461
                { USER_RECORD_SECRET,      "secret"     },
1462
                { USER_RECORD_BINDING,     "binding"    },
1463
                { USER_RECORD_PER_MACHINE, "perMachine" },
1464
                { USER_RECORD_STATUS,      "status"     },
1465
                { USER_RECORD_SIGNATURE,   "signature"  },
1466
        };
1467

1468
        sd_json_dispatch_flags_t json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
28,382✔
1469
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL;
28,382✔
1470
        sd_json_variant *array[ELEMENTSOF(mask_field) * 2];
28,382✔
1471
        size_t n_retain = 0;
28,382✔
1472
        UserRecordMask m = 0;
28,382✔
1473
        int r;
28,382✔
1474

1475
        assert((load_flags & _USER_RECORD_MASK_MAX) == 0); /* detect mistakes when accidentally passing
28,382✔
1476
                                                            * UserRecordMask bit masks as UserRecordLoadFlags
1477
                                                            * value */
1478

1479
        assert(v);
28,382✔
1480
        assert(ret_variant);
28,382✔
1481

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

1485
        if (!sd_json_variant_is_object(v))
28,382✔
1486
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is not a JSON object, refusing.");
×
1487

1488
        if (USER_RECORD_ALLOW_MASK(load_flags) == 0) /* allow nothing? */
28,382✔
1489
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Nothing allowed in record, refusing.");
×
1490

1491
        if (USER_RECORD_STRIP_MASK(load_flags) == _USER_RECORD_MASK_MAX) /* strip everything? */
28,382✔
1492
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Stripping everything from record, refusing.");
×
1493

1494
        /* Extra safety: mark sensitive parts of the JSON as such, so that they are not included in debug
1495
         * output and erased from memory when we are done. We do this for any record that passes through here. */
1496
        FOREACH_STRING(key,
141,910✔
1497
                       /* This section contains literal passwords and such in plain text */
1498
                       "secret",
1499

1500
                       /* Personally Identifiable Information (PII) — avoid leaking in logs */
1501
                       "realName",
1502
                       "location",
1503
                       "emailAddress")
1504
                sd_json_variant_sensitive(sd_json_variant_by_key(v, key));
113,528✔
1505

1506
        /* Check if we have the special sections and if they match our flags set */
1507
        FOREACH_ELEMENT(i, mask_field) {
198,674✔
1508
                sd_json_variant *e, *k;
170,292✔
1509

1510
                if (FLAGS_SET(USER_RECORD_STRIP_MASK(load_flags), i->mask)) {
170,292✔
1511
                        if (!w)
12,437✔
1512
                                w = sd_json_variant_ref(v);
10,082✔
1513

1514
                        r = sd_json_variant_filter(&w, STRV_MAKE(i->name));
12,437✔
1515
                        if (r < 0)
12,437✔
1516
                                return json_log(w, json_flags, r, "Failed to remove field from variant: %m");
×
1517

1518
                        continue;
12,437✔
1519
                }
1520

1521
                e = sd_json_variant_by_key_full(v, i->name, &k);
157,855✔
1522
                if (e) {
157,855✔
1523
                        if (!FLAGS_SET(USER_RECORD_ALLOW_MASK(load_flags), i->mask))
27,101✔
1524
                                return json_log(e, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", i->name);
×
1525

1526
                        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
27,101✔
1527
                                array[n_retain++] = k;
146✔
1528
                                array[n_retain++] = e;
146✔
1529
                        }
1530

1531
                        m |= i->mask;
27,101✔
1532
                } else {
1533
                        if (FLAGS_SET(USER_RECORD_REQUIRE_MASK(load_flags), i->mask))
130,754✔
1534
                                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks '%s' field, which is required.", i->name);
×
1535
                }
1536
        }
1537

1538
        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
28,382✔
1539
                /* If we are supposed to strip regular items, then let's instead just allocate a new object
1540
                 * with just the stuff we need. */
1541

1542
                w = sd_json_variant_unref(w);
280✔
1543
                r = sd_json_variant_new_object(&w, array, n_retain);
280✔
1544
                if (r < 0)
280✔
1545
                        return json_log(v, json_flags, r, "Failed to allocate new object: %m");
×
1546
        } else
1547
                /* And now check if there's anything else in the record */
1548
                for (size_t i = 0; i < sd_json_variant_elements(v); i += 2) {
28,137✔
1549
                        const char *f;
28,102✔
1550
                        bool special = false;
28,102✔
1551

1552
                        assert_se(f = sd_json_variant_string(sd_json_variant_by_index(v, i)));
28,102✔
1553

1554
                        FOREACH_ELEMENT(j, mask_field)
196,714✔
1555
                                if (streq(f, j->name)) { /* already covered in the loop above */
168,612✔
1556
                                        special = true;
35✔
1557
                                        continue;
35✔
1558
                                }
1559

1560
                        if (!special) {
28,102✔
1561
                                if ((load_flags & (USER_RECORD_ALLOW_REGULAR|USER_RECORD_REQUIRE_REGULAR)) == 0)
28,067✔
1562
                                        return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", f);
×
1563

1564
                                m |= USER_RECORD_REGULAR;
28,067✔
1565
                                break;
28,067✔
1566
                        }
1567
                }
1568

1569
        if (FLAGS_SET(load_flags, USER_RECORD_REQUIRE_REGULAR) && !FLAGS_SET(m, USER_RECORD_REGULAR))
28,382✔
1570
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks basic identity fields, which are required.");
×
1571

1572
        if (!FLAGS_SET(load_flags, USER_RECORD_EMPTY_OK) && m == 0)
28,382✔
1573
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is empty.");
×
1574

1575
        if (w)
28,382✔
1576
                *ret_variant = TAKE_PTR(w);
10,082✔
1577
        else
1578
                *ret_variant = sd_json_variant_ref(v);
18,300✔
1579

1580
        if (ret_mask)
28,382✔
1581
                *ret_mask = m;
28,338✔
1582
        return 0;
1583
}
1584

1585
int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load_flags) {
18,437✔
1586

1587
        static const sd_json_dispatch_field user_dispatch_table[] = {
18,437✔
1588
                { "userName",                   SD_JSON_VARIANT_STRING,        json_dispatch_user_group_name,        offsetof(UserRecord, user_name),                     SD_JSON_RELAX  },
1589
                { "aliases",                    SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, aliases),                       SD_JSON_RELAX  },
1590
                { "realm",                      SD_JSON_VARIANT_STRING,        json_dispatch_realm,                  offsetof(UserRecord, realm),                         0              },
1591
                { "uuid",                       SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, uuid),                          0              },
1592
                { "blobDirectory",              SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, blob_directory),                SD_JSON_STRICT },
1593
                { "blobManifest",               SD_JSON_VARIANT_OBJECT,        dispatch_blob_manifest,               offsetof(UserRecord, blob_manifest),                 0              },
1594
                { "realName",                   SD_JSON_VARIANT_STRING,        json_dispatch_gecos,                  offsetof(UserRecord, real_name),                     0              },
1595
                { "emailAddress",               SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, email_address),                 SD_JSON_STRICT },
1596
                { "iconName",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, icon_name),                     SD_JSON_STRICT },
1597
                { "location",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, location),                      0              },
1598
                { "disposition",                SD_JSON_VARIANT_STRING,        json_dispatch_user_disposition,       offsetof(UserRecord, disposition),                   0              },
1599
                { "lastChangeUSec",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, last_change_usec),              0              },
1600
                { "lastPasswordChangeUSec",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, last_password_change_usec),     0              },
1601
                { "shell",                      SD_JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0              },
1602
                { "umask",                      _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode,            offsetof(UserRecord, umask),                         SD_JSON_STRICT },
1603
                { "environment",                SD_JSON_VARIANT_ARRAY,         json_dispatch_strv_environment,       offsetof(UserRecord, environment),                   0              },
1604
                { "timeZone",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, time_zone),                     SD_JSON_STRICT },
1605
                { "preferredLanguage",          SD_JSON_VARIANT_STRING,        json_dispatch_locale,                 offsetof(UserRecord, preferred_language),            0              },
1606
                { "additionalLanguages",        SD_JSON_VARIANT_ARRAY,         json_dispatch_locales,                offsetof(UserRecord, additional_languages),          0              },
1607
                { "niceLevel",                  _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0              },
1608
                { "resourceLimits",             _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0              },
1609
                { "locked",                     SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, locked),                        0              },
1610
                { "notBeforeUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0              },
1611
                { "notAfterUSec",               _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0              },
1612
                { "storage",                    SD_JSON_VARIANT_STRING,        json_dispatch_user_storage,           offsetof(UserRecord, storage),                       0              },
1613
                { "diskSize",                   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size),                     0              },
1614
                { "diskSizeRelative",           _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0              },
1615
                { "skeletonDirectory",          SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            SD_JSON_STRICT },
1616
                { "accessMode",                 _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0              },
1617
                { "tasksMax",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0              },
1618
                { "memoryHigh",                 SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0              },
1619
                { "memoryMax",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0              },
1620
                { "cpuWeight",                  SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0              },
1621
                { "ioWeight",                   SD_JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0              },
1622
                { "mountNoDevices",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nodev),                         0              },
1623
                { "mountNoSuid",                SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, nosuid),                        0              },
1624
                { "mountNoExecute",             SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,             offsetof(UserRecord, noexec),                        0              },
1625
                { "cifsDomain",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   SD_JSON_STRICT },
1626
                { "cifsUserName",               SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                SD_JSON_STRICT },
1627
                { "cifsService",                SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_service),                  SD_JSON_STRICT },
1628
                { "cifsExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, cifs_extra_mount_options),      0              },
1629
                { "imagePath",                  SD_JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    SD_JSON_STRICT },
1630
                { "homeDirectory",              SD_JSON_VARIANT_STRING,        json_dispatch_home_directory,         offsetof(UserRecord, home_directory),                0              },
1631
                { "uid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0              },
1632
                { "gid",                        SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0              },
1633
                { "memberOf",                   SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     SD_JSON_RELAX  },
1634
                { "capabilityBoundingSet",      SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_bounding_set),       SD_JSON_STRICT },
1635
                { "capabilityAmbientSet",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, capability_ambient_set),        SD_JSON_STRICT },
1636
                { "fileSystemType",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, file_system_type),              SD_JSON_STRICT },
1637
                { "partitionUuid",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0              },
1638
                { "luksUuid",                   SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0              },
1639
                { "fileSystemUuid",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0              },
1640
                { "luksDiscard",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0              },
1641
                { "luksOfflineDiscard",         _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_tristate,            offsetof(UserRecord, luks_offline_discard),          0              },
1642
                { "luksCipher",                 SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   SD_JSON_STRICT },
1643
                { "luksCipherMode",             SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              SD_JSON_STRICT },
1644
                { "luksVolumeKeySize",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0              },
1645
                { "luksPbkdfHashAlgorithm",     SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     SD_JSON_STRICT },
1646
                { "luksPbkdfType",              SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               SD_JSON_STRICT },
1647
                { "luksPbkdfForceIterations",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_force_iterations),   0              },
1648
                { "luksPbkdfTimeCostUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0              },
1649
                { "luksPbkdfMemoryCost",        _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0              },
1650
                { "luksPbkdfParallelThreads",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0              },
1651
                { "luksSectorSize",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, luks_sector_size),              0              },
1652
                { "luksExtraMountOptions",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, luks_extra_mount_options),      0              },
1653
                { "dropCaches",                 SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, drop_caches),                   0              },
1654
                { "autoResizeMode",             _SD_JSON_VARIANT_TYPE_INVALID, dispatch_auto_resize_mode,            offsetof(UserRecord, auto_resize_mode),              0              },
1655
                { "rebalanceWeight",            _SD_JSON_VARIANT_TYPE_INVALID, dispatch_rebalance_weight,            offsetof(UserRecord, rebalance_weight),              0              },
1656
                { "service",                    SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, service),                       SD_JSON_STRICT },
1657
                { "rateLimitIntervalUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0              },
1658
                { "rateLimitBurst",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0              },
1659
                { "enforcePasswordPolicy",      SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0              },
1660
                { "autoLogin",                  SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0              },
1661
                { "preferredSessionType",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_type),        SD_JSON_STRICT },
1662
                { "preferredSessionLauncher",   SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,              offsetof(UserRecord, preferred_session_launcher),    SD_JSON_STRICT },
1663
                { "stopDelayUSec",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0              },
1664
                { "killProcesses",              SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0              },
1665
                { "passwordChangeMinUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0              },
1666
                { "passwordChangeMaxUSec",      _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0              },
1667
                { "passwordChangeWarnUSec",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0              },
1668
                { "passwordChangeInactiveUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0              },
1669
                { "passwordChangeNow",          SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0              },
1670
                { "pkcs11TokenUri",             SD_JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0              },
1671
                { "fido2HmacCredential",        SD_JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0              },
1672
                { "recoveryKeyType",            SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, recovery_key_type),             0              },
1673
                { "selfModifiableFields",       SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_fields),        SD_JSON_STRICT },
1674
                { "selfModifiableBlobs",        SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_blobs),         SD_JSON_STRICT },
1675
                { "selfModifiablePrivileged",   SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,                offsetof(UserRecord, self_modifiable_privileged),    SD_JSON_STRICT },
1676
                { "tmpLimit",                   _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, tmp_limit),                     0,             },
1677
                { "tmpLimitScale",              _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, tmp_limit),                     0,             },
1678
                { "devShmLimit",                _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit,                 offsetof(UserRecord, dev_shm_limit),                 0,             },
1679
                { "devShmLimitScale",           _SD_JSON_VARIANT_TYPE_INVALID, dispatch_tmpfs_limit_scale,           offsetof(UserRecord, dev_shm_limit),                 0,             },
1680
                { "defaultArea",                SD_JSON_VARIANT_STRING,        json_dispatch_filename,               offsetof(UserRecord, default_area),                  0              },
1681

1682
                { "secret",                     SD_JSON_VARIANT_OBJECT,        dispatch_secret,                      0,                                                   0              },
1683
                { "privileged",                 SD_JSON_VARIANT_OBJECT,        dispatch_privileged,                  0,                                                   0              },
1684

1685
                /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */
1686
                { "perMachine",                 SD_JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0              },
1687
                { "binding",                    SD_JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0              },
1688
                { "status",                     SD_JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0              },
1689

1690
                /* Ignore 'signature', we check it with explicit accessors instead */
1691
                { "signature",                  SD_JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0              },
1692
                {},
1693
        };
1694

1695
        sd_json_dispatch_flags_t json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
18,437✔
1696
        int r;
18,437✔
1697

1698
        assert(h);
18,437✔
1699
        assert(!h->json);
18,437✔
1700

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

1703
        r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
18,437✔
1704
        if (r < 0)
18,437✔
1705
                return r;
1706

1707
        r = sd_json_dispatch(h->json, user_dispatch_table, json_flags | SD_JSON_ALLOW_EXTENSIONS, h);
18,437✔
1708
        if (r < 0)
18,437✔
1709
                return r;
1710

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

1714
        r = dispatch_per_machine("perMachine", sd_json_variant_by_key(h->json, "perMachine"), json_flags, h);
18,437✔
1715
        if (r < 0)
18,437✔
1716
                return r;
1717

1718
        r = dispatch_binding("binding", sd_json_variant_by_key(h->json, "binding"), json_flags, h);
18,437✔
1719
        if (r < 0)
18,437✔
1720
                return r;
1721

1722
        r = dispatch_status("status", sd_json_variant_by_key(h->json, "status"), json_flags, h);
18,437✔
1723
        if (r < 0)
18,437✔
1724
                return r;
1725

1726
        if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->user_name)
18,437✔
1727
                return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "User name field missing, refusing.");
×
1728

1729
        r = user_record_augment(h, json_flags);
18,437✔
1730
        if (r < 0)
18,437✔
1731
                return r;
×
1732

1733
        return 0;
1734
}
1735

1736
int user_record_build(UserRecord **ret, ...) {
3,004✔
1737
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
3,004✔
1738
        _cleanup_(user_record_unrefp) UserRecord *u = NULL;
3,004✔
1739
        va_list ap;
3,004✔
1740
        int r;
3,004✔
1741

1742
        assert(ret);
3,004✔
1743

1744
        va_start(ap, ret);
3,004✔
1745
        r = sd_json_buildv(&v, ap);
3,004✔
1746
        va_end(ap);
3,004✔
1747

1748
        if (r < 0)
3,004✔
1749
                return r;
1750

1751
        u = user_record_new();
3,004✔
1752
        if (!u)
3,004✔
1753
                return -ENOMEM;
1754

1755
        r = user_record_load(u, v, USER_RECORD_LOAD_FULL);
3,004✔
1756
        if (r < 0)
3,004✔
1757
                return r;
1758

1759
        *ret = TAKE_PTR(u);
3,004✔
1760
        return 0;
3,004✔
1761
}
1762

1763
const char* user_record_user_name_and_realm(UserRecord *h) {
11,748✔
1764
        assert(h);
11,748✔
1765

1766
        /* Return the pre-initialized joined string if it is defined */
1767
        if (h->user_name_and_realm_auto)
11,748✔
1768
                return h->user_name_and_realm_auto;
1769

1770
        /* If it's not defined then we cannot have a realm */
1771
        assert(!h->realm);
11,727✔
1772
        return h->user_name;
11,727✔
1773
}
1774

1775
UserStorage user_record_storage(UserRecord *h) {
25,339✔
1776
        assert(h);
25,339✔
1777

1778
        if (h->storage >= 0)
25,339✔
1779
                return h->storage;
2,416✔
1780

1781
        return USER_CLASSIC;
1782
}
1783

1784
const char* user_record_file_system_type(UserRecord *h) {
×
1785
        assert(h);
×
1786

1787
        return h->file_system_type ?: "btrfs";
×
1788
}
1789

1790
const char* user_record_skeleton_directory(UserRecord *h) {
144✔
1791
        assert(h);
144✔
1792

1793
        return h->skeleton_directory ?: "/etc/skel";
144✔
1794
}
1795

1796
mode_t user_record_access_mode(UserRecord *h) {
9✔
1797
        assert(h);
9✔
1798

1799
        return h->access_mode != MODE_INVALID ? h->access_mode : 0700;
9✔
1800
}
1801

1802
static const char *user_record_home_directory_real(UserRecord *h) {
5,322✔
1803
        assert(h);
5,322✔
1804

1805
        if (h->home_directory)
5,322✔
1806
                return h->home_directory;
1807
        if (h->home_directory_auto)
2,316✔
1808
                return h->home_directory_auto;
1809

1810
        /* The root user is special, hence be special about it */
1811
        if (user_record_is_root(h))
53✔
1812
                return "/root";
×
1813

1814
        return "/";
1815
}
1816

1817
const char* user_record_home_directory(UserRecord *h) {
5,402✔
1818
        assert(h);
5,402✔
1819

1820
        if (h->use_fallback && h->fallback_home_directory)
5,402✔
1821
                return h->fallback_home_directory;
1822

1823
        return user_record_home_directory_real(h);
5,262✔
1824
}
1825

1826
const char* user_record_image_path(UserRecord *h) {
1,174✔
1827
        assert(h);
1,174✔
1828

1829
        if (h->image_path)
1,174✔
1830
                return h->image_path;
1831
        if (h->image_path_auto)
113✔
1832
                return h->image_path_auto;
1833

1834
        /* For some storage types the image is the home directory itself. (But let's ignore the fallback logic for it) */
1835
        return IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT) ?
60✔
1836
                user_record_home_directory_real(h) : NULL;
60✔
1837
}
1838

1839
static bool user_record_image_is_blockdev(UserRecord *h) {
×
1840
        assert(h);
×
1841

1842
        const char *p = user_record_image_path(h);
×
1843
        if (!p)
×
1844
                return false;
1845

1846
        return path_startswith(p, "/dev/");
×
1847
}
1848

1849
const char* user_record_cifs_user_name(UserRecord *h) {
×
1850
        assert(h);
×
1851

1852
        return h->cifs_user_name ?: h->user_name;
×
1853
}
1854

1855
unsigned long user_record_mount_flags(UserRecord *h) {
42✔
1856
        assert(h);
42✔
1857

1858
        return (h->nosuid ? MS_NOSUID : 0) |
42✔
1859
                (h->noexec ? MS_NOEXEC : 0) |
42✔
1860
                (h->nodev ? MS_NODEV : 0);
42✔
1861
}
1862

1863
static const char *user_record_shell_real(UserRecord *h) {
2,302✔
1864
        assert(h);
2,302✔
1865

1866
        if (h->shell)
2,302✔
1867
                return h->shell;
1868

1869
        if (user_record_is_root(h))
406✔
1870
                return "/bin/sh";
1871

1872
        if (user_record_disposition(h) == USER_REGULAR)
401✔
1873
                return DEFAULT_USER_SHELL;
379✔
1874

1875
        return NOLOGIN;
1876
}
1877

1878
const char* user_record_shell(UserRecord *h) {
2,302✔
1879
        const char *shell;
2,302✔
1880

1881
        assert(h);
2,302✔
1882

1883
        shell = user_record_shell_real(h);
2,302✔
1884

1885
        /* Return fallback shall if we are told so — except if the primary shell is already a nologin shell,
1886
         * then let's not risk anything. */
1887
        if (h->use_fallback && h->fallback_shell)
2,302✔
1888
                return is_nologin_shell(shell) ? NOLOGIN : h->fallback_shell;
404✔
1889

1890
        return shell;
1891
}
1892

1893
const char* user_record_real_name(UserRecord *h) {
1,800✔
1894
        assert(h);
1,800✔
1895

1896
        return h->real_name ?: h->user_name;
1,800✔
1897
}
1898

1899
bool user_record_luks_discard(UserRecord *h) {
×
1900
        assert(h);
×
1901

1902
        if (h->luks_discard >= 0)
×
1903
                return h->luks_discard;
×
1904

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

1911
        return user_record_image_is_blockdev(h);
×
1912
}
1913

1914
bool user_record_luks_offline_discard(UserRecord *h) {
×
1915
        const char *ip;
×
1916

1917
        assert(h);
×
1918

1919
        if (h->luks_offline_discard >= 0)
×
1920
                return h->luks_offline_discard;
×
1921

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

1925
        ip = user_record_image_path(h);
×
1926
        if (!ip)
×
1927
                return false;
1928

1929
        if (path_startswith(ip, "/dev/"))
×
1930
                return user_record_luks_discard(h);
×
1931

1932
        return true;
1933
}
1934

1935
const char* user_record_luks_cipher(UserRecord *h) {
×
1936
        assert(h);
×
1937

1938
        return h->luks_cipher ?: "aes";
×
1939
}
1940

1941
const char* user_record_luks_cipher_mode(UserRecord *h) {
×
1942
        assert(h);
×
1943

1944
        return h->luks_cipher_mode ?: "xts-plain64";
×
1945
}
1946

1947
uint64_t user_record_luks_volume_key_size(UserRecord *h) {
×
1948
        assert(h);
×
1949

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

1952
        if (h->luks_volume_key_size == UINT64_MAX)
×
1953
                return 256 / 8;
×
1954

1955
        return MIN(h->luks_volume_key_size, SIZE_MAX);
1956
}
1957

1958
const char* user_record_luks_pbkdf_type(UserRecord *h) {
×
1959
        assert(h);
×
1960

1961
        return h->luks_pbkdf_type ?: "argon2id";
×
1962
}
1963

1964
uint64_t user_record_luks_pbkdf_force_iterations(UserRecord *h) {
×
1965
        assert(h);
×
1966

1967
        /* propagate default "benchmark" mode as itself */
1968
        if (h->luks_pbkdf_force_iterations == UINT64_MAX)
×
1969
                return UINT64_MAX;
1970

1971
        /* clamp everything else to actually accepted number of iterations of libcryptsetup */
1972
        return CLAMP(h->luks_pbkdf_force_iterations, 1U, UINT32_MAX);
×
1973
}
1974

1975
uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
×
1976
        assert(h);
×
1977

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

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

1983
        return MIN(DIV_ROUND_UP(h->luks_pbkdf_time_cost_usec, USEC_PER_MSEC), UINT32_MAX) * USEC_PER_MSEC;
×
1984
}
1985

1986
uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h) {
×
1987
        assert(h);
×
1988

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

1994
        return MIN(DIV_ROUND_UP(h->luks_pbkdf_memory_cost, 1024), UINT32_MAX) * 1024;
×
1995
}
1996

1997
uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h) {
×
1998
        assert(h);
×
1999

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

2004
        return MIN(h->luks_pbkdf_parallel_threads, UINT32_MAX);
×
2005
}
2006

2007
uint64_t user_record_luks_sector_size(UserRecord *h) {
×
2008
        assert(h);
×
2009

2010
        if (h->luks_sector_size == UINT64_MAX)
×
2011
                return 512;
2012

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

2017
const char* user_record_luks_pbkdf_hash_algorithm(UserRecord *h) {
×
2018
        assert(h);
×
2019

2020
        return h->luks_pbkdf_hash_algorithm ?: "sha512";
×
2021
}
2022

2023
gid_t user_record_gid(UserRecord *h) {
4,797✔
2024
        assert(h);
4,797✔
2025

2026
        if (gid_is_valid(h->gid))
4,797✔
2027
                return h->gid;
2028

2029
        return (gid_t) h->uid;
2,273✔
2030
}
2031

2032
UserDisposition user_record_disposition(UserRecord *h) {
26,896✔
2033
        assert(h);
26,896✔
2034

2035
        if (h->disposition >= 0)
26,896✔
2036
                return h->disposition;
2037

2038
        /* If not declared, derive from UID */
2039

2040
        if (!uid_is_valid(h->uid))
4,146✔
2041
                return _USER_DISPOSITION_INVALID;
2042

2043
        if (user_record_is_root(h) || user_record_is_nobody(h))
4,126✔
2044
                return USER_INTRINSIC;
1,624✔
2045

2046
        if (uid_is_system(h->uid))
2,502✔
2047
                return USER_SYSTEM;
2048

2049
        if (uid_is_dynamic(h->uid) || uid_is_greeter(h->uid))
336✔
2050
                return USER_DYNAMIC;
2051

2052
        if (uid_is_container(h->uid))
336✔
2053
                return USER_CONTAINER;
2054

2055
        if (uid_is_foreign(h->uid))
203✔
2056
                return USER_FOREIGN;
2057

2058
        if (h->uid > INT32_MAX)
203✔
2059
                return USER_RESERVED;
×
2060

2061
        return USER_REGULAR;
2062
}
2063

2064
int user_record_removable(UserRecord *h) {
11,676✔
2065
        UserStorage storage;
11,676✔
2066
        assert(h);
11,676✔
2067

2068
        if (h->removable >= 0)
11,676✔
2069
                return h->removable;
2070

2071
        /* Refuse to decide for classic records */
2072
        storage = user_record_storage(h);
11,676✔
2073
        if (h->storage < 0 || h->storage == USER_CLASSIC)
11,676✔
2074
                return -1;
2075

2076
        /* For now consider only LUKS home directories with a reference by path as removable */
2077
        return storage == USER_LUKS && user_record_image_is_blockdev(h);
369✔
2078
}
2079

2080
uint64_t user_record_ratelimit_interval_usec(UserRecord *h) {
130✔
2081
        assert(h);
130✔
2082

2083
        if (h->ratelimit_interval_usec == UINT64_MAX)
130✔
2084
                return DEFAULT_RATELIMIT_INTERVAL_USEC;
44✔
2085

2086
        return h->ratelimit_interval_usec;
2087
}
2088

2089
uint64_t user_record_ratelimit_burst(UserRecord *h) {
305✔
2090
        assert(h);
305✔
2091

2092
        if (h->ratelimit_burst == UINT64_MAX)
305✔
2093
                return DEFAULT_RATELIMIT_BURST;
126✔
2094

2095
        return h->ratelimit_burst;
2096
}
2097

2098
bool user_record_can_authenticate(UserRecord *h) {
×
2099
        assert(h);
×
2100

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

2103
        if (h->n_pkcs11_encrypted_key > 0)
×
2104
                return true;
2105

2106
        if (h->n_fido2_hmac_salt > 0)
×
2107
                return true;
2108

2109
        return !strv_isempty(h->hashed_password);
×
2110
}
2111

2112
bool user_record_drop_caches(UserRecord *h) {
216✔
2113
        assert(h);
216✔
2114

2115
        if (h->drop_caches >= 0)
216✔
2116
                return h->drop_caches;
×
2117

2118
        /* By default drop caches on fscrypt, not otherwise. */
2119
        return user_record_storage(h) == USER_FSCRYPT;
216✔
2120
}
2121

2122
AutoResizeMode user_record_auto_resize_mode(UserRecord *h) {
×
2123
        assert(h);
×
2124

2125
        if (h->auto_resize_mode >= 0)
×
2126
                return h->auto_resize_mode;
2127

2128
        return user_record_storage(h) == USER_LUKS ? AUTO_RESIZE_SHRINK_AND_GROW : AUTO_RESIZE_OFF;
×
2129
}
2130

2131
uint64_t user_record_rebalance_weight(UserRecord *h) {
143✔
2132
        assert(h);
143✔
2133

2134
        if (h->rebalance_weight == REBALANCE_WEIGHT_UNSET)
143✔
2135
                return REBALANCE_WEIGHT_DEFAULT;
33✔
2136

2137
        return h->rebalance_weight;
2138
}
2139

2140
static uint64_t parse_caps_strv(char **l) {
×
2141
        uint64_t c = 0;
×
2142
        int r;
×
2143

2144
        STRV_FOREACH(i, l) {
×
2145
                r = capability_from_name(*i);
×
2146
                if (r < 0)
×
2147
                        log_debug_errno(r, "Don't know capability '%s', ignoring: %m", *i);
×
2148
                else
2149
                        c |= UINT64_C(1) << r;
×
2150
        }
2151

2152
        return c;
×
2153
}
2154

2155
uint64_t user_record_capability_bounding_set(UserRecord *h) {
633✔
2156
        assert(h);
633✔
2157

2158
        /* Returns UINT64_MAX if no bounding set is configured (!) */
2159

2160
        if (!h->capability_bounding_set)
633✔
2161
                return UINT64_MAX;
2162

2163
        return parse_caps_strv(h->capability_bounding_set);
×
2164
}
2165

2166
uint64_t user_record_capability_ambient_set(UserRecord *h) {
633✔
2167
        assert(h);
633✔
2168

2169
        /* Returns UINT64_MAX if no ambient set is configured (!) */
2170

2171
        if (!h->capability_ambient_set)
633✔
2172
                return UINT64_MAX;
2173

2174
        return parse_caps_strv(h->capability_ambient_set) & user_record_capability_bounding_set(h);
×
2175
}
2176

2177
int user_record_languages(UserRecord *h, char ***ret) {
633✔
2178
        _cleanup_strv_free_ char **l = NULL;
633✔
2179
        int r;
633✔
2180

2181
        assert(h);
633✔
2182
        assert(ret);
633✔
2183

2184
        if (h->preferred_language) {
633✔
2185
                l = strv_new(h->preferred_language);
×
2186
                if (!l)
×
2187
                        return -ENOMEM;
2188
        }
2189

2190
        r = strv_extend_strv(&l, h->additional_languages, /* filter_duplicates= */ true);
633✔
2191
        if (r < 0)
633✔
2192
                return r;
2193

2194
        *ret = TAKE_PTR(l);
633✔
2195
        return 0;
633✔
2196
}
2197

2198
uint32_t user_record_tmp_limit_scale(UserRecord *h) {
268✔
2199
        assert(h);
268✔
2200

2201
        if (h->tmp_limit.is_set)
268✔
2202
                return h->tmp_limit.limit_scale;
3✔
2203

2204
        /* By default grant regular users only 80% quota */
2205
        if (user_record_disposition(h) == USER_REGULAR)
265✔
2206
                return UINT32_SCALE_FROM_PERCENT(80);
212✔
2207

2208
        return UINT32_MAX;
2209
}
2210

2211
uint32_t user_record_dev_shm_limit_scale(UserRecord *h) {
268✔
2212
        assert(h);
268✔
2213

2214
        if (h->dev_shm_limit.is_set)
268✔
2215
                return h->dev_shm_limit.limit_scale;
3✔
2216

2217
        /* By default grant regular users only 80% quota */
2218
        if (user_record_disposition(h) == USER_REGULAR)
265✔
2219
                return UINT32_SCALE_FROM_PERCENT(80);
212✔
2220

2221
        return UINT32_MAX;
2222
}
2223

2224
const char** user_record_self_modifiable_fields(UserRecord *h) {
220✔
2225
        /* As a rule of thumb: a setting is safe if it cannot be used by a
2226
         * user to give themselves some unfair advantage over other users on
2227
         * a given system. */
2228
        static const char *const default_fields[] = {
220✔
2229
                /* For display purposes */
2230
                "realName",
2231
                "emailAddress", /* Just the $EMAIL env var */
2232
                "iconName",
2233
                "location",
2234

2235
                /* Basic account settings */
2236
                "shell",
2237
                "umask",
2238
                "environment",
2239
                "timeZone",
2240
                "preferredLanguage",
2241
                "additionalLanguages",
2242
                "preferredSessionLauncher",
2243
                "preferredSessionType",
2244
                "defaultArea",
2245

2246
                /* Authentication methods */
2247
                "pkcs11TokenUri",
2248
                "fido2HmacCredential",
2249
                "recoveryKeyType",
2250

2251
                "lastChangeUSec", /* Necessary to be able to change record at all */
2252
                "lastPasswordChangeUSec", /* Ditto, but for authentication methods */
2253
                NULL
2254
        };
2255

2256
        assert(h);
220✔
2257

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

2262
        /* Note that we intentionally distinguish between NULL and an empty array here */
2263
        if (h->self_modifiable_fields)
220✔
2264
                return (const char**) h->self_modifiable_fields;
2265

2266
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_fields : NULL;
218✔
2267
}
2268

2269
const char** user_record_self_modifiable_blobs(UserRecord *h) {
158✔
2270
        static const char *const default_blobs[] = {
158✔
2271
                /* For display purposes */
2272
                "avatar",
2273
                "login-background",
2274
                NULL
2275
        };
2276

2277
        assert(h);
158✔
2278

2279
        /* Note that we intentionally distinguish between NULL and an empty array here */
2280
        if (h->self_modifiable_blobs)
158✔
2281
                return (const char**) h->self_modifiable_blobs;
2282

2283
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_blobs : NULL;
158✔
2284
}
2285

2286
const char** user_record_self_modifiable_privileged(UserRecord *h) {
182✔
2287
        static const char *const default_fields[] = {
182✔
2288
                /* For display purposes */
2289
                "passwordHint",
2290

2291
                /* Authentication methods */
2292
                "hashedPassword",
2293
                "pkcs11EncryptedKey",
2294
                "fido2HmacSalt",
2295
                "recoveryKey",
2296

2297
                "sshAuthorizedKeys", /* Basically just ~/.ssh/authorized_keys */
2298
                NULL
2299
        };
2300

2301
        assert(h);
182✔
2302

2303
        /* Note that we intentionally distinguish between NULL and an empty array here */
2304
        if (h->self_modifiable_privileged)
182✔
2305
                return (const char**) h->self_modifiable_privileged;
2306

2307
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_fields : NULL;
180✔
2308
}
2309

2310
static int remove_self_modifiable_json_fields_common(UserRecord *current, sd_json_variant **target) {
72✔
2311
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *blobs = NULL;
72✔
2312
        char **allowed;
72✔
2313
        int r;
72✔
2314

2315
        assert(current);
72✔
2316
        assert(target);
72✔
2317

2318
        if (!sd_json_variant_is_object(*target))
72✔
2319
                return -EINVAL;
2320

2321
        v = sd_json_variant_ref(*target);
72✔
2322

2323
        /* Handle basic fields */
2324
        allowed = (char**) user_record_self_modifiable_fields(current);
72✔
2325
        r = sd_json_variant_filter(&v, allowed);
72✔
2326
        if (r < 0)
72✔
2327
                return r;
2328

2329
        /* Handle blobs */
2330
        blobs = sd_json_variant_ref(sd_json_variant_by_key(v, "blobManifest"));
72✔
2331
        if (blobs) {
72✔
2332
                /* The blobManifest contains the sha256 hashes of the blobs,
2333
                 * which are enforced by the service managing the user. So, by
2334
                 * comparing the blob manifests like this, we're actually comparing
2335
                 * the contents of the blob directories & files */
2336

2337
                allowed = (char**) user_record_self_modifiable_blobs(current);
10✔
2338
                r = sd_json_variant_filter(&blobs, allowed);
10✔
2339
                if (r < 0)
10✔
2340
                        return r;
2341

2342
                if (sd_json_variant_is_blank_object(blobs))
10✔
2343
                        r = sd_json_variant_filter(&v, STRV_MAKE("blobManifest"));
×
2344
                else
2345
                        r = sd_json_variant_set_field(&v, "blobManifest", blobs);
10✔
2346
                if (r < 0)
10✔
2347
                        return r;
2348
        }
2349

2350
        JSON_VARIANT_REPLACE(*target, TAKE_PTR(v));
72✔
2351
        return 0;
72✔
2352
}
2353

2354
static int remove_self_modifiable_json_fields(UserRecord *current, UserRecord *h, sd_json_variant **ret) {
44✔
2355
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *privileged = NULL;
44✔
2356
        sd_json_variant *per_machine;
44✔
2357
        char **allowed;
44✔
2358
        int r;
44✔
2359

2360
        assert(current);
44✔
2361
        assert(h);
44✔
2362
        assert(ret);
44✔
2363

2364
        r = user_group_record_mangle(h->json, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &v, NULL);
44✔
2365
        if (r < 0)
44✔
2366
                return r;
2367

2368
        /* Handle the regular section */
2369
        r = remove_self_modifiable_json_fields_common(current, &v);
44✔
2370
        if (r < 0)
44✔
2371
                return r;
2372

2373
        /* Handle the perMachine section */
2374
        per_machine = sd_json_variant_by_key(v, "perMachine");
44✔
2375
        if (per_machine) {
44✔
2376
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *new_per_machine = NULL;
28✔
2377
                sd_json_variant *e;
28✔
2378

2379
                if (!sd_json_variant_is_array(per_machine))
28✔
2380
                        return -EINVAL;
2381

2382
                JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
63✔
2383
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *z = NULL;
35✔
2384

2385
                        if (!sd_json_variant_is_object(e))
35✔
2386
                                return -EINVAL;
2387

2388
                        r = per_machine_match(e, 0);
35✔
2389
                        if (r < 0)
35✔
2390
                                return r;
2391
                        if (r == 0) {
35✔
2392
                                /* It's only permissible to change anything inside of matching perMachine sections */
2393
                                r = sd_json_variant_append_array(&new_per_machine, e);
7✔
2394
                                if (r < 0)
7✔
2395
                                        return r;
2396
                                continue;
7✔
2397
                        }
2398

2399
                        z = sd_json_variant_ref(e);
28✔
2400

2401
                        r = remove_self_modifiable_json_fields_common(current, &z);
28✔
2402
                        if (r < 0)
28✔
2403
                                return r;
2404

2405
                        if (!sd_json_variant_is_blank_object(z)) {
28✔
2406
                                r = sd_json_variant_append_array(&new_per_machine, z);
28✔
2407
                                if (r < 0)
28✔
2408
                                        return r;
2409
                        }
2410
                }
2411

2412
                if (sd_json_variant_is_blank_array(new_per_machine))
28✔
2413
                        r = sd_json_variant_filter(&v, STRV_MAKE("perMachine"));
×
2414
                else
2415
                        r = sd_json_variant_set_field(&v, "perMachine", new_per_machine);
28✔
2416
                if (r < 0)
28✔
2417
                        return r;
2418
        }
2419

2420
        /* Handle the privileged section */
2421
        privileged = sd_json_variant_ref(sd_json_variant_by_key(v, "privileged"));
44✔
2422
        if (privileged) {
44✔
2423
                allowed = (char**) user_record_self_modifiable_privileged(current);
34✔
2424
                r = sd_json_variant_filter(&privileged, allowed);
34✔
2425
                if (r < 0)
34✔
2426
                        return r;
2427

2428
                if (sd_json_variant_is_blank_object(privileged))
34✔
2429
                        r = sd_json_variant_filter(&v, STRV_MAKE("privileged"));
32✔
2430
                else
2431
                        r = sd_json_variant_set_field(&v, "privileged", privileged);
2✔
2432
                if (r < 0)
34✔
2433
                        return r;
2434
        }
2435

2436
        JSON_VARIANT_REPLACE(*ret, TAKE_PTR(v));
44✔
2437
        return 0;
44✔
2438
}
2439

2440
int user_record_self_changes_allowed(UserRecord *current, UserRecord *incoming) {
22✔
2441
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *vc = NULL, *vi = NULL;
44✔
2442
        int r;
22✔
2443

2444
        assert(current);
22✔
2445
        assert(incoming);
22✔
2446

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

2452
        r = remove_self_modifiable_json_fields(current, current, &vc);
22✔
2453
        if (r < 0)
22✔
2454
                return r;
2455

2456
        /* Note that we use `current` as the source of the allowlist, and not
2457
         * `incoming`. This prevents the user from adding fields. Consider a
2458
         * scenario that would've been possible if we had messed up this check:
2459
         *
2460
         * 1) A user starts out with no group memberships and no custom allowlist.
2461
         *    Thus, this user is not an administrator, and the `memberOf` and
2462
         *    `selfModifiableFields` fields are unset in their record.
2463
         * 2) This user crafts a request to add the following to their record:
2464
         *    { "memberOf": ["wheel"], "selfModifiableFields": ["memberOf", "selfModifiableFields"] }
2465
         * 3) We remove the `mebmerOf` and `selfModifiabileFields` fields from `incoming`
2466
         * 4) `current` and `incoming` compare as equal, so we let the change happen
2467
         * 5) the user has granted themselves administrator privileges
2468
         */
2469
        r = remove_self_modifiable_json_fields(current, incoming, &vi);
22✔
2470
        if (r < 0)
22✔
2471
                return r;
2472

2473
        return sd_json_variant_equal(vc, vi);
22✔
2474
}
2475

2476
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
306✔
2477
        assert(h);
306✔
2478

2479
        /* Calculates when the it's possible to login next. Returns:
2480
         *
2481
         * UINT64_MAX → Nothing known
2482
         * 0          → Right away
2483
         * Any other  → Next time in CLOCK_REALTIME in usec (which could be in the past)
2484
         */
2485

2486
        if (h->ratelimit_begin_usec == UINT64_MAX ||
306✔
2487
            h->ratelimit_count == UINT64_MAX)
183✔
2488
                return UINT64_MAX;
2489

2490
        if (h->ratelimit_begin_usec > now(CLOCK_REALTIME)) /* If the ratelimit time is in the future, then
183✔
2491
                                                            * the local clock is probably incorrect. Let's
2492
                                                            * not refuse login then. */
2493
                return UINT64_MAX;
2494

2495
        if (h->ratelimit_count < user_record_ratelimit_burst(h))
183✔
2496
                return 0;
2497

2498
        return usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h));
×
2499
}
2500

2501
bool user_record_equal(UserRecord *a, UserRecord *b) {
149✔
2502
        assert(a);
149✔
2503
        assert(b);
149✔
2504

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

2508
        return sd_json_variant_equal(a->json, b->json);
149✔
2509
}
2510

2511
bool user_record_compatible(UserRecord *a, UserRecord *b) {
133✔
2512
        assert(a);
133✔
2513
        assert(b);
133✔
2514

2515
        /* If either lacks the regular section, we can't really decide, let's hence say they are
2516
         * incompatible. */
2517
        if (!(a->mask & b->mask & USER_RECORD_REGULAR))
133✔
2518
                return false;
2519

2520
        return streq_ptr(a->user_name, b->user_name) &&
133✔
2521
                streq_ptr(a->realm, b->realm);
133✔
2522
}
2523

2524
int user_record_compare_last_change(UserRecord *a, UserRecord *b) {
16✔
2525
        assert(a);
16✔
2526
        assert(b);
16✔
2527

2528
        if (a->last_change_usec == b->last_change_usec)
16✔
2529
                return 0;
2530

2531
        /* Always consider a record with a timestamp newer than one without */
2532
        if (a->last_change_usec == UINT64_MAX)
16✔
2533
                return -1;
2534
        if (b->last_change_usec == UINT64_MAX)
16✔
2535
                return 1;
2536

2537
        return CMP(a->last_change_usec, b->last_change_usec);
16✔
2538
}
2539

2540
int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret) {
5,561✔
2541
        _cleanup_(user_record_unrefp) UserRecord *c = NULL;
5,561✔
2542
        int r;
5,561✔
2543

2544
        assert(h);
5,561✔
2545
        assert(ret);
5,561✔
2546

2547
        c = user_record_new();
5,561✔
2548
        if (!c)
5,561✔
2549
                return -ENOMEM;
2550

2551
        r = user_record_load(c, h->json, flags);
5,561✔
2552
        if (r < 0)
5,561✔
2553
                return r;
2554

2555
        *ret = TAKE_PTR(c);
5,561✔
2556
        return 0;
5,561✔
2557
}
2558

2559
int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask) {
24✔
2560
        _cleanup_(user_record_unrefp) UserRecord *x = NULL, *y = NULL;
24✔
2561
        int r;
24✔
2562

2563
        assert(a);
24✔
2564
        assert(b);
24✔
2565

2566
        /* Compares the two records, but ignores anything not listed in the specified mask */
2567

2568
        if ((a->mask & ~mask) != 0) {
24✔
2569
                r = user_record_clone(a, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &x);
24✔
2570
                if (r < 0)
24✔
2571
                        return r;
2572

2573
                a = x;
24✔
2574
        }
2575

2576
        if ((b->mask & ~mask) != 0) {
24✔
2577
                r = user_record_clone(b, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &y);
24✔
2578
                if (r < 0)
24✔
2579
                        return r;
2580

2581
                b = y;
24✔
2582
        }
2583

2584
        return user_record_equal(a, b);
24✔
2585
}
2586

2587
int user_record_test_blocked(UserRecord *h) {
198✔
2588
        usec_t n;
198✔
2589

2590
        /* Checks whether access to the specified user shall be allowed at the moment. Returns:
2591
         *
2592
         *          -ESTALE: Record is from the future
2593
         *          -ENOLCK: Record is blocked
2594
         *          -EL2HLT: Record is not valid yet
2595
         *          -EL3HLT: Record is not valid anymore
2596
         *
2597
         */
2598

2599
        assert(h);
198✔
2600

2601
        if (h->locked > 0)
198✔
2602
                return -ENOLCK;
2603

2604
        n = now(CLOCK_REALTIME);
170✔
2605

2606
        if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
170✔
2607
                return -EL2HLT;
2608
        if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
170✔
2609
                return -EL3HLT;
2610

2611
        if (h->last_change_usec != UINT64_MAX &&
170✔
2612
            h->last_change_usec > n) /* Complain during log-ins when the record is from the future */
2613
                return -ESTALE;
×
2614

2615
        return 0;
2616
}
2617

2618
int user_record_test_password_change_required(UserRecord *h) {
200✔
2619
        bool change_permitted;
200✔
2620
        usec_t n;
200✔
2621

2622
        assert(h);
200✔
2623

2624
        /* Checks whether the user must change the password when logging in
2625

2626
            -EKEYREVOKED: Change password now because admin said so
2627
             -EOWNERDEAD: Change password now because it expired
2628
           -EKEYREJECTED: Password is expired, no changing is allowed
2629
            -EKEYEXPIRED: Password is about to expire, warn user
2630
               -ENETDOWN: Record has expiration info but no password change timestamp
2631
                  -EROFS: No password change required nor permitted
2632
                 -ESTALE: RTC likely incorrect, last password change is in the future
2633
                       0: No password change required, but permitted
2634
         */
2635

2636
        /* If a password change request has been set explicitly, it overrides everything */
2637
        if (h->password_change_now > 0)
200✔
2638
                return -EKEYREVOKED;
2639

2640
        n = now(CLOCK_REALTIME);
200✔
2641

2642
        /* Password change in the future? Then our RTC is likely incorrect */
2643
        if (h->last_password_change_usec != UINT64_MAX &&
200✔
2644
            h->last_password_change_usec > n &&
×
2645
            (h->password_change_min_usec != UINT64_MAX ||
×
2646
             h->password_change_max_usec != UINT64_MAX ||
×
2647
             h->password_change_inactive_usec != UINT64_MAX))
×
2648
            return -ESTALE;
2649

2650
        /* Then, let's check if password changing is currently allowed at all */
2651
        if (h->password_change_min_usec != UINT64_MAX) {
200✔
2652

2653
                /* Expiry configured but no password change timestamp known? */
2654
                if (h->last_password_change_usec == UINT64_MAX)
×
2655
                        return -ENETDOWN;
2656

2657
                if (h->password_change_min_usec >= UINT64_MAX - h->last_password_change_usec)
×
2658
                        change_permitted = false;
2659
                else
2660
                        change_permitted = n >= h->last_password_change_usec + h->password_change_min_usec;
×
2661

2662
        } else
2663
                change_permitted = true;
2664

2665
        /* Let's check whether the password has expired.  */
2666
        if (!(h->password_change_max_usec == UINT64_MAX ||
200✔
2667
              h->password_change_max_usec >= UINT64_MAX - h->last_password_change_usec)) {
×
2668

2669
                uint64_t change_before;
×
2670

2671
                /* Expiry configured but no password change timestamp known? */
2672
                if (h->last_password_change_usec == UINT64_MAX)
×
2673
                        return -ENETDOWN;
2674

2675
                /* Password is in inactive phase? */
2676
                if (h->password_change_inactive_usec != UINT64_MAX &&
×
2677
                    h->password_change_inactive_usec < UINT64_MAX - h->password_change_max_usec) {
×
2678
                        usec_t added;
×
2679

2680
                        added = h->password_change_inactive_usec + h->password_change_max_usec;
×
2681
                        if (added < UINT64_MAX - h->last_password_change_usec &&
×
2682
                            n >= h->last_password_change_usec + added)
×
2683
                                return -EKEYREJECTED;
2684
                }
2685

2686
                /* Password needs to be changed now? */
2687
                change_before = h->last_password_change_usec + h->password_change_max_usec;
×
2688
                if (n >= change_before)
×
2689
                        return change_permitted ? -EOWNERDEAD : -EKEYREJECTED;
×
2690

2691
                /* Warn user? */
2692
                if (h->password_change_warn_usec != UINT64_MAX &&
×
2693
                    (change_before < h->password_change_warn_usec ||
×
2694
                     n >= change_before - h->password_change_warn_usec))
×
2695
                        return change_permitted ? -EKEYEXPIRED : -EROFS;
×
2696
        }
2697

2698
        /* No password changing necessary */
2699
        return change_permitted ? 0 : -EROFS;
200✔
2700
}
2701

2702
bool user_record_is_root(const UserRecord *u) {
5,057✔
2703
        assert(u);
5,057✔
2704

2705
        return u->uid == 0 || streq_ptr(u->user_name, "root");
5,057✔
2706
}
2707

2708
bool user_record_is_nobody(const UserRecord *u) {
2,751✔
2709
        assert(u);
2,751✔
2710

2711
        return u->uid == UID_NOBODY || STRPTR_IN_SET(u->user_name, NOBODY_USER_NAME, "nobody");
2,751✔
2712
}
2713

2714
bool user_record_matches_user_name(const UserRecord *u, const char *user_name) {
7,102✔
2715
        assert(u);
7,102✔
2716
        assert(user_name);
7,102✔
2717

2718
        if (streq_ptr(u->user_name, user_name))
7,102✔
2719
                return true;
2720

2721
        if (streq_ptr(u->user_name_and_realm_auto, user_name))
27✔
2722
                return true;
2723

2724
        if (strv_contains(u->aliases, user_name))
21✔
2725
                return true;
2726

2727
        const char *realm = strrchr(user_name, '@');
15✔
2728
        if (realm && streq_ptr(realm+1, u->realm))
15✔
2729
                STRV_FOREACH(a, u->aliases)
18✔
2730
                        if (startswith(user_name, *a) == realm)
18✔
2731
                                return true;
2732

2733
        return false;
2734
}
2735

2736
int suitable_blob_filename(const char *name) {
429✔
2737
        /* Enforces filename requirements as described in docs/USER_RECORD_BULK_DIRS.md */
2738
        return filename_is_valid(name) &&
858✔
2739
               in_charset(name, URI_UNRESERVED) &&
429✔
2740
               name[0] != '.';
423✔
2741
}
2742

2743
bool userdb_match_is_set(const UserDBMatch *match) {
56,190✔
2744
        if (!match)
56,190✔
2745
                return false;
2746

2747
        return !strv_isempty(match->fuzzy_names) ||
28,770✔
2748
                !FLAGS_SET(match->disposition_mask, USER_DISPOSITION_MASK_ALL) ||
28,734✔
2749
                match->uid_min > 0 ||
28,265✔
2750
                match->uid_max < UID_INVALID-1 ||
56,999✔
2751
                !sd_id128_is_null(match->uuid);
28,229✔
2752
}
2753

2754
void userdb_match_done(UserDBMatch *match) {
27,296✔
2755
        assert(match);
27,296✔
2756
        strv_free(match->fuzzy_names);
27,296✔
2757
}
27,296✔
2758

2759
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches) {
58✔
2760
        assert(names || n_names == 0);
58✔
2761

2762
        /* Checks if any of the user record strings in the names[] array matches any of the search strings in
2763
         * the matches** strv fuzzily. */
2764

2765
        FOREACH_ARRAY(n, names, n_names) {
204✔
2766
                if (!*n)
146✔
2767
                        continue;
67✔
2768

2769
                _cleanup_free_ char *lcn = strdup(*n);
79✔
2770
                if (!lcn)
79✔
2771
                        return -ENOMEM;
2772

2773
                ascii_strlower(lcn);
79✔
2774

2775
                STRV_FOREACH(i, matches) {
158✔
2776
                        _cleanup_free_ char *lc = strdup(*i);
79✔
2777
                        if (!lc)
79✔
2778
                                return -ENOMEM;
2779

2780
                        ascii_strlower(lc);
79✔
2781

2782
                        /* First do substring check */
2783
                        if (strstr(lcn, lc))
79✔
2784
                                return true;
2785

2786
                        /* Then do some fuzzy string comparison (but only if the needle is non-trivially long) */
2787
                        if (strlen(lc) >= 5 && strlevenshtein(lcn, lc) < 3)
79✔
2788
                                return true;
2789
                }
2790
        }
2791

2792
        return false;
2793
}
2794

2795
bool user_record_match(UserRecord *u, const UserDBMatch *match) {
12,097✔
2796
        assert(u);
12,097✔
2797

2798
        if (!match)
12,097✔
2799
                return true;
2800

2801
        if (!uid_is_valid(u->uid))
5,628✔
2802
                return false;
2803

2804
        if (u->uid < match->uid_min || u->uid > match->uid_max)
5,628✔
2805
                return false;
2806

2807
        if (!BIT_SET(match->disposition_mask, user_record_disposition(u)))
5,600✔
2808
                return false;
2809

2810
        if (!sd_id128_is_null(match->uuid) && !sd_id128_equal(match->uuid, u->uuid))
11,038✔
2811
                return false;
×
2812

2813
        if (!strv_isempty(match->fuzzy_names)) {
5,519✔
2814

2815
                /* Note this array of names is sparse, i.e. various entries listed in it will be
2816
                 * NULL. Because of that we are not using a NULL terminated strv here, but a regular
2817
                 * array. */
2818
                const char* names[] = {
56✔
2819
                        u->user_name,
28✔
2820
                        user_record_user_name_and_realm(u),
28✔
2821
                        u->real_name,
28✔
2822
                        u->email_address,
28✔
2823
                        u->cifs_user_name,
28✔
2824
                };
2825

2826
                if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names) &&
56✔
2827
                    !user_name_fuzzy_match((const char**) u->aliases, strv_length(u->aliases), match->fuzzy_names))
28✔
2828
                        return false;
28✔
2829
        }
2830

2831
        return true;
2832
}
2833

2834
int json_dispatch_dispositions_mask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
2✔
2835
        uint64_t *mask = ASSERT_PTR(userdata);
2✔
2836

2837
        if (sd_json_variant_is_null(variant)) {
2✔
2838
                *mask = UINT64_MAX;
×
2839
                return 0;
×
2840
        }
2841

2842
        if (!sd_json_variant_is_array(variant))
2✔
2843
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
×
2844

2845
        uint64_t m = 0;
2846
        for (size_t i = 0; i < sd_json_variant_elements(variant); i++) {
16✔
2847
                sd_json_variant *e;
14✔
2848
                const char *a;
14✔
2849

2850
                e = sd_json_variant_by_index(variant, i);
14✔
2851
                if (!sd_json_variant_is_string(e))
14✔
2852
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
2853

2854
                assert_se(a = sd_json_variant_string(e));
14✔
2855

2856
                UserDisposition d = user_disposition_from_string(a);
14✔
2857
                if (d < 0)
14✔
2858
                        return json_log(e, flags, d, "JSON field '%s' contains an invalid user disposition type: %s", strna(name), a);
×
2859

2860
                m |= INDEX_TO_MASK(uint64_t, d);
14✔
2861
        }
2862

2863
        *mask = m;
2✔
2864
        return 0;
2✔
2865
}
2866

2867
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
2868
        [USER_CLASSIC]   = "classic",
2869
        [USER_LUKS]      = "luks",
2870
        [USER_DIRECTORY] = "directory",
2871
        [USER_SUBVOLUME] = "subvolume",
2872
        [USER_FSCRYPT]   = "fscrypt",
2873
        [USER_CIFS]      = "cifs",
2874
};
2875

2876
DEFINE_STRING_TABLE_LOOKUP(user_storage, UserStorage);
3,163✔
2877

2878
static const char* const user_disposition_table[_USER_DISPOSITION_MAX] = {
2879
        [USER_INTRINSIC] = "intrinsic",
2880
        [USER_SYSTEM]    = "system",
2881
        [USER_DYNAMIC]   = "dynamic",
2882
        [USER_REGULAR]   = "regular",
2883
        [USER_CONTAINER] = "container",
2884
        [USER_FOREIGN]   = "foreign",
2885
        [USER_RESERVED]  = "reserved",
2886
};
2887

2888
DEFINE_STRING_TABLE_LOOKUP(user_disposition, UserDisposition);
22,042✔
2889

2890
static const char* const auto_resize_mode_table[_AUTO_RESIZE_MODE_MAX] = {
2891
        [AUTO_RESIZE_OFF]             = "off",
2892
        [AUTO_RESIZE_GROW]            = "grow",
2893
        [AUTO_RESIZE_SHRINK_AND_GROW] = "shrink-and-grow",
2894
};
2895

2896
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