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

systemd / systemd / 14048528658

24 Mar 2025 08:06PM UTC coverage: 71.927% (-0.04%) from 71.966%
14048528658

push

github

web-flow
Ratelimit attempts to open watchdog, increase logging (#35708)

29 of 69 new or added lines in 4 files covered. (42.03%)

1083 existing lines in 38 files now uncovered.

296538 of 412279 relevant lines covered (71.93%)

716588.24 hits per line

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

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

3
#include "bitfield.h"
4
#include "group-record.h"
5
#include "json-util.h"
6
#include "strv.h"
7
#include "uid-classification.h"
8
#include "user-util.h"
9

10
GroupRecord* group_record_new(void) {
4,830✔
11
        GroupRecord *h;
4,830✔
12

13
        h = new(GroupRecord, 1);
4,830✔
14
        if (!h)
4,830✔
15
                return NULL;
16

17
        *h = (GroupRecord) {
4,830✔
18
                .n_ref = 1,
19
                .disposition = _USER_DISPOSITION_INVALID,
20
                .last_change_usec = UINT64_MAX,
21
                .gid = GID_INVALID,
22
        };
23

24
        return h;
4,830✔
25
}
26

27
static GroupRecord *group_record_free(GroupRecord *g) {
4,820✔
28
        if (!g)
4,820✔
29
                return NULL;
30

31
        free(g->group_name);
4,820✔
32
        free(g->realm);
4,820✔
33
        free(g->group_name_and_realm_auto);
4,820✔
34
        free(g->description);
4,820✔
35

36
        strv_free(g->members);
4,820✔
37
        free(g->service);
4,820✔
38
        strv_free(g->administrators);
4,820✔
39
        strv_free_erase(g->hashed_password);
4,820✔
40

41
        sd_json_variant_unref(g->json);
4,820✔
42

43
        return mfree(g);
4,820✔
44
}
45

46
DEFINE_TRIVIAL_REF_UNREF_FUNC(GroupRecord, group_record, group_record_free);
42,987✔
47

48
static int dispatch_privileged(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
49✔
49

50
        static const sd_json_dispatch_field privileged_dispatch_table[] = {
49✔
51
                { "hashedPassword", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv, offsetof(GroupRecord, hashed_password), SD_JSON_STRICT },
52
                {},
53
        };
54

55
        return sd_json_dispatch(variant, privileged_dispatch_table, flags, userdata);
49✔
56
}
57

58
static int dispatch_binding(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,403✔
59

60
        static const sd_json_dispatch_field binding_dispatch_table[] = {
1,403✔
61
                { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
62
                {},
63
        };
64

65
        sd_json_variant *m;
1,403✔
66
        sd_id128_t mid;
1,403✔
67
        int r;
1,403✔
68

69
        if (!variant)
1,403✔
70
                return 0;
1,403✔
71

72
        if (!sd_json_variant_is_object(variant))
239✔
73
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
74

75
        r = sd_id128_get_machine(&mid);
239✔
76
        if (r < 0)
239✔
77
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
78

79
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
239✔
80
        if (!m)
239✔
81
                return 0;
82

83
        return sd_json_dispatch(m, binding_dispatch_table, flags, userdata);
239✔
84
}
85

86
static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,403✔
87

88
        static const sd_json_dispatch_field per_machine_dispatch_table[] = {
1,403✔
89
                { "matchMachineId", _SD_JSON_VARIANT_TYPE_INVALID, NULL,                           0,                                     0             },
90
                { "matchHostname",  _SD_JSON_VARIANT_TYPE_INVALID, NULL,                           0,                                     0             },
91
                { "gid",            SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,       offsetof(GroupRecord, gid),            0             },
92
                { "members",        SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, members),        SD_JSON_RELAX },
93
                { "administrators", SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, administrators), SD_JSON_RELAX },
94
                {},
95
        };
96

97
        sd_json_variant *e;
1,403✔
98
        int r;
1,403✔
99

100
        if (!variant)
1,403✔
101
                return 0;
102

103
        if (!sd_json_variant_is_array(variant))
×
104
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
×
105

106
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
107
                if (!sd_json_variant_is_object(e))
×
108
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
×
109

110
                r = per_machine_match(e, flags);
×
111
                if (r < 0)
×
112
                        return r;
113
                if (r == 0)
×
114
                        continue;
×
115

116
                r = sd_json_dispatch(e, per_machine_dispatch_table, flags, userdata);
×
117
                if (r < 0)
×
118
                        return r;
119
        }
120

121
        return 0;
×
122
}
123

124
static int dispatch_status(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,403✔
125

126
        static const sd_json_dispatch_field status_dispatch_table[] = {
1,403✔
127
                { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(GroupRecord, service), SD_JSON_STRICT },
128
                {},
129
        };
130

131
        sd_json_variant *m;
1,403✔
132
        sd_id128_t mid;
1,403✔
133
        int r;
1,403✔
134

135
        if (!variant)
1,403✔
136
                return 0;
1,403✔
137

138
        if (!sd_json_variant_is_object(variant))
633✔
139
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
140

141
        r = sd_id128_get_machine(&mid);
633✔
142
        if (r < 0)
633✔
143
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
144

145
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
633✔
146
        if (!m)
633✔
147
                return 0;
148

149
        return sd_json_dispatch(m, status_dispatch_table, flags, userdata);
633✔
150
}
151

152
static int group_record_augment(GroupRecord *h, sd_json_dispatch_flags_t json_flags) {
1,403✔
153
        assert(h);
1,403✔
154

155
        if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
1,403✔
156
                return 0;
157

158
        assert(h->group_name);
1,292✔
159

160
        if (!h->group_name_and_realm_auto && h->realm) {
1,292✔
161
                h->group_name_and_realm_auto = strjoin(h->group_name, "@", h->realm);
36✔
162
                if (!h->group_name_and_realm_auto)
36✔
163
                        return json_log_oom(h->json, json_flags);
×
164
        }
165

166
        return 0;
167
}
168

169
int group_record_load(
1,403✔
170
                GroupRecord *h,
171
                sd_json_variant *v,
172
                UserRecordLoadFlags load_flags) {
173

174
        static const sd_json_dispatch_field group_dispatch_table[] = {
1,403✔
175
                { "groupName",      SD_JSON_VARIANT_STRING,        json_dispatch_user_group_name,  offsetof(GroupRecord, group_name),       SD_JSON_RELAX  },
176
                { "realm",          SD_JSON_VARIANT_STRING,        json_dispatch_realm,            offsetof(GroupRecord, realm),            0              },
177
                { "description",    SD_JSON_VARIANT_STRING,        json_dispatch_gecos,            offsetof(GroupRecord, description),      0              },
178
                { "disposition",    SD_JSON_VARIANT_STRING,        json_dispatch_user_disposition, offsetof(GroupRecord, disposition),      0              },
179
                { "service",        SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(GroupRecord, service),          SD_JSON_STRICT },
180
                { "lastChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(GroupRecord, last_change_usec), 0              },
181
                { "gid",            SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,       offsetof(GroupRecord, gid),              0              },
182
                { "members",        SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, members),          SD_JSON_RELAX  },
183
                { "administrators", SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, administrators),   SD_JSON_RELAX  },
184

185
                { "privileged",     SD_JSON_VARIANT_OBJECT,        dispatch_privileged,            0,                                       0              },
186

187
                /* Not defined for now, for groups, but let's at least generate sensible errors about it */
188
                { "secret",         SD_JSON_VARIANT_OBJECT,        sd_json_dispatch_unsupported,   0,                                       0              },
189

190
                /* Ignore the perMachine, binding and status stuff here, and process it later, so that it overrides whatever is set above */
191
                { "perMachine",     SD_JSON_VARIANT_ARRAY,         NULL,                           0,                                       0              },
192
                { "binding",        SD_JSON_VARIANT_OBJECT,        NULL,                           0,                                       0              },
193
                { "status",         SD_JSON_VARIANT_OBJECT,        NULL,                           0,                                       0              },
194

195
                /* Ignore 'signature', we check it with explicit accessors instead */
196
                { "signature",      SD_JSON_VARIANT_ARRAY,         NULL,                           0,                                       0              },
197
                {},
198
        };
199

200
        sd_json_dispatch_flags_t json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
1,403✔
201
        int r;
1,403✔
202

203
        assert(h);
1,403✔
204
        assert(!h->json);
1,403✔
205

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

208
        if ((USER_RECORD_REQUIRE_MASK(load_flags) & (USER_RECORD_SECRET|USER_RECORD_PRIVILEGED)))
1,403✔
209
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Secret and privileged section currently not available for groups, refusing.");
×
210

211
        r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
1,403✔
212
        if (r < 0)
1,403✔
213
                return r;
214

215
        r = sd_json_dispatch(h->json, group_dispatch_table, json_flags | SD_JSON_ALLOW_EXTENSIONS, h);
1,403✔
216
        if (r < 0)
1,403✔
217
                return r;
218

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

222
        r = dispatch_per_machine("perMachine", sd_json_variant_by_key(h->json, "perMachine"), json_flags, h);
1,403✔
223
        if (r < 0)
1,403✔
224
                return r;
225

226
        r = dispatch_binding("binding", sd_json_variant_by_key(h->json, "binding"), json_flags, h);
1,403✔
227
        if (r < 0)
1,403✔
228
                return r;
229

230
        r = dispatch_status("status", sd_json_variant_by_key(h->json, "status"), json_flags, h);
1,403✔
231
        if (r < 0)
1,403✔
232
                return r;
233

234
        if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->group_name)
1,403✔
235
                return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "Group name field missing, refusing.");
×
236

237
        r = group_record_augment(h, json_flags);
1,403✔
238
        if (r < 0)
1,403✔
239
                return r;
×
240

241
        return 0;
242
}
243

244
int group_record_build(GroupRecord **ret, ...) {
33✔
245
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
33✔
246
        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
33✔
247
        va_list ap;
33✔
248
        int r;
33✔
249

250
        assert(ret);
33✔
251

252
        va_start(ap, ret);
33✔
253
        r = sd_json_buildv(&v, ap);
33✔
254
        va_end(ap);
33✔
255

256
        if (r < 0)
33✔
257
                return r;
258

259
        g = group_record_new();
33✔
260
        if (!g)
33✔
261
                return -ENOMEM;
262

263
        r = group_record_load(g, v, USER_RECORD_LOAD_FULL);
33✔
264
        if (r < 0)
33✔
265
                return r;
266

267
        *ret = TAKE_PTR(g);
33✔
268
        return 0;
33✔
269
}
270

271
const char* group_record_group_name_and_realm(GroupRecord *h) {
79✔
272
        assert(h);
79✔
273

274
        /* Return the pre-initialized joined string if it is defined */
275
        if (h->group_name_and_realm_auto)
79✔
276
                return h->group_name_and_realm_auto;
277

278
        /* If it's not defined then we cannot have a realm */
279
        assert(!h->realm);
79✔
280
        return h->group_name;
79✔
281
}
282

283
UserDisposition group_record_disposition(GroupRecord *h) {
1,087✔
284
        assert(h);
1,087✔
285

286
        if (h->disposition >= 0)
1,087✔
287
                return h->disposition;
288

289
        /* If not declared, derive from GID */
290

291
        if (!gid_is_valid(h->gid))
926✔
292
                return _USER_DISPOSITION_INVALID;
293

294
        if (h->gid == 0 || h->gid == GID_NOBODY)
926✔
295
                return USER_INTRINSIC;
296

297
        if (gid_is_system(h->gid))
862✔
298
                return USER_SYSTEM;
299

300
        if (gid_is_dynamic(h->gid))
25✔
301
                return USER_DYNAMIC;
302

303
        if (gid_is_container(h->gid))
25✔
304
                return USER_CONTAINER;
305

UNCOV
306
        if (gid_is_foreign(h->gid))
×
307
                return USER_FOREIGN;
308

UNCOV
309
        if (h->gid > INT32_MAX)
×
310
                return USER_RESERVED;
×
311

312
        return USER_REGULAR;
313
}
314

315
int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **ret) {
619✔
316
        _cleanup_(group_record_unrefp) GroupRecord *c = NULL;
619✔
317
        int r;
619✔
318

319
        assert(h);
619✔
320
        assert(ret);
619✔
321

322
        c = group_record_new();
619✔
323
        if (!c)
619✔
324
                return -ENOMEM;
325

326
        r = group_record_load(c, h->json, flags);
619✔
327
        if (r < 0)
619✔
328
                return r;
329

330
        *ret = TAKE_PTR(c);
619✔
331
        return 0;
619✔
332
}
333

334
bool group_record_matches_group_name(const GroupRecord *g, const char *group_name) {
54✔
335
        assert(g);
54✔
336
        assert(group_name);
54✔
337

338
        if (streq_ptr(g->group_name, group_name))
54✔
339
                return true;
340

341
        if (streq_ptr(g->group_name_and_realm_auto, group_name))
×
342
                return true;
×
343

344
        return false;
345
}
346

347
bool group_record_match(GroupRecord *h, const UserDBMatch *match) {
1,144✔
348
        assert(h);
1,144✔
349

350
        if (!match)
1,144✔
351
                return true;
352

353
        if (!gid_is_valid(h->gid))
894✔
354
                return false;
355

356
        if (h->gid < match->gid_min || h->gid > match->gid_max)
894✔
357
                return false;
358

359
        if (!BIT_SET(match->disposition_mask, group_record_disposition(h)))
834✔
360
                return false;
361

362
        if (!strv_isempty(match->fuzzy_names)) {
712✔
363
                const char* names[] = {
120✔
364
                        h->group_name,
60✔
365
                        group_record_group_name_and_realm(h),
60✔
366
                        h->description,
60✔
367
                };
368

369
                if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names))
60✔
370
                        return false;
60✔
371
        }
372

373
        return true;
374
}
375

376
bool group_record_is_root(const GroupRecord *g) {
115✔
377
        assert(g);
115✔
378

379
        return g->gid == 0 || streq_ptr(g->group_name, "root");
115✔
380
}
381

382
bool group_record_is_nobody(const GroupRecord *g) {
115✔
383
        assert(g);
115✔
384

385
        return g->gid == GID_NOBODY || STRPTR_IN_SET(g->group_name, NOBODY_GROUP_NAME, "nobody");
115✔
386
}
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