]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/group-record.c
Merge pull request #14592 from keszybz/simplifications
[thirdparty/systemd.git] / src / shared / group-record.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "group-record.h"
4 #include "strv.h"
5 #include "user-util.h"
6
7 GroupRecord* group_record_new(void) {
8 GroupRecord *h;
9
10 h = new(GroupRecord, 1);
11 if (!h)
12 return NULL;
13
14 *h = (GroupRecord) {
15 .n_ref = 1,
16 .disposition = _USER_DISPOSITION_INVALID,
17 .last_change_usec = UINT64_MAX,
18 .gid = GID_INVALID,
19 };
20
21 return h;
22 }
23
24 static GroupRecord *group_record_free(GroupRecord *g) {
25 if (!g)
26 return NULL;
27
28 free(g->group_name);
29 free(g->realm);
30 free(g->group_name_and_realm_auto);
31
32 strv_free(g->members);
33 free(g->service);
34 strv_free(g->administrators);
35 strv_free_erase(g->hashed_password);
36
37 json_variant_unref(g->json);
38
39 return mfree(g);
40 }
41
42 DEFINE_TRIVIAL_REF_UNREF_FUNC(GroupRecord, group_record, group_record_free);
43
44 static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
45
46 static const JsonDispatch privileged_dispatch_table[] = {
47 { "hashedPassword", _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv, offsetof(GroupRecord, hashed_password), JSON_SAFE },
48 {},
49 };
50
51 return json_dispatch(variant, privileged_dispatch_table, NULL, flags, userdata);
52 }
53
54 static int dispatch_binding(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
55
56 static const JsonDispatch binding_dispatch_table[] = {
57 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
58 {},
59 };
60
61 char smid[SD_ID128_STRING_MAX];
62 JsonVariant *m;
63 sd_id128_t mid;
64 int r;
65
66 if (!variant)
67 return 0;
68
69 if (!json_variant_is_object(variant))
70 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
71
72 r = sd_id128_get_machine(&mid);
73 if (r < 0)
74 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
75
76 m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
77 if (!m)
78 return 0;
79
80 return json_dispatch(m, binding_dispatch_table, NULL, flags, userdata);
81 }
82
83 static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
84
85 static const JsonDispatch per_machine_dispatch_table[] = {
86 { "matchMachineId", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
87 { "matchHostname", _JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
88 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
89 { "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
90 { "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
91 {},
92 };
93
94 JsonVariant *e;
95 int r;
96
97 if (!variant)
98 return 0;
99
100 if (!json_variant_is_array(variant))
101 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
102
103 JSON_VARIANT_ARRAY_FOREACH(e, variant) {
104 bool matching = false;
105 JsonVariant *m;
106
107 if (!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 m = json_variant_by_key(e, "matchMachineId");
111 if (m) {
112 r = per_machine_id_match(m, flags);
113 if (r < 0)
114 return r;
115
116 matching = r > 0;
117 }
118
119 if (!matching) {
120 m = json_variant_by_key(e, "matchHostname");
121 if (m) {
122 r = per_machine_hostname_match(m, flags);
123 if (r < 0)
124 return r;
125
126 matching = r > 0;
127 }
128 }
129
130 if (!matching)
131 continue;
132
133 r = json_dispatch(e, per_machine_dispatch_table, NULL, flags, userdata);
134 if (r < 0)
135 return r;
136 }
137
138 return 0;
139 }
140
141 static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
142
143 static const JsonDispatch status_dispatch_table[] = {
144 { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
145 {},
146 };
147
148 char smid[SD_ID128_STRING_MAX];
149 JsonVariant *m;
150 sd_id128_t mid;
151 int r;
152
153 if (!variant)
154 return 0;
155
156 if (!json_variant_is_object(variant))
157 return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
158
159 r = sd_id128_get_machine(&mid);
160 if (r < 0)
161 return json_log(variant, flags, r, "Failed to determine machine ID: %m");
162
163 m = json_variant_by_key(variant, sd_id128_to_string(mid, smid));
164 if (!m)
165 return 0;
166
167 return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
168 }
169
170 static int group_record_augment(GroupRecord *h, JsonDispatchFlags json_flags) {
171 assert(h);
172
173 if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
174 return 0;
175
176 assert(h->group_name);
177
178 if (!h->group_name_and_realm_auto && h->realm) {
179 h->group_name_and_realm_auto = strjoin(h->group_name, "@", h->realm);
180 if (!h->group_name_and_realm_auto)
181 return json_log_oom(h->json, json_flags);
182 }
183
184 return 0;
185 }
186
187 int group_record_load(
188 GroupRecord *h,
189 JsonVariant *v,
190 UserRecordLoadFlags load_flags) {
191
192 static const JsonDispatch group_dispatch_table[] = {
193 { "groupName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), 0 },
194 { "realm", JSON_VARIANT_STRING, json_dispatch_realm, offsetof(GroupRecord, realm), 0 },
195 { "disposition", JSON_VARIANT_STRING, json_dispatch_user_disposition, offsetof(GroupRecord, disposition), 0 },
196 { "service", JSON_VARIANT_STRING, json_dispatch_string, offsetof(GroupRecord, service), JSON_SAFE },
197 { "lastChangeUSec", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(GroupRecord, last_change_usec), 0 },
198 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
199 { "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), 0 },
200 { "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), 0 },
201
202 { "privileged", JSON_VARIANT_OBJECT, dispatch_privileged, 0, 0 },
203
204 /* Not defined for now, for groups, but let's at least generate sensible errors about it */
205 { "secret", JSON_VARIANT_OBJECT, json_dispatch_unsupported, 0, 0 },
206
207 /* Ignore the perMachine, binding and status stuff here, and process it later, so that it overrides whatever is set above */
208 { "perMachine", JSON_VARIANT_ARRAY, NULL, 0, 0 },
209 { "binding", JSON_VARIANT_OBJECT, NULL, 0, 0 },
210 { "status", JSON_VARIANT_OBJECT, NULL, 0, 0 },
211
212 /* Ignore 'signature', we check it with explicit accessors instead */
213 { "signature", JSON_VARIANT_ARRAY, NULL, 0, 0 },
214 {},
215 };
216
217 JsonDispatchFlags json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
218 int r;
219
220 assert(h);
221 assert(!h->json);
222
223 /* Note that this call will leave a half-initialized record around on failure! */
224
225 if ((USER_RECORD_REQUIRE_MASK(load_flags) & (USER_RECORD_SECRET|USER_RECORD_PRIVILEGED)))
226 return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Secret and privileged section currently not available for groups, refusing.");
227
228 r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
229 if (r < 0)
230 return r;
231
232 r = json_dispatch(h->json, group_dispatch_table, NULL, json_flags, h);
233 if (r < 0)
234 return r;
235
236 /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields, since we want
237 * them to override the global options. Let's process them now. */
238
239 r = dispatch_per_machine("perMachine", json_variant_by_key(h->json, "perMachine"), json_flags, h);
240 if (r < 0)
241 return r;
242
243 r = dispatch_binding("binding", json_variant_by_key(h->json, "binding"), json_flags, h);
244 if (r < 0)
245 return r;
246
247 r = dispatch_status("status", json_variant_by_key(h->json, "status"), json_flags, h);
248 if (r < 0)
249 return r;
250
251 if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->group_name)
252 return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "Group name field missing, refusing.");
253
254 r = group_record_augment(h, json_flags);
255 if (r < 0)
256 return r;
257
258 return 0;
259 }
260
261 int group_record_build(GroupRecord **ret, ...) {
262 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
263 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
264 va_list ap;
265 int r;
266
267 assert(ret);
268
269 va_start(ap, ret);
270 r = json_buildv(&v, ap);
271 va_end(ap);
272
273 if (r < 0)
274 return r;
275
276 g = group_record_new();
277 if (!g)
278 return -ENOMEM;
279
280 r = group_record_load(g, v, USER_RECORD_LOAD_FULL);
281 if (r < 0)
282 return r;
283
284 *ret = TAKE_PTR(g);
285 return 0;
286 }
287
288 const char *group_record_group_name_and_realm(GroupRecord *h) {
289 assert(h);
290
291 /* Return the pre-initialized joined string if it is defined */
292 if (h->group_name_and_realm_auto)
293 return h->group_name_and_realm_auto;
294
295 /* If it's not defined then we cannot have a realm */
296 assert(!h->realm);
297 return h->group_name;
298 }
299
300 UserDisposition group_record_disposition(GroupRecord *h) {
301 assert(h);
302
303 if (h->disposition >= 0)
304 return h->disposition;
305
306 /* If not declared, derive from GID */
307
308 if (!gid_is_valid(h->gid))
309 return _USER_DISPOSITION_INVALID;
310
311 if (h->gid == 0 || h->gid == GID_NOBODY)
312 return USER_INTRINSIC;
313
314 if (gid_is_system(h->gid))
315 return USER_SYSTEM;
316
317 if (gid_is_dynamic(h->gid))
318 return USER_DYNAMIC;
319
320 if (gid_is_container(h->gid))
321 return USER_CONTAINER;
322
323 if (h->gid > INT32_MAX)
324 return USER_RESERVED;
325
326 return USER_REGULAR;
327 }
328
329 int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **ret) {
330 _cleanup_(group_record_unrefp) GroupRecord *c = NULL;
331 int r;
332
333 assert(h);
334 assert(ret);
335
336 c = group_record_new();
337 if (!c)
338 return -ENOMEM;
339
340 r = group_record_load(c, h->json, flags);
341 if (r < 0)
342 return r;
343
344 *ret = TAKE_PTR(c);
345 return 0;
346 }