1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "nss-systemd.h"
7 #include "user-record-nss.h"
8 #include "user-record.h"
10 #include "userdb-glue.h"
13 UserDBFlags
nss_glue_userdb_flags(void) {
14 UserDBFlags flags
= USERDB_EXCLUDE_NSS
;
16 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
17 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
18 flags
|= USERDB_EXCLUDE_DYNAMIC_USER
;
23 int nss_pack_user_record(
29 const char *rn
, *hd
, *shell
;
35 assert(hr
->user_name
);
36 required
= strlen(hr
->user_name
) + 1;
38 assert_se(rn
= user_record_real_name(hr
));
39 required
+= strlen(rn
) + 1;
41 assert_se(hd
= user_record_home_directory(hr
));
42 required
+= strlen(hd
) + 1;
44 assert_se(shell
= user_record_shell(hr
));
45 required
+= strlen(shell
) + 1;
47 if (buflen
< required
)
50 *pwd
= (struct passwd
) {
53 .pw_gid
= user_record_gid(hr
),
54 .pw_passwd
= (char*) PASSWORD_SEE_SHADOW
,
59 pwd
->pw_gecos
= stpcpy(pwd
->pw_name
, hr
->user_name
) + 1;
60 pwd
->pw_dir
= stpcpy(pwd
->pw_gecos
, rn
) + 1;
61 pwd
->pw_shell
= stpcpy(pwd
->pw_dir
, hd
) + 1;
62 strcpy(pwd
->pw_shell
, shell
);
67 enum nss_status
userdb_getpwnam(
70 char *buffer
, size_t buflen
,
73 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
79 if (_nss_systemd_is_blocked())
80 return NSS_STATUS_NOTFOUND
;
82 r
= userdb_by_name(name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &hr
);
84 return NSS_STATUS_NOTFOUND
;
87 return NSS_STATUS_UNAVAIL
;
90 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
93 return NSS_STATUS_TRYAGAIN
;
96 return NSS_STATUS_SUCCESS
;
99 enum nss_status
userdb_getpwuid(
106 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
112 if (_nss_systemd_is_blocked())
113 return NSS_STATUS_NOTFOUND
;
115 r
= userdb_by_uid(uid
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &hr
);
117 return NSS_STATUS_NOTFOUND
;
120 return NSS_STATUS_UNAVAIL
;
123 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
126 return NSS_STATUS_TRYAGAIN
;
129 return NSS_STATUS_SUCCESS
;
132 int nss_pack_user_record_shadow(
144 assert(hr
->user_name
);
145 required
= strlen(hr
->user_name
) + 1;
147 assert_se(hashed
= strv_isempty(hr
->hashed_password
) ? PASSWORD_LOCKED_AND_INVALID
: hr
->hashed_password
[0]);
148 required
+= strlen(hashed
) + 1;
150 if (buflen
< required
)
153 *spwd
= (struct spwd
) {
155 .sp_lstchg
= hr
->last_password_change_usec
== 0 ? 1 : /* map 0 to 1, since 0 means please change pasword on next login */
156 hr
->last_password_change_usec
== UINT64_MAX
? -1 :
157 (long int) (hr
->last_password_change_usec
/ USEC_PER_DAY
),
158 .sp_min
= hr
->password_change_min_usec
!= UINT64_MAX
? (long int) (hr
->password_change_min_usec
/ USEC_PER_DAY
) : -1,
159 .sp_max
= hr
->password_change_max_usec
!= UINT64_MAX
? (long int) (hr
->password_change_max_usec
/ USEC_PER_DAY
) : -1,
160 .sp_warn
= hr
->password_change_warn_usec
!= UINT64_MAX
? (long int) (hr
->password_change_warn_usec
/ USEC_PER_DAY
) : -1,
161 .sp_inact
= hr
->password_change_inactive_usec
!= UINT64_MAX
? (long int) (hr
->password_change_inactive_usec
/ USEC_PER_DAY
) : -1,
162 .sp_expire
= hr
->locked
> 0 || hr
->not_after_usec
== 0 ? 1 : /* already expired/locked */
163 hr
->not_after_usec
== UINT64_MAX
? -1 :
164 (long int) (hr
->not_after_usec
/ USEC_PER_DAY
),
165 .sp_flag
= ULONG_MAX
,
170 spwd
->sp_pwdp
= stpcpy(spwd
->sp_namp
, hr
->user_name
) + 1;
171 strcpy(spwd
->sp_pwdp
, hashed
);
176 enum nss_status
userdb_getspnam(
179 char *buffer
, size_t buflen
,
182 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
188 if (_nss_systemd_is_blocked())
189 return NSS_STATUS_NOTFOUND
;
191 r
= userdb_by_name(name
, nss_glue_userdb_flags(), &hr
);
193 return NSS_STATUS_NOTFOUND
;
196 return NSS_STATUS_UNAVAIL
;
199 if (hr
->incomplete
) /* protected records missing? */
200 return NSS_STATUS_NOTFOUND
;
202 r
= nss_pack_user_record_shadow(hr
, spwd
, buffer
, buflen
);
205 return NSS_STATUS_TRYAGAIN
;
208 return NSS_STATUS_SUCCESS
;
211 int nss_pack_group_record(
213 char **extra_members
,
218 char **array
= NULL
, *p
, **m
;
219 size_t required
, n
= 0, i
= 0;
224 assert(g
->group_name
);
225 required
= strlen(g
->group_name
) + 1;
227 STRV_FOREACH(m
, g
->members
) {
228 required
+= sizeof(char*); /* space for ptr array entry */
229 required
+= strlen(*m
) + 1;
232 STRV_FOREACH(m
, extra_members
) {
233 if (strv_contains(g
->members
, *m
))
236 required
+= sizeof(char*);
237 required
+= strlen(*m
) + 1;
241 required
+= sizeof(char*); /* trailing NULL in ptr array entry */
243 if (buflen
< required
)
246 array
= (char**) buffer
; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
247 p
= buffer
+ sizeof(void*) * (n
+ 1); /* place member strings right after the ptr array */
249 STRV_FOREACH(m
, g
->members
) {
251 p
= stpcpy(p
, *m
) + 1;
253 STRV_FOREACH(m
, extra_members
) {
254 if (strv_contains(g
->members
, *m
))
258 p
= stpcpy(p
, *m
) + 1;
264 *gr
= (struct group
) {
265 .gr_name
= strcpy(p
, g
->group_name
),
267 .gr_passwd
= (char*) PASSWORD_SEE_SHADOW
,
274 enum nss_status
userdb_getgrnam(
281 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
282 _cleanup_strv_free_
char **members
= NULL
;
288 if (_nss_systemd_is_blocked())
289 return NSS_STATUS_NOTFOUND
;
291 r
= groupdb_by_name(name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &g
);
292 if (r
< 0 && r
!= -ESRCH
) {
294 return NSS_STATUS_UNAVAIL
;
297 r
= membershipdb_by_group_strv(name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &members
);
298 if (r
< 0 && r
!= -ESRCH
) {
300 return NSS_STATUS_UNAVAIL
;
304 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
306 if (strv_isempty(members
))
307 return NSS_STATUS_NOTFOUND
;
309 /* Grmbl, so we are supposed to extend a group entry, but the group entry itself is not
310 * accessible via non-NSS. Hence let's do what we have to do, and query NSS after all to
311 * acquire it, so that we can extend it (that's because glibc's group merging feature will
312 * merge groups only if both GID and name match and thus we need to have both first). It
313 * sucks behaving recursively likely this, but it's apparently what everybody does. We break
314 * the recursion for ourselves via the _nss_systemd_block_nss() lock. */
316 r
= _nss_systemd_block(true);
322 r
= nss_group_record_by_name(name
, false, &g
);
324 return NSS_STATUS_NOTFOUND
;
327 return NSS_STATUS_UNAVAIL
;
331 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
334 return NSS_STATUS_TRYAGAIN
;
337 return NSS_STATUS_SUCCESS
;
340 enum nss_status
userdb_getgrgid(
348 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
349 _cleanup_strv_free_
char **members
= NULL
;
356 if (_nss_systemd_is_blocked())
357 return NSS_STATUS_NOTFOUND
;
359 r
= groupdb_by_gid(gid
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &g
);
360 if (r
< 0 && r
!= -ESRCH
) {
362 return NSS_STATUS_UNAVAIL
;
366 _cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
368 /* So, quite possibly we have to extend an existing group record with additional members. But
369 * to do this we need to know the group name first. The group didn't exist via non-NSS
370 * queries though, hence let's try to acquire it here recursively via NSS. */
372 r
= _nss_systemd_block(true);
378 r
= nss_group_record_by_gid(gid
, false, &g
);
380 return NSS_STATUS_NOTFOUND
;
383 return NSS_STATUS_UNAVAIL
;
390 r
= membershipdb_by_group_strv(g
->group_name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &members
);
391 if (r
< 0 && r
!= -ESRCH
) {
393 return NSS_STATUS_UNAVAIL
;
396 /* If we acquired the record via NSS then there's no reason to respond unless we have to augment the
397 * list of members of the group */
398 if (from_nss
&& strv_isempty(members
))
399 return NSS_STATUS_NOTFOUND
;
401 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
404 return NSS_STATUS_TRYAGAIN
;
407 return NSS_STATUS_SUCCESS
;
410 int nss_pack_group_record_shadow(
422 assert(hr
->group_name
);
423 required
= strlen(hr
->group_name
) + 1;
425 assert_se(hashed
= strv_isempty(hr
->hashed_password
) ? PASSWORD_LOCKED_AND_INVALID
: hr
->hashed_password
[0]);
426 required
+= strlen(hashed
) + 1;
428 if (buflen
< required
)
431 *sgrp
= (struct sgrp
) {
437 sgrp
->sg_passwd
= stpcpy(sgrp
->sg_namp
, hr
->group_name
) + 1;
438 strcpy(sgrp
->sg_passwd
, hashed
);
443 enum nss_status
userdb_getsgnam(
446 char *buffer
, size_t buflen
,
449 _cleanup_(group_record_unrefp
) GroupRecord
*hr
= NULL
;
455 if (_nss_systemd_is_blocked())
456 return NSS_STATUS_NOTFOUND
;
458 r
= groupdb_by_name(name
, nss_glue_userdb_flags(), &hr
);
460 return NSS_STATUS_NOTFOUND
;
463 return NSS_STATUS_UNAVAIL
;
466 if (hr
->incomplete
) /* protected records missing? */
467 return NSS_STATUS_NOTFOUND
;
469 r
= nss_pack_group_record_shadow(hr
, sgrp
, buffer
, buflen
);
472 return NSS_STATUS_TRYAGAIN
;
475 return NSS_STATUS_SUCCESS
;