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