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

systemd / systemd / 15232239991

24 May 2025 08:01PM UTC coverage: 72.053% (-0.02%) from 72.07%
15232239991

push

github

web-flow
docs: add man pages for sd_device_enumerator_[new,ref,unref,unrefp] (#37586)

For #20929.

299160 of 415197 relevant lines covered (72.05%)

703671.29 hits per line

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

55.15
/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 "cap-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) {
11,822✔
34
        UserRecord *h;
11,822✔
35

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

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

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

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

159
        free(h->blob_directory);
11,807✔
160
        hashmap_free(h->blob_manifest);
11,807✔
161

162
        free(h->shell);
11,807✔
163

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

170
        free(h->skeleton_directory);
11,807✔
171

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

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

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

187
        free(h->fallback_shell);
11,807✔
188
        free(h->fallback_home_directory);
11,807✔
189

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

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

201
        free(h->state);
11,807✔
202
        free(h->service);
11,807✔
203

204
        free(h->preferred_session_type);
11,807✔
205
        free(h->preferred_session_launcher);
11,807✔
206

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

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

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

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

225
        free(h->default_area);
11,807✔
226

227
        sd_json_variant_unref(h->json);
11,807✔
228

229
        return mfree(h);
11,807✔
230
}
231

232
DEFINE_TRIVIAL_REF_UNREF_FUNC(UserRecord, user_record, user_record_free);
23,584✔
233

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

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

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

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

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

258
        return 0;
259
}
260

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

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

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

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

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

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

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

289
        return 0;
290
}
291

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

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

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

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

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

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

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

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

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

328
        return 0;
329
}
330

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

337
        assert_se(limits);
×
338

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

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

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

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

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

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

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

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

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

390
        return 0;
×
391
}
392

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

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

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

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

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

414
        return 0;
415
}
416

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

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

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

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

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

438
        return 0;
439
}
440

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

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

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

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

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

462
        return 0;
463
}
464

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

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

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

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

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

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

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

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

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

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

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

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

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

522
        n = sd_json_variant_string(variant);
×
523

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

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

531
        return 0;
532
}
533

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

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

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

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

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

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

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

565
JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_disposition, UserDisposition, user_disposition_from_string);
12,460✔
566
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_user_storage, UserStorage, user_storage_from_string);
2,858✔
567

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

636
        return 0;
637
}
638

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

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

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

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

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

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

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

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

675
        return 0;
676
}
677

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

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

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

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

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

700
        } else {
701

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

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

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

711
                        n = sd_json_variant_string(e);
×
712
                        if (!pkcs11_uri_valid(n))
×
713
                                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element in '%s' is not a valid RFC7512 PKCS#11 URI: %s", strna(name), n);
×
714

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

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

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

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

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

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

745
        return 0;
×
746
}
747

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

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

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

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

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

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

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

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

782
        return 0;
×
783
}
784

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

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

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

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

804
        return 0;
×
805
}
806

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

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

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

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

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

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

832
        return 0;
×
833
}
834

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

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

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

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

855
        return 0;
×
856
}
857

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

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

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

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

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

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

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

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

899
        return 0;
×
900
}
901

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

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

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

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

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

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

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

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

935
        return 0;
×
936
}
937

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

941
        assert_se(mode);
×
942

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

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

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

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

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

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

968
        assert_se(rebalance_weight);
749✔
969

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

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

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

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

993
        return 0;
994
}
995

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

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

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

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

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

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

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

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

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

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

1042
        return sd_json_dispatch(variant, privileged_dispatch_table, flags, userdata);
5,939✔
1043
}
1044

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

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

1064
        sd_json_variant *m;
10,920✔
1065
        sd_id128_t mid;
10,920✔
1066
        int r;
10,920✔
1067

1068
        if (!variant)
10,920✔
1069
                return 0;
10,920✔
1070

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1135
        assert(ids);
1,953✔
1136

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

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

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

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

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

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

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

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

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

1174
                return false;
×
1175
        }
1176

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

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

1185
        assert(hns);
×
1186

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

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

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

1201
                JSON_VARIANT_ARRAY_FOREACH(e, hns) {
×
1202

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

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

1212
                return false;
×
1213
        }
1214

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

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

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

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

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

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

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

1261
        return false;
1262
}
1263

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

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

1355
        sd_json_variant *e;
10,920✔
1356
        int r;
10,920✔
1357

1358
        if (!variant)
10,920✔
1359
                return 0;
1360

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

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

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

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

1379
        return 0;
1,843✔
1380
}
1381

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

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

1409
        sd_json_variant *m;
10,920✔
1410
        sd_id128_t mid;
10,920✔
1411
        int r;
10,920✔
1412

1413
        if (!variant)
10,920✔
1414
                return 0;
10,920✔
1415

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

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

1423
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
3,790✔
1424
        if (!m)
3,790✔
1425
                return 0;
1426

1427
        return sd_json_dispatch(m, status_dispatch_table, flags, userdata);
3,790✔
1428
}
1429

1430
int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret) {
4,228✔
1431
        const char *suffix;
4,228✔
1432
        char *z;
4,228✔
1433

1434
        assert(storage >= 0);
4,228✔
1435
        assert(user_name_and_realm);
4,228✔
1436
        assert(ret);
4,228✔
1437

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

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

1451
        *ret = path_simplify(z);
134✔
1452
        return 1;
134✔
1453
}
1454

1455
static int user_record_augment(UserRecord *h, sd_json_dispatch_flags_t json_flags) {
10,920✔
1456
        int r;
10,920✔
1457

1458
        assert(h);
10,920✔
1459

1460
        if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
10,920✔
1461
                return 0;
1462

1463
        assert(h->user_name);
10,765✔
1464

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

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

1475
        if (!h->home_directory && !h->home_directory_auto) {
5,986✔
1476
                h->home_directory_auto = path_join(get_home_root(), h->user_name);
4,415✔
1477
                if (!h->home_directory_auto)
4,415✔
1478
                        return json_log_oom(h->json, json_flags);
×
1479
        }
1480

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

1487
        return 0;
1488
}
1489

1490
int user_group_record_mangle(
19,097✔
1491
                sd_json_variant *v,
1492
                UserRecordLoadFlags load_flags,
1493
                sd_json_variant **ret_variant,
1494
                UserRecordMask *ret_mask) {
1495

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

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

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

1519
        assert(v);
19,097✔
1520
        assert(ret_variant);
19,097✔
1521

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

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

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

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

1534
        /* Check if we have the special sections and if they match our flags set */
1535
        FOREACH_ELEMENT(i, mask_field) {
133,679✔
1536
                sd_json_variant *e, *k;
114,582✔
1537

1538
                if (FLAGS_SET(USER_RECORD_STRIP_MASK(load_flags), i->mask)) {
114,582✔
1539
                        if (!w)
7,573✔
1540
                                w = sd_json_variant_ref(v);
6,269✔
1541

1542
                        r = sd_json_variant_filter(&w, STRV_MAKE(i->name));
7,573✔
1543
                        if (r < 0)
7,573✔
1544
                                return json_log(w, json_flags, r, "Failed to remove field from variant: %m");
×
1545

1546
                        continue;
7,573✔
1547
                }
1548

1549
                e = sd_json_variant_by_key_full(v, i->name, &k);
107,009✔
1550
                if (e) {
107,009✔
1551
                        if (!FLAGS_SET(USER_RECORD_ALLOW_MASK(load_flags), i->mask))
18,148✔
1552
                                return json_log(e, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", i->name);
×
1553

1554
                        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
18,148✔
1555
                                array[n_retain++] = k;
122✔
1556
                                array[n_retain++] = e;
122✔
1557
                        }
1558

1559
                        m |= i->mask;
18,148✔
1560
                } else {
1561
                        if (FLAGS_SET(USER_RECORD_REQUIRE_MASK(load_flags), i->mask))
88,861✔
1562
                                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks '%s' field, which is required.", i->name);
×
1563
                }
1564
        }
1565

1566
        if (FLAGS_SET(load_flags, USER_RECORD_STRIP_REGULAR)) {
19,097✔
1567
                /* If we are supposed to strip regular items, then let's instead just allocate a new object
1568
                 * with just the stuff we need. */
1569

1570
                w = sd_json_variant_unref(w);
232✔
1571
                r = sd_json_variant_new_object(&w, array, n_retain);
232✔
1572
                if (r < 0)
232✔
1573
                        return json_log(v, json_flags, r, "Failed to allocate new object: %m");
×
1574
        } else
1575
                /* And now check if there's anything else in the record */
1576
                for (size_t i = 0; i < sd_json_variant_elements(v); i += 2) {
18,898✔
1577
                        const char *f;
18,865✔
1578
                        bool special = false;
18,865✔
1579

1580
                        assert_se(f = sd_json_variant_string(sd_json_variant_by_index(v, i)));
18,865✔
1581

1582
                        FOREACH_ELEMENT(j, mask_field)
132,055✔
1583
                                if (streq(f, j->name)) { /* already covered in the loop above */
113,190✔
1584
                                        special = true;
33✔
1585
                                        continue;
33✔
1586
                                }
1587

1588
                        if (!special) {
18,865✔
1589
                                if ((load_flags & (USER_RECORD_ALLOW_REGULAR|USER_RECORD_REQUIRE_REGULAR)) == 0)
18,832✔
1590
                                        return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record contains '%s' field, which is not allowed.", f);
×
1591

1592
                                m |= USER_RECORD_REGULAR;
18,832✔
1593
                                break;
18,832✔
1594
                        }
1595
                }
1596

1597
        if (FLAGS_SET(load_flags, USER_RECORD_REQUIRE_REGULAR) && !FLAGS_SET(m, USER_RECORD_REGULAR))
19,097✔
1598
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record lacks basic identity fields, which are required.");
×
1599

1600
        if (!FLAGS_SET(load_flags, USER_RECORD_EMPTY_OK) && m == 0)
19,097✔
1601
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EBADMSG), "Record is empty.");
×
1602

1603
        if (w)
19,097✔
1604
                *ret_variant = TAKE_PTR(w);
6,269✔
1605
        else
1606
                *ret_variant = sd_json_variant_ref(v);
12,828✔
1607

1608
        if (ret_mask)
19,097✔
1609
                *ret_mask = m;
19,053✔
1610
        return 0;
1611
}
1612

1613
int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load_flags) {
10,920✔
1614

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

1710
                { "secret",                     SD_JSON_VARIANT_OBJECT,        dispatch_secret,                      0,                                                   0              },
1711
                { "privileged",                 SD_JSON_VARIANT_OBJECT,        dispatch_privileged,                  0,                                                   0              },
1712

1713
                /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */
1714
                { "perMachine",                 SD_JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0              },
1715
                { "binding",                    SD_JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0              },
1716
                { "status",                     SD_JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0              },
1717

1718
                /* Ignore 'signature', we check it with explicit accessors instead */
1719
                { "signature",                  SD_JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0              },
1720
                {},
1721
        };
1722

1723
        sd_json_dispatch_flags_t json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
10,920✔
1724
        int r;
10,920✔
1725

1726
        assert(h);
10,920✔
1727
        assert(!h->json);
10,920✔
1728

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

1731
        r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
10,920✔
1732
        if (r < 0)
10,920✔
1733
                return r;
1734

1735
        r = sd_json_dispatch(h->json, user_dispatch_table, json_flags | SD_JSON_ALLOW_EXTENSIONS, h);
10,920✔
1736
        if (r < 0)
10,920✔
1737
                return r;
1738

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

1742
        r = dispatch_per_machine("perMachine", sd_json_variant_by_key(h->json, "perMachine"), json_flags, h);
10,920✔
1743
        if (r < 0)
10,920✔
1744
                return r;
1745

1746
        r = dispatch_binding("binding", sd_json_variant_by_key(h->json, "binding"), json_flags, h);
10,920✔
1747
        if (r < 0)
10,920✔
1748
                return r;
1749

1750
        r = dispatch_status("status", sd_json_variant_by_key(h->json, "status"), json_flags, h);
10,920✔
1751
        if (r < 0)
10,920✔
1752
                return r;
1753

1754
        if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->user_name)
10,920✔
1755
                return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "User name field missing, refusing.");
×
1756

1757
        r = user_record_augment(h, json_flags);
10,920✔
1758
        if (r < 0)
10,920✔
1759
                return r;
×
1760

1761
        return 0;
1762
}
1763

1764
int user_record_build(UserRecord **ret, ...) {
2,986✔
1765
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
2,986✔
1766
        _cleanup_(user_record_unrefp) UserRecord *u = NULL;
2,986✔
1767
        va_list ap;
2,986✔
1768
        int r;
2,986✔
1769

1770
        assert(ret);
2,986✔
1771

1772
        va_start(ap, ret);
2,986✔
1773
        r = sd_json_buildv(&v, ap);
2,986✔
1774
        va_end(ap);
2,986✔
1775

1776
        if (r < 0)
2,986✔
1777
                return r;
1778

1779
        u = user_record_new();
2,986✔
1780
        if (!u)
2,986✔
1781
                return -ENOMEM;
1782

1783
        r = user_record_load(u, v, USER_RECORD_LOAD_FULL);
2,986✔
1784
        if (r < 0)
2,986✔
1785
                return r;
1786

1787
        *ret = TAKE_PTR(u);
2,986✔
1788
        return 0;
2,986✔
1789
}
1790

1791
const char* user_record_user_name_and_realm(UserRecord *h) {
4,397✔
1792
        assert(h);
4,397✔
1793

1794
        /* Return the pre-initialized joined string if it is defined */
1795
        if (h->user_name_and_realm_auto)
4,397✔
1796
                return h->user_name_and_realm_auto;
1797

1798
        /* If it's not defined then we cannot have a realm */
1799
        assert(!h->realm);
4,376✔
1800
        return h->user_name;
4,376✔
1801
}
1802

1803
UserStorage user_record_storage(UserRecord *h) {
16,591✔
1804
        assert(h);
16,591✔
1805

1806
        if (h->storage >= 0)
16,591✔
1807
                return h->storage;
2,440✔
1808

1809
        return USER_CLASSIC;
1810
}
1811

1812
const char* user_record_file_system_type(UserRecord *h) {
×
1813
        assert(h);
×
1814

1815
        return h->file_system_type ?: "btrfs";
×
1816
}
1817

1818
const char* user_record_skeleton_directory(UserRecord *h) {
140✔
1819
        assert(h);
140✔
1820

1821
        return h->skeleton_directory ?: "/etc/skel";
140✔
1822
}
1823

1824
mode_t user_record_access_mode(UserRecord *h) {
9✔
1825
        assert(h);
9✔
1826

1827
        return h->access_mode != MODE_INVALID ? h->access_mode : 0700;
9✔
1828
}
1829

1830
static const char *user_record_home_directory_real(UserRecord *h) {
3,909✔
1831
        assert(h);
3,909✔
1832

1833
        if (h->home_directory)
3,909✔
1834
                return h->home_directory;
1835
        if (h->home_directory_auto)
1,042✔
1836
                return h->home_directory_auto;
1837

1838
        /* The root user is special, hence be special about it */
1839
        if (user_record_is_root(h))
35✔
1840
                return "/root";
×
1841

1842
        return "/";
1843
}
1844

1845
const char* user_record_home_directory(UserRecord *h) {
3,978✔
1846
        assert(h);
3,978✔
1847

1848
        if (h->use_fallback && h->fallback_home_directory)
3,978✔
1849
                return h->fallback_home_directory;
1850

1851
        return user_record_home_directory_real(h);
3,851✔
1852
}
1853

1854
const char* user_record_image_path(UserRecord *h) {
1,100✔
1855
        assert(h);
1,100✔
1856

1857
        if (h->image_path)
1,100✔
1858
                return h->image_path;
1859
        if (h->image_path_auto)
105✔
1860
                return h->image_path_auto;
1861

1862
        /* For some storage types the image is the home directory itself. (But let's ignore the fallback logic for it) */
1863
        return IN_SET(user_record_storage(h), USER_CLASSIC, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT) ?
58✔
1864
                user_record_home_directory_real(h) : NULL;
58✔
1865
}
1866

1867
const char* user_record_cifs_user_name(UserRecord *h) {
×
1868
        assert(h);
×
1869

1870
        return h->cifs_user_name ?: h->user_name;
×
1871
}
1872

1873
unsigned long user_record_mount_flags(UserRecord *h) {
40✔
1874
        assert(h);
40✔
1875

1876
        return (h->nosuid ? MS_NOSUID : 0) |
40✔
1877
                (h->noexec ? MS_NOEXEC : 0) |
40✔
1878
                (h->nodev ? MS_NODEV : 0);
40✔
1879
}
1880

1881
static const char *user_record_shell_real(UserRecord *h) {
1,242✔
1882
        assert(h);
1,242✔
1883

1884
        if (h->shell)
1,242✔
1885
                return h->shell;
1886

1887
        if (user_record_is_root(h))
382✔
1888
                return "/bin/sh";
1889

1890
        if (user_record_disposition(h) == USER_REGULAR)
377✔
1891
                return DEFAULT_USER_SHELL;
359✔
1892

1893
        return NOLOGIN;
1894
}
1895

1896
const char* user_record_shell(UserRecord *h) {
1,242✔
1897
        const char *shell;
1,242✔
1898

1899
        assert(h);
1,242✔
1900

1901
        shell = user_record_shell_real(h);
1,242✔
1902

1903
        /* Return fallback shall if we are told so — except if the primary shell is already a nologin shell,
1904
         * then let's not risk anything. */
1905
        if (h->use_fallback && h->fallback_shell)
1,242✔
1906
                return is_nologin_shell(shell) ? NOLOGIN : h->fallback_shell;
182✔
1907

1908
        return shell;
1909
}
1910

1911
const char* user_record_real_name(UserRecord *h) {
773✔
1912
        assert(h);
773✔
1913

1914
        return h->real_name ?: h->user_name;
773✔
1915
}
1916

1917
bool user_record_luks_discard(UserRecord *h) {
×
1918
        const char *ip;
×
1919

1920
        assert(h);
×
1921

1922
        if (h->luks_discard >= 0)
×
1923
                return h->luks_discard;
×
1924

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

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

1935
        return path_startswith(ip, "/dev/");
×
1936
}
1937

1938
bool user_record_luks_offline_discard(UserRecord *h) {
×
1939
        const char *ip;
×
1940

1941
        assert(h);
×
1942

1943
        if (h->luks_offline_discard >= 0)
×
1944
                return h->luks_offline_discard;
×
1945

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

1949
        ip = user_record_image_path(h);
×
1950
        if (!ip)
×
1951
                return false;
1952

1953
        if (path_startswith(ip, "/dev/"))
×
1954
                return user_record_luks_discard(h);
×
1955

1956
        return true;
1957
}
1958

1959
const char* user_record_luks_cipher(UserRecord *h) {
×
1960
        assert(h);
×
1961

1962
        return h->luks_cipher ?: "aes";
×
1963
}
1964

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

1968
        return h->luks_cipher_mode ?: "xts-plain64";
×
1969
}
1970

1971
uint64_t user_record_luks_volume_key_size(UserRecord *h) {
×
1972
        assert(h);
×
1973

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

1976
        if (h->luks_volume_key_size == UINT64_MAX)
×
1977
                return 256 / 8;
×
1978

1979
        return MIN(h->luks_volume_key_size, SIZE_MAX);
1980
}
1981

1982
const char* user_record_luks_pbkdf_type(UserRecord *h) {
×
1983
        assert(h);
×
1984

1985
        return h->luks_pbkdf_type ?: "argon2id";
×
1986
}
1987

1988
uint64_t user_record_luks_pbkdf_force_iterations(UserRecord *h) {
×
1989
        assert(h);
×
1990

1991
        /* propagate default "benchmark" mode as itself */
1992
        if (h->luks_pbkdf_force_iterations == UINT64_MAX)
×
1993
                return UINT64_MAX;
1994

1995
        /* clamp everything else to actually accepted number of iterations of libcryptsetup */
1996
        return CLAMP(h->luks_pbkdf_force_iterations, 1U, UINT32_MAX);
×
1997
}
1998

1999
uint64_t user_record_luks_pbkdf_time_cost_usec(UserRecord *h) {
×
2000
        assert(h);
×
2001

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

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

2007
        return MIN(DIV_ROUND_UP(h->luks_pbkdf_time_cost_usec, USEC_PER_MSEC), UINT32_MAX) * USEC_PER_MSEC;
×
2008
}
2009

2010
uint64_t user_record_luks_pbkdf_memory_cost(UserRecord *h) {
×
2011
        assert(h);
×
2012

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

2018
        return MIN(DIV_ROUND_UP(h->luks_pbkdf_memory_cost, 1024), UINT32_MAX) * 1024;
×
2019
}
2020

2021
uint64_t user_record_luks_pbkdf_parallel_threads(UserRecord *h) {
×
2022
        assert(h);
×
2023

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

2028
        return MIN(h->luks_pbkdf_parallel_threads, UINT32_MAX);
×
2029
}
2030

2031
uint64_t user_record_luks_sector_size(UserRecord *h) {
×
2032
        assert(h);
×
2033

2034
        if (h->luks_sector_size == UINT64_MAX)
×
2035
                return 512;
2036

2037
        /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */
2038
        return CLAMP(UINT64_C(1) << (63 - __builtin_clzl(h->luks_sector_size)), 512U, 4096U);
×
2039
}
2040

2041
const char* user_record_luks_pbkdf_hash_algorithm(UserRecord *h) {
×
2042
        assert(h);
×
2043

2044
        return h->luks_pbkdf_hash_algorithm ?: "sha512";
×
2045
}
2046

2047
gid_t user_record_gid(UserRecord *h) {
3,533✔
2048
        assert(h);
3,533✔
2049

2050
        if (gid_is_valid(h->gid))
3,533✔
2051
                return h->gid;
2,293✔
2052

2053
        return (gid_t) h->uid;
1,240✔
2054
}
2055

2056
UserDisposition user_record_disposition(UserRecord *h) {
19,102✔
2057
        assert(h);
19,102✔
2058

2059
        if (h->disposition >= 0)
19,102✔
2060
                return h->disposition;
2061

2062
        /* If not declared, derive from UID */
2063

2064
        if (!uid_is_valid(h->uid))
4,043✔
2065
                return _USER_DISPOSITION_INVALID;
2066

2067
        if (user_record_is_root(h) || user_record_is_nobody(h))
4,043✔
2068
                return USER_INTRINSIC;
1,572✔
2069

2070
        if (uid_is_system(h->uid))
2,471✔
2071
                return USER_SYSTEM;
2072

2073
        if (uid_is_dynamic(h->uid))
233✔
2074
                return USER_DYNAMIC;
2075

2076
        if (uid_is_container(h->uid))
233✔
2077
                return USER_CONTAINER;
2078

2079
        if (uid_is_foreign(h->uid))
79✔
2080
                return USER_FOREIGN;
2081

2082
        if (h->uid > INT32_MAX)
79✔
2083
                return USER_RESERVED;
×
2084

2085
        return USER_REGULAR;
2086
}
2087

2088
int user_record_removable(UserRecord *h) {
10,247✔
2089
        UserStorage storage;
10,247✔
2090
        assert(h);
10,247✔
2091

2092
        if (h->removable >= 0)
10,247✔
2093
                return h->removable;
2094

2095
        /* Refuse to decide for classic records */
2096
        storage = user_record_storage(h);
10,247✔
2097
        if (h->storage < 0 || h->storage == USER_CLASSIC)
10,247✔
2098
                return -1;
2099

2100
        /* For now consider only LUKS home directories with a reference by path as removable */
2101
        return storage == USER_LUKS && path_startswith(user_record_image_path(h), "/dev/");
367✔
2102
}
2103

2104
uint64_t user_record_ratelimit_interval_usec(UserRecord *h) {
122✔
2105
        assert(h);
122✔
2106

2107
        if (h->ratelimit_interval_usec == UINT64_MAX)
122✔
2108
                return DEFAULT_RATELIMIT_INTERVAL_USEC;
38✔
2109

2110
        return h->ratelimit_interval_usec;
2111
}
2112

2113
uint64_t user_record_ratelimit_burst(UserRecord *h) {
292✔
2114
        assert(h);
292✔
2115

2116
        if (h->ratelimit_burst == UINT64_MAX)
292✔
2117
                return DEFAULT_RATELIMIT_BURST;
114✔
2118

2119
        return h->ratelimit_burst;
2120
}
2121

2122
bool user_record_can_authenticate(UserRecord *h) {
×
2123
        assert(h);
×
2124

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

2127
        if (h->n_pkcs11_encrypted_key > 0)
×
2128
                return true;
2129

2130
        if (h->n_fido2_hmac_salt > 0)
×
2131
                return true;
2132

2133
        return !strv_isempty(h->hashed_password);
×
2134
}
2135

2136
bool user_record_drop_caches(UserRecord *h) {
203✔
2137
        assert(h);
203✔
2138

2139
        if (h->drop_caches >= 0)
203✔
2140
                return h->drop_caches;
×
2141

2142
        /* By default drop caches on fscrypt, not otherwise. */
2143
        return user_record_storage(h) == USER_FSCRYPT;
203✔
2144
}
2145

2146
AutoResizeMode user_record_auto_resize_mode(UserRecord *h) {
×
2147
        assert(h);
×
2148

2149
        if (h->auto_resize_mode >= 0)
×
2150
                return h->auto_resize_mode;
2151

2152
        return user_record_storage(h) == USER_LUKS ? AUTO_RESIZE_SHRINK_AND_GROW : AUTO_RESIZE_OFF;
×
2153
}
2154

2155
uint64_t user_record_rebalance_weight(UserRecord *h) {
235✔
2156
        assert(h);
235✔
2157

2158
        if (h->rebalance_weight == REBALANCE_WEIGHT_UNSET)
235✔
2159
                return REBALANCE_WEIGHT_DEFAULT;
81✔
2160

2161
        return h->rebalance_weight;
2162
}
2163

2164
static uint64_t parse_caps_strv(char **l) {
×
2165
        uint64_t c = 0;
×
2166
        int r;
×
2167

2168
        STRV_FOREACH(i, l) {
×
2169
                r = capability_from_name(*i);
×
2170
                if (r < 0)
×
2171
                        log_debug_errno(r, "Don't know capability '%s', ignoring: %m", *i);
×
2172
                else
2173
                        c |= UINT64_C(1) << r;
×
2174
        }
2175

2176
        return c;
×
2177
}
2178

2179
uint64_t user_record_capability_bounding_set(UserRecord *h) {
486✔
2180
        assert(h);
486✔
2181

2182
        /* Returns UINT64_MAX if no bounding set is configured (!) */
2183

2184
        if (!h->capability_bounding_set)
486✔
2185
                return UINT64_MAX;
2186

2187
        return parse_caps_strv(h->capability_bounding_set);
×
2188
}
2189

2190
uint64_t user_record_capability_ambient_set(UserRecord *h) {
486✔
2191
        assert(h);
486✔
2192

2193
        /* Returns UINT64_MAX if no ambient set is configured (!) */
2194

2195
        if (!h->capability_ambient_set)
486✔
2196
                return UINT64_MAX;
2197

2198
        return parse_caps_strv(h->capability_ambient_set) & user_record_capability_bounding_set(h);
×
2199
}
2200

2201
int user_record_languages(UserRecord *h, char ***ret) {
486✔
2202
        _cleanup_strv_free_ char **l = NULL;
486✔
2203
        int r;
486✔
2204

2205
        assert(h);
486✔
2206
        assert(ret);
486✔
2207

2208
        if (h->preferred_language) {
486✔
2209
                l = strv_new(h->preferred_language);
×
2210
                if (!l)
×
2211
                        return -ENOMEM;
2212
        }
2213

2214
        r = strv_extend_strv(&l, h->additional_languages, /* filter_duplicates= */ true);
486✔
2215
        if (r < 0)
486✔
2216
                return r;
2217

2218
        *ret = TAKE_PTR(l);
486✔
2219
        return 0;
486✔
2220
}
2221

2222
uint32_t user_record_tmp_limit_scale(UserRecord *h) {
252✔
2223
        assert(h);
252✔
2224

2225
        if (h->tmp_limit.is_set)
252✔
2226
                return h->tmp_limit.limit_scale;
2✔
2227

2228
        /* By default grant regular users only 80% quota */
2229
        if (user_record_disposition(h) == USER_REGULAR)
250✔
2230
                return UINT32_SCALE_FROM_PERCENT(80);
199✔
2231

2232
        return UINT32_MAX;
2233
}
2234

2235
uint32_t user_record_dev_shm_limit_scale(UserRecord *h) {
252✔
2236
        assert(h);
252✔
2237

2238
        if (h->dev_shm_limit.is_set)
252✔
2239
                return h->dev_shm_limit.limit_scale;
2✔
2240

2241
        /* By default grant regular users only 80% quota */
2242
        if (user_record_disposition(h) == USER_REGULAR)
250✔
2243
                return UINT32_SCALE_FROM_PERCENT(80);
199✔
2244

2245
        return UINT32_MAX;
2246
}
2247

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

2259
                /* Basic account settings */
2260
                "shell",
2261
                "umask",
2262
                "environment",
2263
                "timeZone",
2264
                "preferredLanguage",
2265
                "additionalLanguages",
2266
                "preferredSessionLauncher",
2267
                "preferredSessionType",
2268
                "defaultArea",
2269

2270
                /* Authentication methods */
2271
                "pkcs11TokenUri",
2272
                "fido2HmacCredential",
2273
                "recoveryKeyType",
2274

2275
                "lastChangeUSec", /* Necessary to be able to change record at all */
2276
                "lastPasswordChangeUSec", /* Ditto, but for authentication methods */
2277
                NULL
2278
        };
2279

2280
        assert(h);
212✔
2281

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

2286
        /* Note that we intentionally distinguish between NULL and an empty array here */
2287
        if (h->self_modifiable_fields)
212✔
2288
                return (const char**) h->self_modifiable_fields;
2289

2290
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_fields : NULL;
210✔
2291
}
2292

2293
const char** user_record_self_modifiable_blobs(UserRecord *h) {
150✔
2294
        static const char *const default_blobs[] = {
150✔
2295
                /* For display purposes */
2296
                "avatar",
2297
                "login-background",
2298
                NULL
2299
        };
2300

2301
        assert(h);
150✔
2302

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

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

2310
const char** user_record_self_modifiable_privileged(UserRecord *h) {
174✔
2311
        static const char *const default_fields[] = {
174✔
2312
                /* For display purposes */
2313
                "passwordHint",
2314

2315
                /* Authentication methods */
2316
                "hashedPassword",
2317
                "pkcs11EncryptedKey",
2318
                "fido2HmacSalt",
2319
                "recoveryKey",
2320

2321
                "sshAuthorizedKeys", /* Basically just ~/.ssh/authorized_keys */
2322
                NULL
2323
        };
2324

2325
        assert(h);
174✔
2326

2327
        /* Note that we intentionally distinguish between NULL and an empty array here */
2328
        if (h->self_modifiable_privileged)
174✔
2329
                return (const char**) h->self_modifiable_privileged;
2330

2331
        return user_record_disposition(h) == USER_REGULAR ? (const char**) default_fields : NULL;
172✔
2332
}
2333

2334
static int remove_self_modifiable_json_fields_common(UserRecord *current, sd_json_variant **target) {
72✔
2335
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *blobs = NULL;
72✔
2336
        char **allowed;
72✔
2337
        int r;
72✔
2338

2339
        assert(current);
72✔
2340
        assert(target);
72✔
2341

2342
        if (!sd_json_variant_is_object(*target))
72✔
2343
                return -EINVAL;
2344

2345
        v = sd_json_variant_ref(*target);
72✔
2346

2347
        /* Handle basic fields */
2348
        allowed = (char**) user_record_self_modifiable_fields(current);
72✔
2349
        r = sd_json_variant_filter(&v, allowed);
72✔
2350
        if (r < 0)
72✔
2351
                return r;
2352

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

2361
                allowed = (char**) user_record_self_modifiable_blobs(current);
10✔
2362
                r = sd_json_variant_filter(&blobs, allowed);
10✔
2363
                if (r < 0)
10✔
2364
                        return r;
2365

2366
                if (sd_json_variant_is_blank_object(blobs))
10✔
2367
                        r = sd_json_variant_filter(&v, STRV_MAKE("blobManifest"));
×
2368
                else
2369
                        r = sd_json_variant_set_field(&v, "blobManifest", blobs);
10✔
2370
                if (r < 0)
10✔
2371
                        return r;
2372
        }
2373

2374
        JSON_VARIANT_REPLACE(*target, TAKE_PTR(v));
72✔
2375
        return 0;
72✔
2376
}
2377

2378
static int remove_self_modifiable_json_fields(UserRecord *current, UserRecord *h, sd_json_variant **ret) {
44✔
2379
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *privileged = NULL;
44✔
2380
        sd_json_variant *per_machine;
44✔
2381
        char **allowed;
44✔
2382
        int r;
44✔
2383

2384
        assert(current);
44✔
2385
        assert(h);
44✔
2386
        assert(ret);
44✔
2387

2388
        r = user_group_record_mangle(h->json, USER_RECORD_EXTRACT_SIGNABLE|USER_RECORD_PERMISSIVE, &v, NULL);
44✔
2389
        if (r < 0)
44✔
2390
                return r;
2391

2392
        /* Handle the regular section */
2393
        r = remove_self_modifiable_json_fields_common(current, &v);
44✔
2394
        if (r < 0)
44✔
2395
                return r;
2396

2397
        /* Handle the perMachine section */
2398
        per_machine = sd_json_variant_by_key(v, "perMachine");
44✔
2399
        if (per_machine) {
44✔
2400
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *new_per_machine = NULL;
28✔
2401
                sd_json_variant *e;
28✔
2402

2403
                if (!sd_json_variant_is_array(per_machine))
28✔
2404
                        return -EINVAL;
2405

2406
                JSON_VARIANT_ARRAY_FOREACH(e, per_machine) {
63✔
2407
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *z = NULL;
35✔
2408

2409
                        if (!sd_json_variant_is_object(e))
35✔
2410
                                return -EINVAL;
2411

2412
                        r = per_machine_match(e, 0);
35✔
2413
                        if (r < 0)
35✔
2414
                                return r;
2415
                        if (r == 0) {
35✔
2416
                                /* It's only permissible to change anything inside of matching perMachine sections */
2417
                                r = sd_json_variant_append_array(&new_per_machine, e);
7✔
2418
                                if (r < 0)
7✔
2419
                                        return r;
2420
                                continue;
7✔
2421
                        }
2422

2423
                        z = sd_json_variant_ref(e);
28✔
2424

2425
                        r = remove_self_modifiable_json_fields_common(current, &z);
28✔
2426
                        if (r < 0)
28✔
2427
                                return r;
2428

2429
                        if (!sd_json_variant_is_blank_object(z)) {
28✔
2430
                                r = sd_json_variant_append_array(&new_per_machine, z);
28✔
2431
                                if (r < 0)
28✔
2432
                                        return r;
2433
                        }
2434
                }
2435

2436
                if (sd_json_variant_is_blank_array(new_per_machine))
28✔
2437
                        r = sd_json_variant_filter(&v, STRV_MAKE("perMachine"));
×
2438
                else
2439
                        r = sd_json_variant_set_field(&v, "perMachine", new_per_machine);
28✔
2440
                if (r < 0)
28✔
2441
                        return r;
2442
        }
2443

2444
        /* Handle the privileged section */
2445
        privileged = sd_json_variant_ref(sd_json_variant_by_key(v, "privileged"));
44✔
2446
        if (privileged) {
44✔
2447
                allowed = (char**) user_record_self_modifiable_privileged(current);
34✔
2448
                r = sd_json_variant_filter(&privileged, allowed);
34✔
2449
                if (r < 0)
34✔
2450
                        return r;
2451

2452
                if (sd_json_variant_is_blank_object(privileged))
34✔
2453
                        r = sd_json_variant_filter(&v, STRV_MAKE("privileged"));
32✔
2454
                else
2455
                        r = sd_json_variant_set_field(&v, "privileged", privileged);
2✔
2456
                if (r < 0)
34✔
2457
                        return r;
2458
        }
2459

2460
        JSON_VARIANT_REPLACE(*ret, TAKE_PTR(v));
44✔
2461
        return 0;
44✔
2462
}
2463

2464
int user_record_self_changes_allowed(UserRecord *current, UserRecord *incoming) {
22✔
2465
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *vc = NULL, *vi = NULL;
44✔
2466
        int r;
22✔
2467

2468
        assert(current);
22✔
2469
        assert(incoming);
22✔
2470

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

2476
        r = remove_self_modifiable_json_fields(current, current, &vc);
22✔
2477
        if (r < 0)
22✔
2478
                return r;
2479

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

2497
        return sd_json_variant_equal(vc, vi);
22✔
2498
}
2499

2500
uint64_t user_record_ratelimit_next_try(UserRecord *h) {
291✔
2501
        assert(h);
291✔
2502

2503
        /* Calculates when the it's possible to login next. Returns:
2504
         *
2505
         * UINT64_MAX → Nothing known
2506
         * 0          → Right away
2507
         * Any other  → Next time in CLOCK_REALTIME in usec (which could be in the past)
2508
         */
2509

2510
        if (h->ratelimit_begin_usec == UINT64_MAX ||
291✔
2511
            h->ratelimit_count == UINT64_MAX)
177✔
2512
                return UINT64_MAX;
2513

2514
        if (h->ratelimit_begin_usec > now(CLOCK_REALTIME)) /* If the ratelimit time is in the future, then
177✔
2515
                                                            * the local clock is probably incorrect. Let's
2516
                                                            * not refuse login then. */
2517
                return UINT64_MAX;
2518

2519
        if (h->ratelimit_count < user_record_ratelimit_burst(h))
177✔
2520
                return 0;
2521

2522
        return usec_add(h->ratelimit_begin_usec, user_record_ratelimit_interval_usec(h));
×
2523
}
2524

2525
bool user_record_equal(UserRecord *a, UserRecord *b) {
141✔
2526
        assert(a);
141✔
2527
        assert(b);
141✔
2528

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

2532
        return sd_json_variant_equal(a->json, b->json);
141✔
2533
}
2534

2535
bool user_record_compatible(UserRecord *a, UserRecord *b) {
129✔
2536
        assert(a);
129✔
2537
        assert(b);
129✔
2538

2539
        /* If either lacks the regular section, we can't really decide, let's hence say they are
2540
         * incompatible. */
2541
        if (!(a->mask & b->mask & USER_RECORD_REGULAR))
129✔
2542
                return false;
2543

2544
        return streq_ptr(a->user_name, b->user_name) &&
129✔
2545
                streq_ptr(a->realm, b->realm);
129✔
2546
}
2547

2548
int user_record_compare_last_change(UserRecord *a, UserRecord *b) {
16✔
2549
        assert(a);
16✔
2550
        assert(b);
16✔
2551

2552
        if (a->last_change_usec == b->last_change_usec)
16✔
2553
                return 0;
2554

2555
        /* Always consider a record with a timestamp newer than one without */
2556
        if (a->last_change_usec == UINT64_MAX)
16✔
2557
                return -1;
2558
        if (b->last_change_usec == UINT64_MAX)
16✔
2559
                return 1;
2560

2561
        return CMP(a->last_change_usec, b->last_change_usec);
16✔
2562
}
2563

2564
int user_record_clone(UserRecord *h, UserRecordLoadFlags flags, UserRecord **ret) {
2,584✔
2565
        _cleanup_(user_record_unrefp) UserRecord *c = NULL;
2,584✔
2566
        int r;
2,584✔
2567

2568
        assert(h);
2,584✔
2569
        assert(ret);
2,584✔
2570

2571
        c = user_record_new();
2,584✔
2572
        if (!c)
2,584✔
2573
                return -ENOMEM;
2574

2575
        r = user_record_load(c, h->json, flags);
2,584✔
2576
        if (r < 0)
2,584✔
2577
                return r;
2578

2579
        *ret = TAKE_PTR(c);
2,584✔
2580
        return 0;
2,584✔
2581
}
2582

2583
int user_record_masked_equal(UserRecord *a, UserRecord *b, UserRecordMask mask) {
22✔
2584
        _cleanup_(user_record_unrefp) UserRecord *x = NULL, *y = NULL;
22✔
2585
        int r;
22✔
2586

2587
        assert(a);
22✔
2588
        assert(b);
22✔
2589

2590
        /* Compares the two records, but ignores anything not listed in the specified mask */
2591

2592
        if ((a->mask & ~mask) != 0) {
22✔
2593
                r = user_record_clone(a, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &x);
22✔
2594
                if (r < 0)
22✔
2595
                        return r;
2596

2597
                a = x;
22✔
2598
        }
2599

2600
        if ((b->mask & ~mask) != 0) {
22✔
2601
                r = user_record_clone(b, USER_RECORD_ALLOW(mask) | USER_RECORD_STRIP(~mask & _USER_RECORD_MASK_MAX) | USER_RECORD_PERMISSIVE, &y);
22✔
2602
                if (r < 0)
22✔
2603
                        return r;
2604

2605
                b = y;
22✔
2606
        }
2607

2608
        return user_record_equal(a, b);
22✔
2609
}
2610

2611
int user_record_test_blocked(UserRecord *h) {
190✔
2612
        usec_t n;
190✔
2613

2614
        /* Checks whether access to the specified user shall be allowed at the moment. Returns:
2615
         *
2616
         *          -ESTALE: Record is from the future
2617
         *          -ENOLCK: Record is blocked
2618
         *          -EL2HLT: Record is not valid yet
2619
         *          -EL3HLT: Record is not valid anymore
2620
         *
2621
         */
2622

2623
        assert(h);
190✔
2624

2625
        if (h->locked > 0)
190✔
2626
                return -ENOLCK;
2627

2628
        n = now(CLOCK_REALTIME);
167✔
2629

2630
        if (h->not_before_usec != UINT64_MAX && n < h->not_before_usec)
167✔
2631
                return -EL2HLT;
2632
        if (h->not_after_usec != UINT64_MAX && n > h->not_after_usec)
167✔
2633
                return -EL3HLT;
2634

2635
        if (h->last_change_usec != UINT64_MAX &&
167✔
2636
            h->last_change_usec > n) /* Complain during log-ins when the record is from the future */
2637
                return -ESTALE;
×
2638

2639
        return 0;
2640
}
2641

2642
int user_record_test_password_change_required(UserRecord *h) {
192✔
2643
        bool change_permitted;
192✔
2644
        usec_t n;
192✔
2645

2646
        assert(h);
192✔
2647

2648
        /* Checks whether the user must change the password when logging in
2649

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

2660
        /* If a password change request has been set explicitly, it overrides everything */
2661
        if (h->password_change_now > 0)
192✔
2662
                return -EKEYREVOKED;
2663

2664
        n = now(CLOCK_REALTIME);
192✔
2665

2666
        /* Password change in the future? Then our RTC is likely incorrect */
2667
        if (h->last_password_change_usec != UINT64_MAX &&
192✔
2668
            h->last_password_change_usec > n &&
×
2669
            (h->password_change_min_usec != UINT64_MAX ||
×
2670
             h->password_change_max_usec != UINT64_MAX ||
×
2671
             h->password_change_inactive_usec != UINT64_MAX))
×
2672
            return -ESTALE;
2673

2674
        /* Then, let's check if password changing is currently allowed at all */
2675
        if (h->password_change_min_usec != UINT64_MAX) {
192✔
2676

2677
                /* Expiry configured but no password change timestamp known? */
2678
                if (h->last_password_change_usec == UINT64_MAX)
×
2679
                        return -ENETDOWN;
2680

2681
                if (h->password_change_min_usec >= UINT64_MAX - h->last_password_change_usec)
×
2682
                        change_permitted = false;
2683
                else
2684
                        change_permitted = n >= h->last_password_change_usec + h->password_change_min_usec;
×
2685

2686
        } else
2687
                change_permitted = true;
2688

2689
        /* Let's check whether the password has expired.  */
2690
        if (!(h->password_change_max_usec == UINT64_MAX ||
192✔
2691
              h->password_change_max_usec >= UINT64_MAX - h->last_password_change_usec)) {
×
2692

2693
                uint64_t change_before;
×
2694

2695
                /* Expiry configured but no password change timestamp known? */
2696
                if (h->last_password_change_usec == UINT64_MAX)
×
2697
                        return -ENETDOWN;
2698

2699
                /* Password is in inactive phase? */
2700
                if (h->password_change_inactive_usec != UINT64_MAX &&
×
2701
                    h->password_change_inactive_usec < UINT64_MAX - h->password_change_max_usec) {
×
2702
                        usec_t added;
×
2703

2704
                        added = h->password_change_inactive_usec + h->password_change_max_usec;
×
2705
                        if (added < UINT64_MAX - h->last_password_change_usec &&
×
2706
                            n >= h->last_password_change_usec + added)
×
2707
                                return -EKEYREJECTED;
2708
                }
2709

2710
                /* Password needs to be changed now? */
2711
                change_before = h->last_password_change_usec + h->password_change_max_usec;
×
2712
                if (n >= change_before)
×
2713
                        return change_permitted ? -EOWNERDEAD : -EKEYREJECTED;
×
2714

2715
                /* Warn user? */
2716
                if (h->password_change_warn_usec != UINT64_MAX &&
×
2717
                    (change_before < h->password_change_warn_usec ||
×
2718
                     n >= change_before - h->password_change_warn_usec))
×
2719
                        return change_permitted ? -EKEYEXPIRED : -EROFS;
×
2720
        }
2721

2722
        /* No password changing necessary */
2723
        return change_permitted ? 0 : -EROFS;
192✔
2724
}
2725

2726
bool user_record_is_root(const UserRecord *u) {
4,887✔
2727
        assert(u);
4,887✔
2728

2729
        return u->uid == 0 || streq_ptr(u->user_name, "root");
4,887✔
2730
}
2731

2732
bool user_record_is_nobody(const UserRecord *u) {
2,700✔
2733
        assert(u);
2,700✔
2734

2735
        return u->uid == UID_NOBODY || STRPTR_IN_SET(u->user_name, NOBODY_USER_NAME, "nobody");
2,700✔
2736
}
2737

2738
bool user_record_matches_user_name(const UserRecord *u, const char *user_name) {
2,985✔
2739
        assert(u);
2,985✔
2740
        assert(user_name);
2,985✔
2741

2742
        if (streq_ptr(u->user_name, user_name))
2,985✔
2743
                return true;
2744

2745
        if (streq_ptr(u->user_name_and_realm_auto, user_name))
24✔
2746
                return true;
2747

2748
        if (strv_contains(u->aliases, user_name))
18✔
2749
                return true;
2750

2751
        const char *realm = strrchr(user_name, '@');
12✔
2752
        if (realm && streq_ptr(realm+1, u->realm))
12✔
2753
                STRV_FOREACH(a, u->aliases)
18✔
2754
                        if (startswith(user_name, *a) == realm)
18✔
2755
                                return true;
2756

2757
        return false;
2758
}
2759

2760
int suitable_blob_filename(const char *name) {
429✔
2761
        /* Enforces filename requirements as described in docs/USER_RECORD_BULK_DIRS.md */
2762
        return filename_is_valid(name) &&
858✔
2763
               in_charset(name, URI_UNRESERVED) &&
429✔
2764
               name[0] != '.';
423✔
2765
}
2766

2767
bool userdb_match_is_set(const UserDBMatch *match) {
49,129✔
2768
        if (!match)
49,129✔
2769
                return false;
2770

2771
        return !strv_isempty(match->fuzzy_names) ||
31,861✔
2772
                !FLAGS_SET(match->disposition_mask, USER_DISPOSITION_MASK_ALL) ||
31,765✔
2773
                match->uid_min > 0 ||
47,169✔
2774
                match->uid_max < UID_INVALID-1;
15,308✔
2775
}
2776

2777
void userdb_match_done(UserDBMatch *match) {
22,394✔
2778
        assert(match);
22,394✔
2779
        strv_free(match->fuzzy_names);
22,394✔
2780
}
22,394✔
2781

2782
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches) {
116✔
2783
        assert(names || n_names == 0);
116✔
2784

2785
        /* Checks if any of the user record strings in the names[] array matches any of the search strings in
2786
         * the matches** strv fuzzily. */
2787

2788
        FOREACH_ARRAY(n, names, n_names) {
436✔
2789
                if (!*n)
320✔
2790
                        continue;
125✔
2791

2792
                _cleanup_free_ char *lcn = strdup(*n);
195✔
2793
                if (!lcn)
195✔
2794
                        return -ENOMEM;
2795

2796
                ascii_strlower(lcn);
195✔
2797

2798
                STRV_FOREACH(i, matches) {
390✔
2799
                        _cleanup_free_ char *lc = strdup(*i);
195✔
2800
                        if (!lc)
195✔
2801
                                return -ENOMEM;
2802

2803
                        ascii_strlower(lc);
195✔
2804

2805
                        /* First do substring check */
2806
                        if (strstr(lcn, lc))
195✔
2807
                                return true;
2808

2809
                        /* Then do some fuzzy string comparison (but only if the needle is non-trivially long) */
2810
                        if (strlen(lc) >= 5 && strlevenshtein(lcn, lc) < 3)
195✔
2811
                                return true;
2812
                }
2813
        }
2814

2815
        return false;
2816
}
2817

2818
bool user_record_match(UserRecord *u, const UserDBMatch *match) {
7,657✔
2819
        assert(u);
7,657✔
2820

2821
        if (!match)
7,657✔
2822
                return true;
2823

2824
        if (!uid_is_valid(u->uid))
5,781✔
2825
                return false;
2826

2827
        if (u->uid < match->uid_min || u->uid > match->uid_max)
5,781✔
2828
                return false;
2829

2830
        if (!BIT_SET(match->disposition_mask, user_record_disposition(u)))
5,753✔
2831
                return false;
2832

2833
        if (!strv_isempty(match->fuzzy_names)) {
5,689✔
2834

2835
                /* Note this array of names is sparse, i.e. various entries listed in it will be
2836
                 * NULL. Because of that we are not using a NULL terminated strv here, but a regular
2837
                 * array. */
2838
                const char* names[] = {
56✔
2839
                        u->user_name,
28✔
2840
                        user_record_user_name_and_realm(u),
28✔
2841
                        u->real_name,
28✔
2842
                        u->email_address,
28✔
2843
                        u->cifs_user_name,
28✔
2844
                };
2845

2846
                if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names) &&
56✔
2847
                    !user_name_fuzzy_match((const char**) u->aliases, strv_length(u->aliases), match->fuzzy_names))
28✔
2848
                        return false;
28✔
2849
        }
2850

2851
        return true;
2852
}
2853

2854
int json_dispatch_dispositions_mask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
7,897✔
2855
        uint64_t *mask = ASSERT_PTR(userdata);
7,897✔
2856

2857
        if (sd_json_variant_is_null(variant)) {
7,897✔
2858
                *mask = UINT64_MAX;
×
2859
                return 0;
×
2860
        }
2861

2862
        if (!sd_json_variant_is_array(variant))
7,897✔
2863
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
×
2864

2865
        uint64_t m = 0;
2866
        for (size_t i = 0; i < sd_json_variant_elements(variant); i++) {
23,687✔
2867
                sd_json_variant *e;
15,790✔
2868
                const char *a;
15,790✔
2869

2870
                e = sd_json_variant_by_index(variant, i);
15,790✔
2871
                if (!sd_json_variant_is_string(e))
15,790✔
2872
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
×
2873

2874
                assert_se(a = sd_json_variant_string(e));
15,790✔
2875

2876
                UserDisposition d = user_disposition_from_string(a);
15,790✔
2877
                if (d < 0)
15,790✔
2878
                        return json_log(e, flags, d, "JSON field '%s' contains an invalid user disposition type: %s", strna(name), a);
×
2879

2880
                m |= INDEX_TO_MASK(uint64_t, d);
15,790✔
2881
        }
2882

2883
        *mask = m;
7,897✔
2884
        return 0;
7,897✔
2885
}
2886

2887
static const char* const user_storage_table[_USER_STORAGE_MAX] = {
2888
        [USER_CLASSIC]   = "classic",
2889
        [USER_LUKS]      = "luks",
2890
        [USER_DIRECTORY] = "directory",
2891
        [USER_SUBVOLUME] = "subvolume",
2892
        [USER_FSCRYPT]   = "fscrypt",
2893
        [USER_CIFS]      = "cifs",
2894
};
2895

2896
DEFINE_STRING_TABLE_LOOKUP(user_storage, UserStorage);
3,069✔
2897

2898
static const char* const user_disposition_table[_USER_DISPOSITION_MAX] = {
2899
        [USER_INTRINSIC] = "intrinsic",
2900
        [USER_SYSTEM]    = "system",
2901
        [USER_DYNAMIC]   = "dynamic",
2902
        [USER_REGULAR]   = "regular",
2903
        [USER_CONTAINER] = "container",
2904
        [USER_FOREIGN]   = "foreign",
2905
        [USER_RESERVED]  = "reserved",
2906
};
2907

2908
DEFINE_STRING_TABLE_LOOKUP(user_disposition, UserDisposition);
60,785✔
2909

2910
static const char* const auto_resize_mode_table[_AUTO_RESIZE_MODE_MAX] = {
2911
        [AUTO_RESIZE_OFF]             = "off",
2912
        [AUTO_RESIZE_GROW]            = "grow",
2913
        [AUTO_RESIZE_SHRINK_AND_GROW] = "shrink-and-grow",
2914
};
2915

2916
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