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 required
+= 2; /* strlen(PASSWORD_SEE_SHADOW) + 1 */
40 assert_se(rn
= user_record_real_name(hr
));
41 required
+= strlen(rn
) + 1;
43 assert_se(hd
= user_record_home_directory(hr
));
44 required
+= strlen(hd
) + 1;
46 assert_se(shell
= user_record_shell(hr
));
47 required
+= strlen(shell
) + 1;
49 if (buflen
< required
)
52 *pwd
= (struct passwd
) {
55 .pw_gid
= user_record_gid(hr
),
60 pwd
->pw_passwd
= stpcpy(pwd
->pw_name
, hr
->user_name
) + 1;
61 pwd
->pw_gecos
= stpcpy(pwd
->pw_passwd
, PASSWORD_SEE_SHADOW
) + 1;
62 pwd
->pw_dir
= stpcpy(pwd
->pw_gecos
, rn
) + 1;
63 pwd
->pw_shell
= stpcpy(pwd
->pw_dir
, hd
) + 1;
64 strcpy(pwd
->pw_shell
, shell
);
69 enum nss_status
userdb_getpwnam(
72 char *buffer
, size_t buflen
,
75 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
81 if (_nss_systemd_is_blocked())
82 return NSS_STATUS_NOTFOUND
;
84 r
= userdb_by_name(name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &hr
);
86 return NSS_STATUS_NOTFOUND
;
89 return NSS_STATUS_UNAVAIL
;
92 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
95 return NSS_STATUS_TRYAGAIN
;
98 return NSS_STATUS_SUCCESS
;
101 enum nss_status
userdb_getpwuid(
108 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
114 if (_nss_systemd_is_blocked())
115 return NSS_STATUS_NOTFOUND
;
117 r
= userdb_by_uid(uid
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &hr
);
119 return NSS_STATUS_NOTFOUND
;
122 return NSS_STATUS_UNAVAIL
;
125 r
= nss_pack_user_record(hr
, pwd
, buffer
, buflen
);
128 return NSS_STATUS_TRYAGAIN
;
131 return NSS_STATUS_SUCCESS
;
134 int nss_pack_user_record_shadow(
146 assert(hr
->user_name
);
147 required
= strlen(hr
->user_name
) + 1;
149 assert_se(hashed
= strv_isempty(hr
->hashed_password
) ? PASSWORD_LOCKED_AND_INVALID
: hr
->hashed_password
[0]);
150 required
+= strlen(hashed
) + 1;
152 if (buflen
< required
)
155 *spwd
= (struct spwd
) {
157 .sp_lstchg
= hr
->last_password_change_usec
== 0 ? 1 : /* map 0 to 1, since 0 means please change password on next login */
158 hr
->last_password_change_usec
== UINT64_MAX
? -1 :
159 (long int) (hr
->last_password_change_usec
/ USEC_PER_DAY
),
160 .sp_min
= hr
->password_change_min_usec
!= UINT64_MAX
? (long int) (hr
->password_change_min_usec
/ USEC_PER_DAY
) : -1,
161 .sp_max
= hr
->password_change_max_usec
!= UINT64_MAX
? (long int) (hr
->password_change_max_usec
/ USEC_PER_DAY
) : -1,
162 .sp_warn
= hr
->password_change_warn_usec
!= UINT64_MAX
? (long int) (hr
->password_change_warn_usec
/ USEC_PER_DAY
) : -1,
163 .sp_inact
= hr
->password_change_inactive_usec
!= UINT64_MAX
? (long int) (hr
->password_change_inactive_usec
/ USEC_PER_DAY
) : -1,
164 .sp_expire
= hr
->locked
> 0 || hr
->not_after_usec
== 0 ? 1 : /* already expired/locked */
165 hr
->not_after_usec
== UINT64_MAX
? -1 :
166 (long int) (hr
->not_after_usec
/ USEC_PER_DAY
),
167 .sp_flag
= ULONG_MAX
,
172 spwd
->sp_pwdp
= stpcpy(spwd
->sp_namp
, hr
->user_name
) + 1;
173 strcpy(spwd
->sp_pwdp
, hashed
);
178 enum nss_status
userdb_getspnam(
181 char *buffer
, size_t buflen
,
184 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
190 if (_nss_systemd_is_blocked())
191 return NSS_STATUS_NOTFOUND
;
193 r
= userdb_by_name(name
, nss_glue_userdb_flags(), &hr
);
195 return NSS_STATUS_NOTFOUND
;
198 return NSS_STATUS_UNAVAIL
;
201 if (hr
->incomplete
) /* protected records missing? */
202 return NSS_STATUS_NOTFOUND
;
204 r
= nss_pack_user_record_shadow(hr
, spwd
, buffer
, buflen
);
207 return NSS_STATUS_TRYAGAIN
;
210 return NSS_STATUS_SUCCESS
;
213 int nss_pack_group_record(
215 char **extra_members
,
220 char **array
= NULL
, *p
, **m
;
221 size_t required
, n
= 0, i
= 0;
226 assert(g
->group_name
);
227 required
= strlen(g
->group_name
) + 1;
229 STRV_FOREACH(m
, g
->members
) {
230 required
+= sizeof(char*); /* space for ptr array entry */
231 required
+= strlen(*m
) + 1;
234 STRV_FOREACH(m
, extra_members
) {
235 if (strv_contains(g
->members
, *m
))
238 required
+= sizeof(char*);
239 required
+= strlen(*m
) + 1;
243 required
+= sizeof(char*); /* trailing NULL in ptr array entry */
245 if (buflen
< required
)
248 array
= (char**) buffer
; /* place ptr array at beginning of buffer, under assumption buffer is aligned */
249 p
= buffer
+ sizeof(void*) * (n
+ 1); /* place member strings right after the ptr array */
251 STRV_FOREACH(m
, g
->members
) {
253 p
= stpcpy(p
, *m
) + 1;
255 STRV_FOREACH(m
, extra_members
) {
256 if (strv_contains(g
->members
, *m
))
260 p
= stpcpy(p
, *m
) + 1;
266 *gr
= (struct group
) {
267 .gr_name
= strcpy(p
, g
->group_name
),
269 .gr_passwd
= (char*) PASSWORD_SEE_SHADOW
,
276 enum nss_status
userdb_getgrnam(
283 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
284 _cleanup_strv_free_
char **members
= NULL
;
290 if (_nss_systemd_is_blocked())
291 return NSS_STATUS_NOTFOUND
;
293 r
= groupdb_by_name(name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &g
);
294 if (r
< 0 && r
!= -ESRCH
) {
296 return NSS_STATUS_UNAVAIL
;
299 r
= membershipdb_by_group_strv(name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &members
);
300 if (r
< 0 && r
!= -ESRCH
) {
302 return NSS_STATUS_UNAVAIL
;
306 _unused_
_cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
308 if (strv_isempty(members
))
309 return NSS_STATUS_NOTFOUND
;
311 /* Grmbl, so we are supposed to extend a group entry, but the group entry itself is not
312 * accessible via non-NSS. Hence let's do what we have to do, and query NSS after all to
313 * acquire it, so that we can extend it (that's because glibc's group merging feature will
314 * merge groups only if both GID and name match and thus we need to have both first). It
315 * sucks behaving recursively likely this, but it's apparently what everybody does. We break
316 * the recursion for ourselves via the _nss_systemd_block_nss() lock. */
318 r
= _nss_systemd_block(true);
324 r
= nss_group_record_by_name(name
, false, &g
);
326 return NSS_STATUS_NOTFOUND
;
329 return NSS_STATUS_UNAVAIL
;
333 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
336 return NSS_STATUS_TRYAGAIN
;
339 return NSS_STATUS_SUCCESS
;
342 enum nss_status
userdb_getgrgid(
350 _cleanup_(group_record_unrefp
) GroupRecord
*g
= NULL
;
351 _cleanup_strv_free_
char **members
= NULL
;
358 if (_nss_systemd_is_blocked())
359 return NSS_STATUS_NOTFOUND
;
361 r
= groupdb_by_gid(gid
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &g
);
362 if (r
< 0 && r
!= -ESRCH
) {
364 return NSS_STATUS_UNAVAIL
;
368 _unused_
_cleanup_(_nss_systemd_unblockp
) bool blocked
= false;
370 /* So, quite possibly we have to extend an existing group record with additional members. But
371 * to do this we need to know the group name first. The group didn't exist via non-NSS
372 * queries though, hence let's try to acquire it here recursively via NSS. */
374 r
= _nss_systemd_block(true);
380 r
= nss_group_record_by_gid(gid
, false, &g
);
382 return NSS_STATUS_NOTFOUND
;
385 return NSS_STATUS_UNAVAIL
;
392 r
= membershipdb_by_group_strv(g
->group_name
, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW
, &members
);
393 if (r
< 0 && r
!= -ESRCH
) {
395 return NSS_STATUS_UNAVAIL
;
398 /* If we acquired the record via NSS then there's no reason to respond unless we have to augment the
399 * list of members of the group */
400 if (from_nss
&& strv_isempty(members
))
401 return NSS_STATUS_NOTFOUND
;
403 r
= nss_pack_group_record(g
, members
, gr
, buffer
, buflen
);
406 return NSS_STATUS_TRYAGAIN
;
409 return NSS_STATUS_SUCCESS
;
412 int nss_pack_group_record_shadow(
424 assert(hr
->group_name
);
425 required
= strlen(hr
->group_name
) + 1;
427 assert_se(hashed
= strv_isempty(hr
->hashed_password
) ? PASSWORD_LOCKED_AND_INVALID
: hr
->hashed_password
[0]);
428 required
+= strlen(hashed
) + 1;
430 if (buflen
< required
)
433 *sgrp
= (struct sgrp
) {
439 sgrp
->sg_passwd
= stpcpy(sgrp
->sg_namp
, hr
->group_name
) + 1;
440 strcpy(sgrp
->sg_passwd
, hashed
);
445 enum nss_status
userdb_getsgnam(
448 char *buffer
, size_t buflen
,
451 _cleanup_(group_record_unrefp
) GroupRecord
*hr
= NULL
;
457 if (_nss_systemd_is_blocked())
458 return NSS_STATUS_NOTFOUND
;
460 r
= groupdb_by_name(name
, nss_glue_userdb_flags(), &hr
);
462 return NSS_STATUS_NOTFOUND
;
465 return NSS_STATUS_UNAVAIL
;
468 if (hr
->incomplete
) /* protected records missing? */
469 return NSS_STATUS_NOTFOUND
;
471 r
= nss_pack_group_record_shadow(hr
, sgrp
, buffer
, buflen
);
474 return NSS_STATUS_TRYAGAIN
;
477 return NSS_STATUS_SUCCESS
;