/src/systemd/src/shared/user-record-nss.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | | |
3 | | #include <gshadow.h> |
4 | | |
5 | | #include "sd-json.h" |
6 | | |
7 | | #include "alloc-util.h" |
8 | | #include "errno-util.h" |
9 | | #include "format-util.h" |
10 | | #include "group-record.h" |
11 | | #include "json-util.h" |
12 | | #include "libcrypt-util.h" |
13 | | #include "log.h" |
14 | | #include "string-util.h" |
15 | | #include "strv.h" |
16 | | #include "time-util.h" |
17 | | #include "user-record.h" |
18 | | #include "user-record-nss.h" |
19 | | #include "user-util.h" |
20 | | #include "utf8.h" |
21 | | |
22 | | #define SET_IF(field, condition, value, fallback) \ |
23 | 86.9k | field = (condition) ? (value) : (fallback) |
24 | | |
25 | 12.4k | static const char* utf8_only(const char *s) { |
26 | 12.4k | return s && utf8_is_valid(s) ? s : NULL; |
27 | 12.4k | } |
28 | | |
29 | 2.44k | static int strv_extend_strv_utf8_only(char ***dst, char **src, bool filter_duplicates) { |
30 | 2.44k | _cleanup_free_ char **t = NULL; |
31 | 2.44k | size_t l, j = 0; |
32 | | |
33 | | /* First, do a shallow copy of s, filtering for only valid utf-8 strings */ |
34 | 2.44k | l = strv_length(src); |
35 | 2.44k | t = new(char*, l + 1); |
36 | 2.44k | if (!t) |
37 | 0 | return -ENOMEM; |
38 | | |
39 | 3.09k | for (size_t i = 0; i < l; i++) |
40 | 647 | if (utf8_is_valid(src[i])) |
41 | 647 | t[j++] = src[i]; |
42 | 2.44k | if (j == 0) |
43 | 1.80k | return 0; |
44 | | |
45 | 647 | t[j] = NULL; |
46 | 647 | return strv_extend_strv(dst, t, filter_duplicates); |
47 | 2.44k | } |
48 | | |
49 | 8.66k | static int nss_add_valid_alias(char ***aliases, const char *name, const char *alias_name) { |
50 | 8.66k | assert(aliases); |
51 | 8.66k | assert(name); |
52 | | |
53 | 8.66k | if (isempty(alias_name) || streq_ptr(alias_name, name)) |
54 | 8.66k | return 0; |
55 | | |
56 | 0 | if (!valid_user_group_name(alias_name, VALID_USER_RELAX)) |
57 | 0 | return 0; |
58 | | |
59 | 0 | return strv_extend(aliases, alias_name); |
60 | 0 | } |
61 | | |
62 | | int nss_passwd_to_user_record( |
63 | | const struct passwd *pwd, |
64 | | const struct spwd *spwd, |
65 | | const char *alias_name, |
66 | 6.21k | UserRecord **ret) { |
67 | | |
68 | 6.21k | _cleanup_(user_record_unrefp) UserRecord *hr = NULL; |
69 | 6.21k | int r; |
70 | | |
71 | 6.21k | assert(pwd); |
72 | | |
73 | 6.21k | if (isempty(pwd->pw_name)) |
74 | 0 | return -EINVAL; |
75 | | |
76 | 6.21k | if (spwd && !streq_ptr(spwd->sp_namp, pwd->pw_name)) |
77 | 0 | return -EINVAL; |
78 | | |
79 | 6.21k | hr = user_record_new(); |
80 | 6.21k | if (!hr) |
81 | 0 | return -ENOMEM; |
82 | | |
83 | 6.21k | r = free_and_strdup(&hr->user_name, pwd->pw_name); |
84 | 6.21k | if (r < 0) |
85 | 0 | return r; |
86 | | |
87 | 6.21k | r = nss_add_valid_alias(&hr->aliases, hr->user_name, alias_name); |
88 | 6.21k | if (r < 0) |
89 | 0 | return r; |
90 | | |
91 | | /* Some bad NSS modules synthesize GECOS fields with embedded ":" or "\n" characters, which are not |
92 | | * something we can output in /etc/passwd compatible format, since these are record separators |
93 | | * there. We normally refuse that, but we need to maintain compatibility with arbitrary NSS modules, |
94 | | * hence let's do what glibc does: mangle the data to fit the format. */ |
95 | 6.21k | if (isempty(pwd->pw_gecos) || streq_ptr(pwd->pw_gecos, hr->user_name)) |
96 | 5.92k | hr->real_name = mfree(hr->real_name); |
97 | 286 | else if (valid_gecos(pwd->pw_gecos)) { |
98 | 286 | r = free_and_strdup(&hr->real_name, pwd->pw_gecos); |
99 | 286 | if (r < 0) |
100 | 0 | return r; |
101 | 286 | } else { |
102 | 0 | _cleanup_free_ char *mangled = NULL; |
103 | |
|
104 | 0 | mangled = mangle_gecos(pwd->pw_gecos); |
105 | 0 | if (!mangled) |
106 | 0 | return -ENOMEM; |
107 | | |
108 | 0 | free_and_replace(hr->real_name, mangled); |
109 | 0 | } |
110 | | |
111 | 6.21k | r = free_and_strdup(&hr->home_directory, utf8_only(empty_to_null(pwd->pw_dir))); |
112 | 6.21k | if (r < 0) |
113 | 0 | return r; |
114 | | |
115 | 6.21k | r = free_and_strdup(&hr->shell, utf8_only(empty_to_null(pwd->pw_shell))); |
116 | 6.21k | if (r < 0) |
117 | 0 | return r; |
118 | | |
119 | 6.21k | hr->uid = pwd->pw_uid; |
120 | 6.21k | hr->gid = pwd->pw_gid; |
121 | | |
122 | 6.21k | if (spwd && |
123 | 0 | looks_like_hashed_password(utf8_only(spwd->sp_pwdp))) { /* Ignore locked, disabled, and mojibake passwords */ |
124 | 0 | strv_free_erase(hr->hashed_password); |
125 | 0 | hr->hashed_password = strv_new(spwd->sp_pwdp); |
126 | 0 | if (!hr->hashed_password) |
127 | 0 | return -ENOMEM; |
128 | 0 | } else |
129 | 6.21k | hr->hashed_password = strv_free_erase(hr->hashed_password); |
130 | | |
131 | | /* shadow-utils suggests using "chage -E 0" (or -E 1, depending on which man page you check) |
132 | | * for locking a whole account, hence check for that. Note that it also defines a way to lock |
133 | | * just a password instead of the whole account, but that's mostly pointless in times of |
134 | | * password-less authorization, hence let's not bother. */ |
135 | | |
136 | 6.21k | SET_IF(hr->locked, |
137 | 6.21k | spwd && spwd->sp_expire >= 0, |
138 | 6.21k | spwd->sp_expire <= 1, -1); |
139 | | |
140 | 6.21k | SET_IF(hr->not_after_usec, |
141 | 6.21k | spwd && spwd->sp_expire > 1 && (uint64_t) spwd->sp_expire < (UINT64_MAX-1)/USEC_PER_DAY, |
142 | 6.21k | spwd->sp_expire * USEC_PER_DAY, UINT64_MAX); |
143 | | |
144 | 6.21k | SET_IF(hr->password_change_now, |
145 | 6.21k | spwd && spwd->sp_lstchg >= 0, |
146 | 6.21k | spwd->sp_lstchg == 0, -1); |
147 | | |
148 | 6.21k | SET_IF(hr->last_password_change_usec, |
149 | 6.21k | spwd && spwd->sp_lstchg > 0 && (uint64_t) spwd->sp_lstchg <= (UINT64_MAX-1)/USEC_PER_DAY, |
150 | 6.21k | spwd->sp_lstchg * USEC_PER_DAY, UINT64_MAX); |
151 | | |
152 | 6.21k | SET_IF(hr->password_change_min_usec, |
153 | 6.21k | spwd && spwd->sp_min > 0 && (uint64_t) spwd->sp_min <= (UINT64_MAX-1)/USEC_PER_DAY, |
154 | 6.21k | spwd->sp_min * USEC_PER_DAY, UINT64_MAX); |
155 | | |
156 | 6.21k | SET_IF(hr->password_change_max_usec, |
157 | 6.21k | spwd && spwd->sp_max > 0 && (uint64_t) spwd->sp_max <= (UINT64_MAX-1)/USEC_PER_DAY, |
158 | 6.21k | spwd->sp_max * USEC_PER_DAY, UINT64_MAX); |
159 | | |
160 | 6.21k | SET_IF(hr->password_change_warn_usec, |
161 | 6.21k | spwd && spwd->sp_warn > 0 && (uint64_t) spwd->sp_warn <= (UINT64_MAX-1)/USEC_PER_DAY, |
162 | 6.21k | spwd->sp_warn * USEC_PER_DAY, UINT64_MAX); |
163 | | |
164 | 6.21k | SET_IF(hr->password_change_inactive_usec, |
165 | 6.21k | spwd && spwd->sp_inact > 0 && (uint64_t) spwd->sp_inact <= (UINT64_MAX-1)/USEC_PER_DAY, |
166 | 6.21k | spwd->sp_inact * USEC_PER_DAY, UINT64_MAX); |
167 | | |
168 | 6.21k | hr->json = sd_json_variant_unref(hr->json); |
169 | 6.21k | r = sd_json_buildo( |
170 | 6.21k | &hr->json, |
171 | 6.21k | SD_JSON_BUILD_PAIR_STRING("userName", hr->user_name), |
172 | 6.21k | JSON_BUILD_PAIR_STRV_NON_EMPTY("aliases", hr->aliases), |
173 | 6.21k | SD_JSON_BUILD_PAIR_UNSIGNED("uid", hr->uid), |
174 | 6.21k | SD_JSON_BUILD_PAIR_UNSIGNED("gid", user_record_gid(hr)), |
175 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(!!hr->real_name, "realName", SD_JSON_BUILD_STRING(hr->real_name)), |
176 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(!!hr->home_directory, "homeDirectory", SD_JSON_BUILD_STRING(hr->home_directory)), |
177 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(!!hr->shell, "shell", SD_JSON_BUILD_STRING(hr->shell)), |
178 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(hr->hashed_password), "privileged", SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_STRV("hashedPassword", hr->hashed_password))), |
179 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->locked >= 0, "locked", SD_JSON_BUILD_BOOLEAN(hr->locked)), |
180 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->not_after_usec != UINT64_MAX, "notAfterUSec", SD_JSON_BUILD_UNSIGNED(hr->not_after_usec)), |
181 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->password_change_now >= 0, "passwordChangeNow", SD_JSON_BUILD_BOOLEAN(hr->password_change_now)), |
182 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->last_password_change_usec != UINT64_MAX, "lastPasswordChangeUSec", SD_JSON_BUILD_UNSIGNED(hr->last_password_change_usec)), |
183 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->password_change_min_usec != UINT64_MAX, "passwordChangeMinUSec", SD_JSON_BUILD_UNSIGNED(hr->password_change_min_usec)), |
184 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->password_change_max_usec != UINT64_MAX, "passwordChangeMaxUSec", SD_JSON_BUILD_UNSIGNED(hr->password_change_max_usec)), |
185 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->password_change_warn_usec != UINT64_MAX, "passwordChangeWarnUSec", SD_JSON_BUILD_UNSIGNED(hr->password_change_warn_usec)), |
186 | 6.21k | SD_JSON_BUILD_PAIR_CONDITION(hr->password_change_inactive_usec != UINT64_MAX, "passwordChangeInactiveUSec", SD_JSON_BUILD_UNSIGNED(hr->password_change_inactive_usec))); |
187 | 6.21k | if (r < 0) |
188 | 0 | return r; |
189 | | |
190 | 6.21k | hr->mask = USER_RECORD_REGULAR | |
191 | 6.21k | (!strv_isempty(hr->hashed_password) ? USER_RECORD_PRIVILEGED : 0); |
192 | | |
193 | 6.21k | if (ret) |
194 | 6.21k | *ret = TAKE_PTR(hr); |
195 | 6.21k | return 0; |
196 | 6.21k | } |
197 | | |
198 | 0 | int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer) { |
199 | 0 | size_t buflen = 4096; |
200 | 0 | int r; |
201 | |
|
202 | 0 | assert(pwd); |
203 | 0 | assert(ret_spwd); |
204 | 0 | assert(ret_buffer); |
205 | |
|
206 | 0 | for (;;) { |
207 | 0 | _cleanup_free_ char *buf = NULL; |
208 | 0 | struct spwd spwd = {}, *result = NULL; |
209 | |
|
210 | 0 | buf = malloc0(buflen); |
211 | 0 | if (!buf) |
212 | 0 | return -ENOMEM; |
213 | | |
214 | 0 | r = getspnam_r(pwd->pw_name, &spwd, buf, buflen, &result); |
215 | 0 | if (r == 0) { |
216 | 0 | if (!result) |
217 | 0 | return -ESRCH; |
218 | | |
219 | 0 | *ret_spwd = *result; |
220 | 0 | *ret_buffer = TAKE_PTR(buf); |
221 | 0 | return 0; |
222 | 0 | } |
223 | 0 | if (r < 0) |
224 | 0 | return -EIO; /* Weird, this should not return negative! */ |
225 | 0 | if (r != ERANGE) |
226 | 0 | return -r; |
227 | | |
228 | 0 | if (buflen > SIZE_MAX / 2) |
229 | 0 | return -ERANGE; |
230 | | |
231 | 0 | buflen *= 2; |
232 | 0 | buf = mfree(buf); |
233 | 0 | } |
234 | 0 | } |
235 | | |
236 | | int nss_user_record_by_name( |
237 | | const char *name, |
238 | | bool with_shadow, |
239 | 6.36k | UserRecord **ret) { |
240 | | |
241 | 6.36k | _cleanup_free_ char *sbuf = NULL; |
242 | 6.36k | _cleanup_free_ struct passwd *result = NULL; |
243 | 6.36k | bool incomplete = false; |
244 | 6.36k | struct spwd spwd, *sresult = NULL; |
245 | 6.36k | int r; |
246 | | |
247 | 6.36k | assert(name); |
248 | | |
249 | 6.36k | r = getpwnam_malloc(name, &result); |
250 | 6.36k | if (r < 0) |
251 | 5.67k | return r; |
252 | | |
253 | 691 | if (with_shadow) { |
254 | 0 | r = nss_spwd_for_passwd(result, &spwd, &sbuf); |
255 | 0 | if (r < 0) { |
256 | 0 | log_debug_errno(r, "Failed to do shadow lookup for user %s, ignoring: %m", name); |
257 | 0 | incomplete = ERRNO_IS_PRIVILEGE(r); |
258 | 0 | } else |
259 | 0 | sresult = &spwd; |
260 | 0 | } else |
261 | 691 | incomplete = true; |
262 | | |
263 | 691 | r = nss_passwd_to_user_record(result, sresult, name, ret); |
264 | 691 | if (r < 0) |
265 | 0 | return r; |
266 | | |
267 | 691 | if (ret) |
268 | 691 | (*ret)->incomplete = incomplete; |
269 | 691 | return 0; |
270 | 691 | } |
271 | | |
272 | | int nss_user_record_by_uid( |
273 | | uid_t uid, |
274 | | bool with_shadow, |
275 | 11.6k | UserRecord **ret) { |
276 | | |
277 | 11.6k | _cleanup_free_ char *sbuf = NULL; |
278 | 11.6k | _cleanup_free_ struct passwd *result = NULL; |
279 | 11.6k | bool incomplete = false; |
280 | 11.6k | struct spwd spwd, *sresult = NULL; |
281 | 11.6k | int r; |
282 | | |
283 | 11.6k | r = getpwuid_malloc(uid, &result); |
284 | 11.6k | if (r < 0) |
285 | 6.16k | return r; |
286 | | |
287 | 5.52k | if (with_shadow) { |
288 | 0 | r = nss_spwd_for_passwd(result, &spwd, &sbuf); |
289 | 0 | if (r < 0) { |
290 | 0 | log_debug_errno(r, "Failed to do shadow lookup for UID " UID_FMT ", ignoring: %m", uid); |
291 | 0 | incomplete = ERRNO_IS_PRIVILEGE(r); |
292 | 0 | } else |
293 | 0 | sresult = &spwd; |
294 | 0 | } else |
295 | 5.52k | incomplete = true; |
296 | | |
297 | 5.52k | r = nss_passwd_to_user_record(result, sresult, /* alias_name= */ NULL, ret); |
298 | 5.52k | if (r < 0) |
299 | 0 | return r; |
300 | | |
301 | 5.52k | if (ret) |
302 | 5.52k | (*ret)->incomplete = incomplete; |
303 | 5.52k | return 0; |
304 | 5.52k | } |
305 | | |
306 | | int nss_group_to_group_record( |
307 | | const struct group *grp, |
308 | | const struct sgrp *sgrp, |
309 | | const char *alias_name, |
310 | 2.44k | GroupRecord **ret) { |
311 | | |
312 | 2.44k | _cleanup_(group_record_unrefp) GroupRecord *g = NULL; |
313 | 2.44k | int r; |
314 | | |
315 | 2.44k | assert(grp); |
316 | | |
317 | 2.44k | if (isempty(grp->gr_name)) |
318 | 0 | return -EINVAL; |
319 | | |
320 | 2.44k | if (sgrp && !streq_ptr(sgrp->sg_namp, grp->gr_name)) |
321 | 0 | return -EINVAL; |
322 | | |
323 | 2.44k | g = group_record_new(); |
324 | 2.44k | if (!g) |
325 | 0 | return -ENOMEM; |
326 | | |
327 | 2.44k | g->group_name = strdup(grp->gr_name); |
328 | 2.44k | if (!g->group_name) |
329 | 0 | return -ENOMEM; |
330 | | |
331 | 2.44k | r = nss_add_valid_alias(&g->aliases, g->group_name, alias_name); |
332 | 2.44k | if (r < 0) |
333 | 0 | return r; |
334 | | |
335 | 2.44k | r = strv_extend_strv_utf8_only(&g->members, grp->gr_mem, false); |
336 | 2.44k | if (r < 0) |
337 | 0 | return r; |
338 | | |
339 | 2.44k | g->gid = grp->gr_gid; |
340 | | |
341 | 2.44k | if (sgrp) { |
342 | 0 | if (looks_like_hashed_password(utf8_only(sgrp->sg_passwd))) { |
343 | 0 | g->hashed_password = strv_new(sgrp->sg_passwd); |
344 | 0 | if (!g->hashed_password) |
345 | 0 | return -ENOMEM; |
346 | 0 | } |
347 | | |
348 | 0 | r = strv_extend_strv_utf8_only(&g->members, sgrp->sg_mem, true); |
349 | 0 | if (r < 0) |
350 | 0 | return r; |
351 | | |
352 | 0 | r = strv_extend_strv_utf8_only(&g->administrators, sgrp->sg_adm, false); |
353 | 0 | if (r < 0) |
354 | 0 | return r; |
355 | 0 | } |
356 | | |
357 | 2.44k | r = sd_json_buildo( |
358 | 2.44k | &g->json, |
359 | 2.44k | SD_JSON_BUILD_PAIR_STRING("groupName", g->group_name), |
360 | 2.44k | JSON_BUILD_PAIR_STRV_NON_EMPTY("aliases", g->aliases), |
361 | 2.44k | SD_JSON_BUILD_PAIR_UNSIGNED("gid", g->gid), |
362 | 2.44k | SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->members), "members", SD_JSON_BUILD_STRV(g->members)), |
363 | 2.44k | SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->hashed_password), "privileged", SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_STRV("hashedPassword", g->hashed_password))), |
364 | 2.44k | SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->administrators), "administrators", SD_JSON_BUILD_STRV(g->administrators))); |
365 | 2.44k | if (r < 0) |
366 | 0 | return r; |
367 | | |
368 | 2.44k | g->mask = USER_RECORD_REGULAR | |
369 | 2.44k | (!strv_isempty(g->hashed_password) ? USER_RECORD_PRIVILEGED : 0); |
370 | | |
371 | 2.44k | if (ret) |
372 | 2.44k | *ret = TAKE_PTR(g); |
373 | 2.44k | return 0; |
374 | 2.44k | } |
375 | | |
376 | 0 | int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer) { |
377 | 0 | size_t buflen = 4096; |
378 | 0 | int r; |
379 | |
|
380 | 0 | assert(grp); |
381 | 0 | assert(ret_sgrp); |
382 | 0 | assert(ret_buffer); |
383 | |
|
384 | 0 | for (;;) { |
385 | 0 | _cleanup_free_ char *buf = NULL; |
386 | 0 | struct sgrp sgrp = {}, *result = NULL; |
387 | |
|
388 | 0 | buf = malloc0(buflen); |
389 | 0 | if (!buf) |
390 | 0 | return -ENOMEM; |
391 | | |
392 | 0 | r = getsgnam_r(grp->gr_name, &sgrp, buf, buflen, &result); |
393 | 0 | if (r == 0) { |
394 | 0 | if (!result) |
395 | 0 | return -ESRCH; |
396 | | |
397 | 0 | *ret_sgrp = *result; |
398 | 0 | *ret_buffer = TAKE_PTR(buf); |
399 | 0 | return 0; |
400 | 0 | } |
401 | 0 | if (r < 0) |
402 | 0 | return -EIO; /* Weird, this should not return negative! */ |
403 | 0 | if (r != ERANGE) |
404 | 0 | return -r; |
405 | | |
406 | 0 | if (buflen > SIZE_MAX / 2) |
407 | 0 | return -ERANGE; |
408 | | |
409 | 0 | buflen *= 2; |
410 | 0 | buf = mfree(buf); |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | int nss_group_record_by_name( |
415 | | const char *name, |
416 | | bool with_shadow, |
417 | 7.80k | GroupRecord **ret) { |
418 | | |
419 | 7.80k | _cleanup_free_ char *sbuf = NULL; |
420 | 7.80k | _cleanup_free_ struct group *result = NULL; |
421 | 7.80k | bool incomplete = false; |
422 | 7.80k | struct sgrp sgrp, *sresult = NULL; |
423 | 7.80k | int r; |
424 | | |
425 | 7.80k | assert(name); |
426 | | |
427 | 7.80k | r = getgrnam_malloc(name, &result); |
428 | 7.80k | if (r < 0) |
429 | 6.92k | return r; |
430 | | |
431 | 887 | if (with_shadow) { |
432 | 0 | r = nss_sgrp_for_group(result, &sgrp, &sbuf); |
433 | 0 | if (r < 0) { |
434 | 0 | log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name); |
435 | 0 | incomplete = ERRNO_IS_PRIVILEGE(r); |
436 | 0 | } else |
437 | 0 | sresult = &sgrp; |
438 | 0 | } else |
439 | 887 | incomplete = true; |
440 | | |
441 | 887 | r = nss_group_to_group_record(result, sresult, name, ret); |
442 | 887 | if (r < 0) |
443 | 0 | return r; |
444 | | |
445 | 887 | if (ret) |
446 | 887 | (*ret)->incomplete = incomplete; |
447 | 887 | return 0; |
448 | 887 | } |
449 | | |
450 | | int nss_group_record_by_gid( |
451 | | gid_t gid, |
452 | | bool with_shadow, |
453 | 6.76k | GroupRecord **ret) { |
454 | | |
455 | 6.76k | _cleanup_free_ char *sbuf = NULL; |
456 | 6.76k | _cleanup_free_ struct group *result = NULL; |
457 | 6.76k | bool incomplete = false; |
458 | 6.76k | struct sgrp sgrp, *sresult = NULL; |
459 | 6.76k | int r; |
460 | | |
461 | 6.76k | r = getgrgid_malloc(gid, &result); |
462 | 6.76k | if (r < 0) |
463 | 5.20k | return r; |
464 | | |
465 | 1.56k | if (with_shadow) { |
466 | 0 | r = nss_sgrp_for_group(result, &sgrp, &sbuf); |
467 | 0 | if (r < 0) { |
468 | 0 | log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name); |
469 | 0 | incomplete = ERRNO_IS_PRIVILEGE(r); |
470 | 0 | } else |
471 | 0 | sresult = &sgrp; |
472 | 0 | } else |
473 | 1.56k | incomplete = true; |
474 | | |
475 | 1.56k | r = nss_group_to_group_record(result, sresult, /* alias_name= */ NULL, ret); |
476 | 1.56k | if (r < 0) |
477 | 0 | return r; |
478 | | |
479 | 1.56k | if (ret) |
480 | 1.56k | (*ret)->incomplete = incomplete; |
481 | 1.56k | return 0; |
482 | 1.56k | } |