1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "group-record.h"
7 GroupRecord
* group_record_new(void) {
10 h
= new(GroupRecord
, 1);
16 .disposition
= _USER_DISPOSITION_INVALID
,
17 .last_change_usec
= UINT64_MAX
,
24 static GroupRecord
*group_record_free(GroupRecord
*g
) {
30 free(g
->group_name_and_realm_auto
);
32 strv_free(g
->members
);
34 strv_free(g
->administrators
);
35 strv_free_erase(g
->hashed_password
);
37 json_variant_unref(g
->json
);
42 DEFINE_TRIVIAL_REF_UNREF_FUNC(GroupRecord
, group_record
, group_record_free
);
44 static int dispatch_privileged(const char *name
, JsonVariant
*variant
, JsonDispatchFlags flags
, void *userdata
) {
46 static const JsonDispatch privileged_dispatch_table
[] = {
47 { "hashedPassword", _JSON_VARIANT_TYPE_INVALID
, json_dispatch_strv
, offsetof(GroupRecord
, hashed_password
), JSON_SAFE
},
51 return json_dispatch(variant
, privileged_dispatch_table
, NULL
, flags
, userdata
);
54 static int dispatch_binding(const char *name
, JsonVariant
*variant
, JsonDispatchFlags flags
, void *userdata
) {
56 static const JsonDispatch binding_dispatch_table
[] = {
57 { "gid", JSON_VARIANT_UNSIGNED
, json_dispatch_uid_gid
, offsetof(GroupRecord
, gid
), 0 },
61 char smid
[SD_ID128_STRING_MAX
];
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
));
72 r
= sd_id128_get_machine(&mid
);
74 return json_log(variant
, flags
, r
, "Failed to determine machine ID: %m");
76 m
= json_variant_by_key(variant
, sd_id128_to_string(mid
, smid
));
80 return json_dispatch(m
, binding_dispatch_table
, NULL
, flags
, userdata
);
83 static int dispatch_per_machine(const char *name
, JsonVariant
*variant
, JsonDispatchFlags flags
, void *userdata
) {
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 },
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
));
103 JSON_VARIANT_ARRAY_FOREACH(e
, variant
) {
104 bool matching
= false;
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
));
110 m
= json_variant_by_key(e
, "matchMachineId");
112 r
= per_machine_id_match(m
, flags
);
120 m
= json_variant_by_key(e
, "matchHostname");
122 r
= per_machine_hostname_match(m
, flags
);
133 r
= json_dispatch(e
, per_machine_dispatch_table
, NULL
, flags
, userdata
);
141 static int dispatch_status(const char *name
, JsonVariant
*variant
, JsonDispatchFlags flags
, void *userdata
) {
143 static const JsonDispatch status_dispatch_table
[] = {
144 { "service", JSON_VARIANT_STRING
, json_dispatch_string
, offsetof(GroupRecord
, service
), JSON_SAFE
},
148 char smid
[SD_ID128_STRING_MAX
];
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
));
159 r
= sd_id128_get_machine(&mid
);
161 return json_log(variant
, flags
, r
, "Failed to determine machine ID: %m");
163 m
= json_variant_by_key(variant
, sd_id128_to_string(mid
, smid
));
167 return json_dispatch(m
, status_dispatch_table
, NULL
, flags
, userdata
);
170 static int group_record_augment(GroupRecord
*h
, JsonDispatchFlags json_flags
) {
173 if (!FLAGS_SET(h
->mask
, USER_RECORD_REGULAR
))
176 assert(h
->group_name
);
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
);
187 int group_record_load(
190 UserRecordLoadFlags load_flags
) {
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 },
202 { "privileged", JSON_VARIANT_OBJECT
, dispatch_privileged
, 0, 0 },
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 },
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 },
212 /* Ignore 'signature', we check it with explicit accessors instead */
213 { "signature", JSON_VARIANT_ARRAY
, NULL
, 0, 0 },
217 JsonDispatchFlags json_flags
= USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags
);
223 /* Note that this call will leave a half-initialized record around on failure! */
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.");
228 r
= user_group_record_mangle(v
, load_flags
, &h
->json
, &h
->mask
);
232 r
= json_dispatch(h
->json
, group_dispatch_table
, NULL
, json_flags
, h
);
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. */
239 r
= dispatch_per_machine("perMachine", json_variant_by_key(h
->json
, "perMachine"), json_flags
, h
);
243 r
= dispatch_binding("binding", json_variant_by_key(h
->json
, "binding"), json_flags
, h
);
247 r
= dispatch_status("status", json_variant_by_key(h
->json
, "status"), json_flags
, h
);
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.");
254 r
= group_record_augment(h
, json_flags
);
261 int group_record_build(GroupRecord
**ret
, ...) {
262 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
263 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
270 r
= json_buildv(&v
, ap
);
276 g
= group_record_new();
280 r
= group_record_load(g
, v
, USER_RECORD_LOAD_FULL
);
288 const char *group_record_group_name_and_realm(GroupRecord
*h
) {
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
;
295 /* If it's not defined then we cannot have a realm */
297 return h
->group_name
;
300 UserDisposition
group_record_disposition(GroupRecord
*h
) {
303 if (h
->disposition
>= 0)
304 return h
->disposition
;
306 /* If not declared, derive from GID */
308 if (!gid_is_valid(h
->gid
))
309 return _USER_DISPOSITION_INVALID
;
311 if (h
->gid
== 0 || h
->gid
== GID_NOBODY
)
312 return USER_INTRINSIC
;
314 if (gid_is_system(h
->gid
))
317 if (gid_is_dynamic(h
->gid
))
320 if (gid_is_container(h
->gid
))
321 return USER_CONTAINER
;
323 if (h
->gid
> INT32_MAX
)
324 return USER_RESERVED
;
329 int group_record_clone(GroupRecord
*h
, UserRecordLoadFlags flags
, GroupRecord
**ret
) {
330 _cleanup_(group_record_unrefp
) GroupRecord
*c
= NULL
;
336 c
= group_record_new();
340 r
= group_record_load(c
, h
->json
, flags
);