1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include "group-record-nss.h"
6 #include "nss-systemd.h"
8 #include "user-record.h"
9 #include "userdb-glue.h"
12 UserDBFlags
nss_glue_userdb_flags(void) {
13 UserDBFlags flags
= USERDB_AVOID_NSS
;
15 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
16 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
17 flags
|= USERDB_AVOID_DYNAMIC_USER
;
22 int nss_pack_user_record(
28 const char *rn
, *hd
, *shell
;
34 assert_se(hr
->user_name
);
35 required
= strlen(hr
->user_name
) + 1;
37 assert_se(rn
= user_record_real_name(hr
));
38 required
+= strlen(rn
) + 1;
40 assert_se(hd
= user_record_home_directory(hr
));
41 required
+= strlen(hd
) + 1;
43 assert_se(shell
= user_record_shell(hr
));
44 required
+= strlen(shell
) + 1;
46 if (buflen
< required
)
49 *pwd
= (struct passwd
) {
52 .pw_gid
= user_record_gid(hr
),
53 .pw_passwd
= (char*) "x", /* means: see shadow file */
58 pwd
->pw_gecos
= stpcpy(pwd
->pw_name
, hr
->user_name
) + 1;
59 pwd
->pw_dir
= stpcpy(pwd
->pw_gecos
, rn
) + 1;
60 pwd
->pw_shell
= stpcpy(pwd
->pw_dir
, hd
) + 1;
61 strcpy(pwd
->pw_shell
, shell
);
66 enum nss_status
userdb_getpwnam(
69 char *buffer
, size_t buflen
,
72 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
78 if (_nss_systemd_is_blocked())
79 return NSS_STATUS_NOTFOUND
;
81 r
= userdb_by_name(name
, nss_glue_userdb_flags(), &hr
);
83 return NSS_STATUS_NOTFOUND
;
86 return NSS_STATUS_UNAVAIL
;
89 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
92 return NSS_STATUS_TRYAGAIN
;
95 return NSS_STATUS_SUCCESS
;
98 enum nss_status
userdb_getpwuid(
105 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
111 if (_nss_systemd_is_blocked())
112 return NSS_STATUS_NOTFOUND
;
114 r
= userdb_by_uid(uid
, nss_glue_userdb_flags(), &hr
);
116 return NSS_STATUS_NOTFOUND
;
119 return NSS_STATUS_UNAVAIL
;
122 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
125 return NSS_STATUS_TRYAGAIN
;
128 return NSS_STATUS_SUCCESS
;
131 int nss_pack_group_record(
133 char **extra_members
,
138 char **array
= NULL
, *p
, **m
;
139 size_t required
, n
= 0, i
= 0;
144 assert_se(g
->group_name
);
145 required
= strlen(g
->group_name
) + 1;
147 STRV_FOREACH(m
, g
->members
) {
148 required
+= sizeof(char*); /* space for ptr array entry */
149 required
+= strlen(*m
) + 1;
152 STRV_FOREACH(m
, extra_members
) {
153 if (strv_contains(g
->members
, *m
))
156 required
+= sizeof(char*);
157 required
+= strlen(*m
) + 1;
161 required
+= sizeof(char*); /* trailing NULL in ptr array entry */
163 if (buflen
< required
)
166 array
= (char**) buffer
; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
167 p
= buffer
+ sizeof(void*) * (n
+ 1); /* place member strings right after the ptr array */
169 STRV_FOREACH(m
, g
->members
) {
171 p
= stpcpy(p
, *m
) + 1;
173 STRV_FOREACH(m
, extra_members
) {
174 if (strv_contains(g
->members
, *m
))
178 p
= stpcpy(p
, *m
) + 1;
184 *gr
= (struct group
) {
185 .gr_name
= strcpy(p
, g
->group_name
),
187 .gr_passwd
= (char*) "x", /* means: see shadow file */
194 enum nss_status
userdb_getgrnam(
201 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
202 _cleanup_strv_free_
char **members
= NULL
;
208 if (_nss_systemd_is_blocked())
209 return NSS_STATUS_NOTFOUND
;
211 r
= groupdb_by_name(name
, nss_glue_userdb_flags(), &g
);
212 if (r
< 0 && r
!= -ESRCH
) {
214 return NSS_STATUS_UNAVAIL
;
217 r
= membershipdb_by_group_strv(name
, nss_glue_userdb_flags(), &members
);
220 return NSS_STATUS_UNAVAIL
;
224 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
226 if (strv_isempty(members
))
227 return NSS_STATUS_NOTFOUND
;
229 /* Grmbl, so we are supposed to extend a group entry, but the group entry itself is not
230 * accessible via non-NSS. Hence let's do what we have to do, and query NSS after all to
231 * acquire it, so that we can extend it (that's because glibc's group merging feature will
232 * merge groups only if both GID and name match and thus we need to have both first). It
233 * sucks behaving recursively likely this, but it's apparently what everybody does. We break
234 * the recursion for ourselves via the _nss_systemd_block_nss() lock. */
236 r
= _nss_systemd_block(true);
242 r
= nss_group_record_by_name(name
, false, &g
);
244 return NSS_STATUS_NOTFOUND
;
247 return NSS_STATUS_UNAVAIL
;
251 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
254 return NSS_STATUS_TRYAGAIN
;
257 return NSS_STATUS_SUCCESS
;
260 enum nss_status
userdb_getgrgid(
268 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
269 _cleanup_strv_free_
char **members
= NULL
;
276 if (_nss_systemd_is_blocked())
277 return NSS_STATUS_NOTFOUND
;
279 r
= groupdb_by_gid(gid
, nss_glue_userdb_flags(), &g
);
280 if (r
< 0 && r
!= -ESRCH
) {
282 return NSS_STATUS_UNAVAIL
;
286 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
288 /* So, quite possibly we have to extend an existing group record with additional members. But
289 * to do this we need to know the group name first. The group didn't exist via non-NSS
290 * queries though, hence let's try to acquire it here recursively via NSS. */
292 r
= _nss_systemd_block(true);
298 r
= nss_group_record_by_gid(gid
, false, &g
);
300 return NSS_STATUS_NOTFOUND
;
303 return NSS_STATUS_UNAVAIL
;
310 r
= membershipdb_by_group_strv(g
->group_name
, nss_glue_userdb_flags(), &members
);
313 return NSS_STATUS_UNAVAIL
;
316 /* If we acquired the record via NSS then there's no reason to respond unless we have to augment the
317 * list of members of the group */
318 if (from_nss
&& strv_isempty(members
))
319 return NSS_STATUS_NOTFOUND
;
321 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
324 return NSS_STATUS_TRYAGAIN
;
327 return NSS_STATUS_SUCCESS
;