]>
Commit | Line | Data |
---|---|---|
71d0b9d4 LP |
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 }, | |
7a8867ab LP |
89 | { "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), JSON_RELAX}, |
90 | { "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), JSON_RELAX}, | |
71d0b9d4 LP |
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[] = { | |
7a8867ab | 193 | { "groupName", JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), JSON_RELAX}, |
71d0b9d4 LP |
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 }, | |
7a8867ab LP |
199 | { "members", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), JSON_RELAX}, |
200 | { "administrators", JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), JSON_RELAX}, | |
71d0b9d4 LP |
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 | } |