1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "group-record-nss.h"
7 #include "user-record.h"
8 #include "userdb-glue.h"
11 UserDBFlags
nss_glue_userdb_flags(void) {
12 UserDBFlags flags
= USERDB_AVOID_NSS
;
14 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
15 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
16 flags
|= USERDB_AVOID_DYNAMIC_USER
;
21 int nss_pack_user_record(
27 const char *rn
, *hd
, *shell
;
33 assert_se(hr
->user_name
);
34 required
= strlen(hr
->user_name
) + 1;
36 assert_se(rn
= user_record_real_name(hr
));
37 required
+= strlen(rn
) + 1;
39 assert_se(hd
= user_record_home_directory(hr
));
40 required
+= strlen(hd
) + 1;
42 assert_se(shell
= user_record_shell(hr
));
43 required
+= strlen(shell
) + 1;
45 if (buflen
< required
)
48 *pwd
= (struct passwd
) {
51 .pw_gid
= user_record_gid(hr
),
52 .pw_passwd
= (char*) "x", /* means: see shadow file */
57 pwd
->pw_gecos
= stpcpy(pwd
->pw_name
, hr
->user_name
) + 1;
58 pwd
->pw_dir
= stpcpy(pwd
->pw_gecos
, rn
) + 1;
59 pwd
->pw_shell
= stpcpy(pwd
->pw_dir
, hd
) + 1;
60 strcpy(pwd
->pw_shell
, shell
);
65 enum nss_status
userdb_getpwnam(
68 char *buffer
, size_t buflen
,
71 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
77 r
= userdb_nss_compat_is_enabled();
80 return NSS_STATUS_UNAVAIL
;
83 return NSS_STATUS_NOTFOUND
;
85 r
= userdb_by_name(name
, nss_glue_userdb_flags(), &hr
);
87 return NSS_STATUS_NOTFOUND
;
90 return NSS_STATUS_UNAVAIL
;
93 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
96 return NSS_STATUS_TRYAGAIN
;
99 return NSS_STATUS_SUCCESS
;
102 enum nss_status
userdb_getpwuid(
109 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
115 r
= userdb_nss_compat_is_enabled();
118 return NSS_STATUS_UNAVAIL
;
121 return NSS_STATUS_NOTFOUND
;
123 r
= userdb_by_uid(uid
, nss_glue_userdb_flags(), &hr
);
125 return NSS_STATUS_NOTFOUND
;
128 return NSS_STATUS_UNAVAIL
;
131 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
134 return NSS_STATUS_TRYAGAIN
;
137 return NSS_STATUS_SUCCESS
;
140 int nss_pack_group_record(
142 char **extra_members
,
147 char **array
= NULL
, *p
, **m
;
148 size_t required
, n
= 0, i
= 0;
153 assert_se(g
->group_name
);
154 required
= strlen(g
->group_name
) + 1;
156 STRV_FOREACH(m
, g
->members
) {
157 required
+= sizeof(char*); /* space for ptr array entry */
158 required
+= strlen(*m
) + 1;
161 STRV_FOREACH(m
, extra_members
) {
162 if (strv_contains(g
->members
, *m
))
165 required
+= sizeof(char*);
166 required
+= strlen(*m
) + 1;
170 required
+= sizeof(char*); /* trailing NULL in ptr array entry */
172 if (buflen
< required
)
175 array
= (char**) buffer
; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
176 p
= buffer
+ sizeof(void*) * (n
+ 1); /* place member strings right after the ptr array */
178 STRV_FOREACH(m
, g
->members
) {
180 p
= stpcpy(p
, *m
) + 1;
182 STRV_FOREACH(m
, extra_members
) {
183 if (strv_contains(g
->members
, *m
))
187 p
= stpcpy(p
, *m
) + 1;
193 *gr
= (struct group
) {
194 .gr_name
= strcpy(p
, g
->group_name
),
196 .gr_passwd
= (char*) "x", /* means: see shadow file */
203 enum nss_status
userdb_getgrnam(
210 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
211 _cleanup_strv_free_
char **members
= NULL
;
217 r
= userdb_nss_compat_is_enabled();
220 return NSS_STATUS_UNAVAIL
;
223 return NSS_STATUS_NOTFOUND
;
225 r
= groupdb_by_name(name
, nss_glue_userdb_flags(), &g
);
226 if (r
< 0 && r
!= -ESRCH
) {
228 return NSS_STATUS_UNAVAIL
;
231 r
= membershipdb_by_group_strv(name
, nss_glue_userdb_flags(), &members
);
234 return NSS_STATUS_UNAVAIL
;
238 _cleanup_close_
int lock_fd
= -1;
240 if (strv_isempty(members
))
241 return NSS_STATUS_NOTFOUND
;
243 /* Grmbl, so we are supposed to extend a group entry, but the group entry itself is not
244 * accessible via non-NSS. Hence let's do what we have to do, and query NSS after all to
245 * acquire it, so that we can extend it (that's because glibc's group merging feature will
246 * merge groups only if both GID and name match and thus we need to have both first). It
247 * sucks behaving recursively likely this, but it's apparently what everybody does. We break
248 * the recursion for ourselves via the userdb_nss_compat_disable() lock. */
250 lock_fd
= userdb_nss_compat_disable();
251 if (lock_fd
< 0 && lock_fd
!= -EBUSY
)
254 r
= nss_group_record_by_name(name
, false, &g
);
256 return NSS_STATUS_NOTFOUND
;
259 return NSS_STATUS_UNAVAIL
;
263 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
266 return NSS_STATUS_TRYAGAIN
;
269 return NSS_STATUS_SUCCESS
;
272 enum nss_status
userdb_getgrgid(
280 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
281 _cleanup_strv_free_
char **members
= NULL
;
288 r
= userdb_nss_compat_is_enabled();
291 return NSS_STATUS_UNAVAIL
;
294 return NSS_STATUS_NOTFOUND
;
296 r
= groupdb_by_gid(gid
, nss_glue_userdb_flags(), &g
);
297 if (r
< 0 && r
!= -ESRCH
) {
299 return NSS_STATUS_UNAVAIL
;
303 _cleanup_close_
int lock_fd
= -1;
305 /* So, quite possibly we have to extend an existing group record with additional members. But
306 * to do this we need to know the group name first. The group didn't exist via non-NSS
307 * queries though, hence let's try to acquire it here recursively via NSS. */
309 lock_fd
= userdb_nss_compat_disable();
310 if (lock_fd
< 0 && lock_fd
!= -EBUSY
)
313 r
= nss_group_record_by_gid(gid
, false, &g
);
315 return NSS_STATUS_NOTFOUND
;
319 return NSS_STATUS_UNAVAIL
;
326 r
= membershipdb_by_group_strv(g
->group_name
, nss_glue_userdb_flags(), &members
);
329 return NSS_STATUS_UNAVAIL
;
332 /* If we acquired the record via NSS then there's no reason to respond unless we have to augment the
333 * list of members of the group */
334 if (from_nss
&& strv_isempty(members
))
335 return NSS_STATUS_NOTFOUND
;
337 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
340 return NSS_STATUS_TRYAGAIN
;
343 return NSS_STATUS_SUCCESS
;