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