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

systemd / systemd / 13731561307

07 Mar 2025 09:19PM UTC coverage: 71.816% (+0.05%) from 71.765%
13731561307

push

github

web-flow
homed: add key management toolchain (#36513)

if Lennart shall dogfood ParticleOS he needs acceptable tooling for
making his homed home dir accessible from his legacy fedora install, and
from local and remote particleos. Let's add explicit support for
scenarios like this:

1. add high level support for enrolling the account signing key from the
fedora install
2. add high level support for "adopting" a local but foreign .home file
on a system
3. add high level support for "registering" a remote user account on a
different system

(this lacks test cases and some docs, hence marked as wip)

511 of 676 new or added lines in 12 files covered. (75.59%)

1096 existing lines in 54 files now uncovered.

295403 of 411331 relevant lines covered (71.82%)

718703.82 hits per line

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

86.53
/src/shared/serialize.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4

5
#include "alloc-util.h"
6
#include "env-util.h"
7
#include "escape.h"
8
#include "fd-util.h"
9
#include "fileio.h"
10
#include "hexdecoct.h"
11
#include "memfd-util.h"
12
#include "missing_mman.h"
13
#include "missing_syscall.h"
14
#include "parse-util.h"
15
#include "process-util.h"
16
#include "serialize.h"
17
#include "strv.h"
18
#include "tmpfile-util.h"
19

20
int serialize_item(FILE *f, const char *key, const char *value) {
771,799✔
21
        assert(f);
771,799✔
22
        assert(key);
771,799✔
23

24
        if (!value)
771,799✔
25
                return 0;
26

27
        /* Make sure that anything we serialize we can also read back again with read_line() with a maximum line size
28
         * of LONG_LINE_MAX. This is a safety net only. All code calling us should filter this out earlier anyway. */
29
        if (strlen(key) + 1 + strlen(value) + 1 > LONG_LINE_MAX)
676,409✔
30
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Attempted to serialize overly long item '%s', refusing.", key);
7✔
31

32
        fputs(key, f);
676,402✔
33
        fputc('=', f);
676,402✔
34
        fputs(value, f);
676,402✔
35
        fputc('\n', f);
676,402✔
36

37
        return 1;
676,402✔
38
}
39

40
int serialize_item_escaped(FILE *f, const char *key, const char *value) {
58,459✔
41
        _cleanup_free_ char *c = NULL;
58,459✔
42

43
        assert(f);
58,459✔
44
        assert(key);
58,459✔
45

46
        if (!value)
58,459✔
47
                return 0;
48

49
        c = xescape(value, " ");
37,163✔
50
        if (!c)
37,163✔
51
                return log_oom();
×
52

53
        return serialize_item(f, key, c);
37,163✔
54
}
55

56
int serialize_item_format(FILE *f, const char *key, const char *format, ...) {
586,279✔
57
        _cleanup_free_ char *allocated = NULL;
586,279✔
58
        char buf[256]; /* Something reasonably short that fits nicely on any stack (i.e. is considerably less
586,279✔
59
                        * than LONG_LINE_MAX (1MiB!) */
60
        const char *b;
586,279✔
61
        va_list ap;
586,279✔
62
        int k;
586,279✔
63

64
        assert(f);
586,279✔
65
        assert(key);
586,279✔
66
        assert(format);
586,279✔
67

68
        /* First, let's try to format this into a stack buffer */
69
        va_start(ap, format);
586,279✔
70
        k = vsnprintf(buf, sizeof(buf), format, ap);
586,279✔
71
        va_end(ap);
586,279✔
72

73
        if (k < 0)
586,279✔
74
                return log_warning_errno(errno, "Failed to serialize item '%s', ignoring: %m", key);
×
75
        if (strlen(key) + 1 + k + 1 > LONG_LINE_MAX) /* See above */
586,279✔
76
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Attempted to serialize overly long item '%s', refusing.", key);
×
77

78
        if ((size_t) k < sizeof(buf))
586,279✔
79
                b = buf; /* Yay, it fit! */
80
        else {
81
                /* So the string didn't fit in the short buffer above, but was not above our total limit,
82
                 * hence let's format it via dynamic memory */
83

84
                va_start(ap, format);
×
85
                k = vasprintf(&allocated, format, ap);
×
86
                va_end(ap);
×
87

88
                if (k < 0)
×
89
                        return log_warning_errno(errno, "Failed to serialize item '%s', ignoring: %m", key);
×
90

91
                b = allocated;
×
92
        }
93

94
        fputs(key, f);
586,279✔
95
        fputc('=', f);
586,279✔
96
        fputs(b, f);
586,279✔
97
        fputc('\n', f);
586,279✔
98

99
        return 1;
100
}
101

102
int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd) {
72,461✔
103
        int copy;
72,461✔
104

105
        assert(f);
72,461✔
106
        assert(fds);
72,461✔
107
        assert(key);
72,461✔
108

109
        if (fd < 0)
72,461✔
110
                return 0;
111

112
        copy = fdset_put_dup(fds, fd);
11,129✔
113
        if (copy < 0)
11,129✔
114
                return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
×
115

116
        return serialize_item_format(f, key, "%i", copy);
11,129✔
117
}
118

119
int serialize_fd_many(FILE *f, FDSet *fds, const char *key, const int fd_array[], size_t n_fd_array) {
875✔
120
        _cleanup_free_ char *t = NULL;
875✔
121

122
        assert(f);
875✔
123

124
        if (n_fd_array == 0)
875✔
125
                return 0;
126

127
        assert(fd_array);
875✔
128

129
        for (size_t i = 0; i < n_fd_array; i++) {
2,477✔
130
                int copy;
1,602✔
131

132
                if (fd_array[i] < 0)
1,602✔
133
                        return -EBADF;
134

135
                copy = fdset_put_dup(fds, fd_array[i]);
1,602✔
136
                if (copy < 0)
1,602✔
137
                        return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
×
138

139
                if (strextendf_with_separator(&t, " ", "%i", copy) < 0)
1,602✔
140
                        return log_oom();
×
141
        }
142

143
        return serialize_item(f, key, t);
875✔
144
}
145

146
int serialize_usec(FILE *f, const char *key, usec_t usec) {
35,396✔
147
        assert(f);
35,396✔
148
        assert(key);
35,396✔
149

150
        if (usec == USEC_INFINITY)
35,396✔
151
                return 0;
152

153
        return serialize_item_format(f, key, USEC_FMT, usec);
8,358✔
154
}
155

156
int serialize_dual_timestamp(FILE *f, const char *name, const dual_timestamp *t) {
235,420✔
157
        assert(f);
235,420✔
158
        assert(name);
235,420✔
159
        assert(t);
235,420✔
160

161
        if (!dual_timestamp_is_set(t))
235,420✔
162
                return 0;
163

164
        return serialize_item_format(f, name, USEC_FMT " " USEC_FMT, t->realtime, t->monotonic);
109,117✔
165
}
166

167
int serialize_strv(FILE *f, const char *key, char * const *l) {
70,021✔
168
        int ret = 0, r;
70,021✔
169

170
        /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
171

172
        assert(f);
70,021✔
173
        assert(key);
70,021✔
174

175
        STRV_FOREACH(i, l) {
105,630✔
176
                r = serialize_item_escaped(f, key, *i);
35,609✔
177
                if ((ret >= 0 && r < 0) ||
35,609✔
178
                    (ret == 0 && r > 0))
35,608✔
179
                        ret = r;
9,792✔
180
        }
181

182
        return ret;
70,021✔
183
}
184

185
int serialize_id128(FILE *f, const char *key, sd_id128_t id) {
30,229✔
186
        assert(f);
30,229✔
187
        assert(key);
30,229✔
188

189
        if (sd_id128_is_null(id))
30,229✔
190
                return 0;
12,159✔
191

192
        return serialize_item_format(f, key, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
18,070✔
193
}
194

195
int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
19,971✔
196
        int r;
19,971✔
197

198
        assert(f);
19,971✔
199
        assert(fds);
19,971✔
200

201
        if (!pidref_is_set(pidref))
19,971✔
202
                return 0;
203

204
        /* We always serialize the pid separately, to keep downgrades mostly working (older versions will
205
         * deserialize the pid and silently fail to deserialize the pidfd). If we also have a pidfd, we
206
         * serialize both the pid and pidfd, so that we can construct the exact same pidref after
207
         * deserialization (this doesn't work with only the pidfd, as we can't retrieve the original pid
208
         * from the pidfd anymore if the process is reaped). */
209

210
        if (pidref->fd >= 0) {
858✔
211
                int copy = fdset_put_dup(fds, pidref->fd);
858✔
212
                if (copy < 0)
858✔
213
                        return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
×
214

215
                r = serialize_item_format(f, key, "@%i:" PID_FMT, copy, pidref->pid);
858✔
216
                if (r < 0)
858✔
217
                        return r;
218
        }
219

220
        return serialize_item_format(f, key, PID_FMT, pidref->pid);
858✔
221
}
222

223
int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
64,153✔
224
        assert(rl);
64,153✔
225

226
        return serialize_item_format(f, key,
128,306✔
227
                                     USEC_FMT " " USEC_FMT " %u %u",
228
                                     rl->begin,
64,153✔
229
                                     rl->interval,
64,153✔
230
                                     rl->num,
64,153✔
231
                                     rl->burst);
64,153✔
232
}
233

234
int serialize_item_hexmem(FILE *f, const char *key, const void *p, size_t l) {
3,825✔
235
        _cleanup_free_ char *encoded = NULL;
3,825✔
236
        int r;
3,825✔
237

238
        assert(f);
3,825✔
239
        assert(key);
3,825✔
240

241
        if (!p && l > 0)
3,825✔
242
                return -EINVAL;
243

244
        if (l == 0)
3,825✔
245
                return 0;
246

247
        encoded = hexmem(p, l);
1✔
248
        if (!encoded)
1✔
249
                return log_oom_debug();
×
250

251
        r = serialize_item(f, key, encoded);
1✔
252
        if (r < 0)
1✔
253
                return r;
×
254

255
        return 1;
256
}
257

258
int serialize_item_base64mem(FILE *f, const char *key, const void *p, size_t l) {
7,648✔
259
        _cleanup_free_ char *encoded = NULL;
7,648✔
260
        ssize_t len;
7,648✔
261
        int r;
7,648✔
262

263
        assert(f);
7,648✔
264
        assert(key);
7,648✔
265

266
        if (!p && l > 0)
7,648✔
267
                return -EINVAL;
268

269
        if (l == 0)
7,648✔
270
                return 0;
271

272
        len = base64mem(p, l, &encoded);
1✔
273
        if (len <= 0)
1✔
274
                return log_oom_debug();
×
275

276
        r = serialize_item(f, key, encoded);
1✔
277
        if (r < 0)
1✔
278
                return r;
×
279

280
        return 1;
281
}
282

283
int serialize_string_set(FILE *f, const char *key, const Set *s) {
7,648✔
284
        int r;
7,648✔
285

286
        assert(f);
7,648✔
287
        assert(key);
7,648✔
288

289
        if (set_isempty(s))
7,648✔
290
                return 0;
7,648✔
291

292
        /* Serialize as individual items, as each element might contain separators and escapes */
293

294
        const char *e;
1✔
295
        SET_FOREACH(e, s) {
3✔
296
                r = serialize_item(f, key, e);
2✔
297
                if (r < 0)
2✔
298
                        return r;
×
299
        }
300

301
        return 1;
1✔
302
}
303

304
int serialize_image_policy(FILE *f, const char *key, const ImagePolicy *p) {
11,469✔
305
        _cleanup_free_ char *policy = NULL;
11,469✔
306
        int r;
11,469✔
307

308
        assert(f);
11,469✔
309
        assert(key);
11,469✔
310

311
        if (!p)
11,469✔
312
                return 0;
313

314
        r = image_policy_to_string(p, /* simplify= */ false, &policy);
×
315
        if (r < 0)
×
316
                return r;
317

318
        r = serialize_item(f, key, policy);
×
319
        if (r < 0)
×
320
                return r;
×
321

322
        return 1;
323
}
324

325
int deserialize_read_line(FILE *f, char **ret) {
2,622,326✔
326
        _cleanup_free_ char *line = NULL;
2,622,326✔
327
        int r;
2,622,326✔
328

329
        assert(f);
2,622,326✔
330
        assert(ret);
2,622,326✔
331

332
        r = read_stripped_line(f, LONG_LINE_MAX, &line);
2,622,326✔
333
        if (r < 0)
2,622,326✔
334
                return log_error_errno(r, "Failed to read serialization line: %m");
×
335
        if (r == 0) { /* eof */
2,622,326✔
336
                *ret = NULL;
×
337
                return 0;
×
338
        }
339

340
        if (isempty(line)) { /* End marker */
2,622,326✔
341
                *ret = NULL;
81,464✔
342
                return 0;
81,464✔
343
        }
344

345
        *ret = TAKE_PTR(line);
2,540,862✔
346
        return 1;
2,540,862✔
347
}
348

349
int deserialize_fd(FDSet *fds, const char *value) {
48,025✔
350
        _cleanup_close_ int our_fd = -EBADF;
48,025✔
351
        int parsed_fd;
48,025✔
352

353
        assert(value);
48,025✔
354

355
        parsed_fd = parse_fd(value);
48,025✔
356
        if (parsed_fd < 0)
48,025✔
357
                return log_debug_errno(parsed_fd, "Failed to parse file descriptor serialization: %s", value);
×
358

359
        our_fd = fdset_remove(fds, parsed_fd); /* Take possession of the fd */
48,025✔
360
        if (our_fd < 0)
48,025✔
361
                return log_debug_errno(our_fd, "Failed to acquire fd from serialization fds: %m");
×
362

363
        return TAKE_FD(our_fd);
364
}
365

366
int deserialize_fd_many(FDSet *fds, const char *value, size_t n, int *ret) {
2,761✔
367
        int r, *fd_array = NULL;
2,761✔
368
        size_t m = 0;
2,761✔
369

370
        assert(value);
2,761✔
371

372
        fd_array = new(int, n);
2,761✔
373
        if (!fd_array)
2,761✔
374
                return -ENOMEM;
2,761✔
375

376
        CLEANUP_ARRAY(fd_array, m, close_many_and_free);
5,522✔
377

378
        for (;;) {
13,247✔
379
                _cleanup_free_ char *w = NULL;
5,243✔
380
                int fd;
8,004✔
381

382
                r = extract_first_word(&value, &w, NULL, 0);
8,004✔
383
                if (r < 0)
8,004✔
384
                        return r;
385
                if (r == 0) {
8,004✔
386
                        if (m < n) /* Too few */
2,761✔
387
                                return -EINVAL;
388

389
                        break;
2,761✔
390
                }
391

392
                if (m >= n) /* Too many */
5,243✔
393
                        return -EINVAL;
394

395
                fd = deserialize_fd(fds, w);
5,243✔
396
                if (fd < 0)
5,243✔
397
                        return fd;
398

399
                fd_array[m++] = fd;
5,243✔
400
        }
401

402
        memcpy(ret, fd_array, m * sizeof(int));
2,761✔
403
        fd_array = mfree(fd_array);
2,761✔
404

405
        return 0;
2,761✔
406
}
407

408
int deserialize_strv(const char *value, char ***l) {
105,292✔
409
        ssize_t unescaped_len;
105,292✔
410
        char *unescaped;
105,292✔
411

412
        assert(l);
105,292✔
413
        assert(value);
105,292✔
414

415
        unescaped_len = cunescape(value, 0, &unescaped);
105,292✔
416
        if (unescaped_len < 0)
105,292✔
417
                return unescaped_len;
×
418

419
        return strv_consume(l, unescaped);
105,292✔
420
}
421

422
int deserialize_usec(const char *value, usec_t *ret) {
18,618✔
423
        int r;
18,618✔
424

425
        assert(value);
18,618✔
426
        assert(ret);
18,618✔
427

428
        r = safe_atou64(value, ret);
18,618✔
429
        if (r < 0)
18,618✔
430
                return log_debug_errno(r, "Failed to parse usec value \"%s\": %m", value);
×
431

432
        return 0;
433
}
434

435
int deserialize_dual_timestamp(const char *value, dual_timestamp *ret) {
51,540✔
436
        uint64_t a, b;
51,540✔
437
        int r, pos;
51,540✔
438

439
        assert(value);
51,540✔
440
        assert(ret);
51,540✔
441

442
        pos = strspn(value, WHITESPACE);
51,540✔
443
        if (value[pos] == '-')
51,540✔
444
                return -EINVAL;
51,540✔
445
        pos += strspn(value + pos, DIGITS);
51,539✔
446
        pos += strspn(value + pos, WHITESPACE);
51,539✔
447
        if (value[pos] == '-')
51,539✔
448
                return -EINVAL;
449

450
        r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
51,538✔
451
        if (r != 2)
51,538✔
452
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
453
                                       "Failed to parse dual timestamp value \"%s\".",
454
                                       value);
455

456
        if (value[pos] != '\0')
51,537✔
457
                /* trailing garbage */
458
                return -EINVAL;
459

460
        *ret = (dual_timestamp) {
51,536✔
461
                .realtime = a,
462
                .monotonic = b,
463
        };
464

465
        return 0;
51,536✔
466
}
467

468
int deserialize_environment(const char *value, char ***list) {
512✔
469
        _cleanup_free_ char *unescaped = NULL;
512✔
470
        ssize_t l;
512✔
471
        int r;
512✔
472

473
        assert(value);
512✔
474
        assert(list);
512✔
475

476
        /* Changes the *environment strv inline. */
477

478
        l = cunescape(value, 0, &unescaped);
512✔
479
        if (l < 0)
512✔
480
                return log_error_errno(l, "Failed to unescape: %m");
2✔
481

482
        r = strv_env_replace_consume(list, TAKE_PTR(unescaped));
510✔
483
        if (r < 0)
510✔
484
                return log_error_errno(r, "Failed to append environment variable: %m");
×
485

486
        return 0;
487
}
488

489
int deserialize_pidref(FDSet *fds, const char *value, PidRef *ret) {
624✔
490
        const char *e;
624✔
491
        int r;
624✔
492

493
        assert(value);
624✔
494
        assert(ret);
624✔
495

496
        e = startswith(value, "@");
624✔
497
        if (e) {
624✔
498
                _cleanup_free_ char *fdstr = NULL, *pidstr = NULL;
624✔
499
                _cleanup_close_ int fd = -EBADF;
624✔
500

501
                r = extract_many_words(&e, ":", /* flags = */ 0, &fdstr, &pidstr);
624✔
502
                if (r < 0)
624✔
503
                        return log_debug_errno(r, "Failed to deserialize pidref '%s': %m", e);
×
504
                if (r == 0)
624✔
505
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot deserialize pidref from empty string.");
×
506

507
                assert(r <= 2);
624✔
508

509
                fd = deserialize_fd(fds, fdstr);
624✔
510
                if (fd < 0)
624✔
511
                        return fd;
512

513
                /* The serialization format changed after 255.4. In systemd <= 255.4 only pidfd is
514
                 * serialized, but that causes problems when reconstructing pidref (see serialize_pidref for
515
                 * details). After 255.4 the pid is serialized as well even if we have a pidfd, but we still
516
                 * need to support older format as we might be upgrading from a version that still uses the
517
                 * old format. */
518
                if (pidstr) {
624✔
519
                        pid_t pid;
624✔
520

521
                        r = parse_pid(pidstr, &pid);
624✔
522
                        if (r < 0)
624✔
523
                                return log_debug_errno(r, "Failed to parse PID: %s", pidstr);
×
524

525
                        *ret = (PidRef) {
624✔
526
                                .pid = pid,
527
                                .fd = TAKE_FD(fd),
624✔
528
                        };
529
                } else
530
                        r = pidref_set_pidfd_consume(ret, TAKE_FD(fd));
×
531
        } else {
UNCOV
532
                pid_t pid;
×
533

UNCOV
534
                r = parse_pid(value, &pid);
×
UNCOV
535
                if (r < 0)
×
536
                        return log_debug_errno(r, "Failed to parse PID: %s", value);
×
537

UNCOV
538
                r = pidref_set_pid(ret, pid);
×
539
        }
540
        if (r < 0)
624✔
541
                return log_debug_errno(r, "Failed to initialize pidref: %m");
×
542

543
        return 0;
544
}
545

546
void deserialize_ratelimit(RateLimit *rl, const char *name, const char *value) {
29,565✔
547
        usec_t begin, interval;
29,565✔
548
        unsigned num, burst;
29,565✔
549

550
        assert(rl);
29,565✔
551
        assert(name);
29,565✔
552
        assert(value);
29,565✔
553

554
        if (sscanf(value, USEC_FMT " " USEC_FMT " %u %u", &begin, &interval, &num, &burst) != 4)
29,565✔
555
                return log_notice("Failed to parse %s, ignoring: %s", name, value);
×
556

557
        /* Preserve the counter only if the configuration didn't change. */
558
        rl->num = (interval == rl->interval && burst == rl->burst) ? num : 0;
29,565✔
559
        rl->begin = begin;
29,565✔
560
}
561

562
int open_serialization_fd(const char *ident) {
5,731✔
563
        assert(ident);
5,731✔
564

565
        int fd = memfd_new_full(ident, MFD_ALLOW_SEALING);
5,731✔
566
        if (fd < 0)
5,731✔
567
                return fd;
568

569
        log_debug("Serializing %s to memfd.", ident);
5,731✔
570
        return fd;
571
}
572

573
int open_serialization_file(const char *ident, FILE **ret) {
3,947✔
574
        _cleanup_fclose_ FILE *f = NULL;
3,947✔
575
        _cleanup_close_ int fd;
3,947✔
576

577
        assert(ret);
3,947✔
578

579
        fd = open_serialization_fd(ident);
3,947✔
580
        if (fd < 0)
3,947✔
581
                return fd;
582

583
        f = take_fdopen(&fd, "w+");
3,947✔
584
        if (!f)
3,947✔
585
                return -errno;
×
586

587
        *ret = TAKE_PTR(f);
3,947✔
588
        return 0;
3,947✔
589
}
590

591
int finish_serialization_fd(int fd) {
1,784✔
592
        assert(fd >= 0);
1,784✔
593

594
        if (lseek(fd, 0, SEEK_SET) < 0)
1,784✔
595
                return -errno;
×
596

597
        return memfd_set_sealed(fd);
1,784✔
598
}
599

600
int finish_serialization_file(FILE *f) {
3,947✔
601
        int r;
3,947✔
602

603
        assert(f);
3,947✔
604

605
        r = fflush_and_check(f);
3,947✔
606
        if (r < 0)
3,947✔
607
                return r;
608

609
        if (fseeko(f, 0, SEEK_SET) < 0)
3,947✔
610
                return -errno;
×
611

612
        int fd = fileno(f);
3,947✔
613
        if (fd < 0)
3,947✔
614
                return -EBADF;
615

616
        return memfd_set_sealed(fd);
3,947✔
617
}
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