1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "errno-util.h"
4 #include "format-util.h"
5 #include "libcrypt-util.h"
7 #include "user-record-nss.h"
9 #define SET_IF(field, condition, value, fallback) \
10 field = (condition) ? (value) : (fallback)
12 int nss_passwd_to_user_record(
13 const struct passwd
*pwd
,
14 const struct spwd
*spwd
,
17 _cleanup_(user_record_unrefp
) UserRecord
*hr
= NULL
;
23 if (isempty(pwd
->pw_name
))
26 if (spwd
&& !streq_ptr(spwd
->sp_namp
, pwd
->pw_name
))
29 hr
= user_record_new();
33 r
= free_and_strdup(&hr
->user_name
, pwd
->pw_name
);
37 r
= free_and_strdup(&hr
->real_name
,
38 streq_ptr(pwd
->pw_gecos
, hr
->user_name
) ? NULL
: empty_to_null(pwd
->pw_gecos
));
42 r
= free_and_strdup(&hr
->home_directory
, empty_to_null(pwd
->pw_dir
));
46 r
= free_and_strdup(&hr
->shell
, empty_to_null(pwd
->pw_shell
));
50 hr
->uid
= pwd
->pw_uid
;
51 hr
->gid
= pwd
->pw_gid
;
53 if (spwd
&& hashed_password_valid(spwd
->sp_pwdp
)) {
54 strv_free_erase(hr
->hashed_password
);
55 hr
->hashed_password
= strv_new(spwd
->sp_pwdp
);
56 if (!hr
->hashed_password
)
59 hr
->hashed_password
= strv_free_erase(hr
->hashed_password
);
61 /* shadow-utils suggests using "chage -E 0" (or -E 1, depending on which man page you check)
62 * for locking a whole account, hence check for that. Note that it also defines a way to lock
63 * just a password instead of the whole account, but that's mostly pointless in times of
64 * password-less authorization, hence let's not bother. */
67 spwd
&& spwd
->sp_expire
>= 0,
68 spwd
->sp_expire
<= 1, -1);
70 SET_IF(hr
->not_after_usec
,
71 spwd
&& spwd
->sp_expire
> 1 && (uint64_t) spwd
->sp_expire
< (UINT64_MAX
-1)/USEC_PER_DAY
,
72 spwd
->sp_expire
* USEC_PER_DAY
, UINT64_MAX
);
74 SET_IF(hr
->password_change_now
,
75 spwd
&& spwd
->sp_lstchg
>= 0,
76 spwd
->sp_lstchg
== 0, -1);
78 SET_IF(hr
->last_password_change_usec
,
79 spwd
&& spwd
->sp_lstchg
> 0 && (uint64_t) spwd
->sp_lstchg
<= (UINT64_MAX
-1)/USEC_PER_DAY
,
80 spwd
->sp_lstchg
* USEC_PER_DAY
, UINT64_MAX
);
82 SET_IF(hr
->password_change_min_usec
,
83 spwd
&& spwd
->sp_min
> 0 && (uint64_t) spwd
->sp_min
<= (UINT64_MAX
-1)/USEC_PER_DAY
,
84 spwd
->sp_min
* USEC_PER_DAY
, UINT64_MAX
);
86 SET_IF(hr
->password_change_max_usec
,
87 spwd
&& spwd
->sp_max
> 0 && (uint64_t) spwd
->sp_max
<= (UINT64_MAX
-1)/USEC_PER_DAY
,
88 spwd
->sp_max
* USEC_PER_DAY
, UINT64_MAX
);
90 SET_IF(hr
->password_change_warn_usec
,
91 spwd
&& spwd
->sp_warn
> 0 && (uint64_t) spwd
->sp_warn
<= (UINT64_MAX
-1)/USEC_PER_DAY
,
92 spwd
->sp_warn
* USEC_PER_DAY
, UINT64_MAX
);
94 SET_IF(hr
->password_change_inactive_usec
,
95 spwd
&& spwd
->sp_inact
> 0 && (uint64_t) spwd
->sp_inact
<= (UINT64_MAX
-1)/USEC_PER_DAY
,
96 spwd
->sp_inact
* USEC_PER_DAY
, UINT64_MAX
);
98 hr
->json
= json_variant_unref(hr
->json
);
99 r
= json_build(&hr
->json
, JSON_BUILD_OBJECT(
100 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(hr
->user_name
)),
101 JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(hr
->uid
)),
102 JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(hr
->gid
)),
103 JSON_BUILD_PAIR_CONDITION(hr
->real_name
, "realName", JSON_BUILD_STRING(hr
->real_name
)),
104 JSON_BUILD_PAIR_CONDITION(hr
->home_directory
, "homeDirectory", JSON_BUILD_STRING(hr
->home_directory
)),
105 JSON_BUILD_PAIR_CONDITION(hr
->shell
, "shell", JSON_BUILD_STRING(hr
->shell
)),
106 JSON_BUILD_PAIR_CONDITION(!strv_isempty(hr
->hashed_password
), "privileged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRV(hr
->hashed_password
)))),
107 JSON_BUILD_PAIR_CONDITION(hr
->locked
>= 0, "locked", JSON_BUILD_BOOLEAN(hr
->locked
)),
108 JSON_BUILD_PAIR_CONDITION(hr
->not_after_usec
!= UINT64_MAX
, "notAfterUSec", JSON_BUILD_UNSIGNED(hr
->not_after_usec
)),
109 JSON_BUILD_PAIR_CONDITION(hr
->password_change_now
>= 0, "passwordChangeNow", JSON_BUILD_BOOLEAN(hr
->password_change_now
)),
110 JSON_BUILD_PAIR_CONDITION(hr
->last_password_change_usec
!= UINT64_MAX
, "lastPasswordChangeUSec", JSON_BUILD_UNSIGNED(hr
->last_password_change_usec
)),
111 JSON_BUILD_PAIR_CONDITION(hr
->password_change_min_usec
!= UINT64_MAX
, "passwordChangeMinUSec", JSON_BUILD_UNSIGNED(hr
->password_change_min_usec
)),
112 JSON_BUILD_PAIR_CONDITION(hr
->password_change_max_usec
!= UINT64_MAX
, "passwordChangeMaxUSec", JSON_BUILD_UNSIGNED(hr
->password_change_max_usec
)),
113 JSON_BUILD_PAIR_CONDITION(hr
->password_change_warn_usec
!= UINT64_MAX
, "passwordChangeWarnUSec", JSON_BUILD_UNSIGNED(hr
->password_change_warn_usec
)),
114 JSON_BUILD_PAIR_CONDITION(hr
->password_change_inactive_usec
!= UINT64_MAX
, "passwordChangeInactiveUSec", JSON_BUILD_UNSIGNED(hr
->password_change_inactive_usec
))));
119 hr
->mask
= USER_RECORD_REGULAR
|
120 (!strv_isempty(hr
->hashed_password
) ? USER_RECORD_PRIVILEGED
: 0);
126 int nss_spwd_for_passwd(const struct passwd
*pwd
, struct spwd
*ret_spwd
, char **ret_buffer
) {
127 size_t buflen
= 4096;
135 _cleanup_free_
char *buf
= NULL
;
136 struct spwd spwd
, *result
;
138 buf
= malloc(buflen
);
142 r
= getspnam_r(pwd
->pw_name
, &spwd
, buf
, buflen
, &result
);
148 *ret_buffer
= TAKE_PTR(buf
);
152 return -EIO
; /* Weird, this should not return negative! */
156 if (buflen
> SIZE_MAX
/ 2)
164 int nss_user_record_by_name(const char *name
, UserRecord
**ret
) {
165 _cleanup_free_
char *buf
= NULL
, *sbuf
= NULL
;
166 struct passwd pwd
, *result
;
167 bool incomplete
= false;
168 size_t buflen
= 4096;
176 buf
= malloc(buflen
);
180 r
= getpwnam_r(name
, &pwd
, buf
, buflen
, &result
);
189 return log_debug_errno(SYNTHETIC_ERRNO(EIO
), "getpwnam_r() returned a negative value");
193 if (buflen
> SIZE_MAX
/ 2)
200 r
= nss_spwd_for_passwd(result
, &spwd
, &sbuf
);
202 log_debug_errno(r
, "Failed to do shadow lookup for user %s, ignoring: %m", name
);
203 incomplete
= ERRNO_IS_PRIVILEGE(r
);
206 r
= nss_passwd_to_user_record(result
, r
>= 0 ? &spwd
: NULL
, ret
);
210 (*ret
)->incomplete
= incomplete
;
214 int nss_user_record_by_uid(uid_t uid
, UserRecord
**ret
) {
215 _cleanup_free_
char *buf
= NULL
, *sbuf
= NULL
;
216 struct passwd pwd
, *result
;
217 bool incomplete
= false;
218 size_t buflen
= 4096;
225 buf
= malloc(buflen
);
229 r
= getpwuid_r(uid
, &pwd
, buf
, buflen
, &result
);
237 return log_debug_errno(SYNTHETIC_ERRNO(EIO
), "getpwuid_r() returned a negative value");
241 if (buflen
> SIZE_MAX
/ 2)
248 r
= nss_spwd_for_passwd(result
, &spwd
, &sbuf
);
250 log_debug_errno(r
, "Failed to do shadow lookup for UID " UID_FMT
", ignoring: %m", uid
);
251 incomplete
= ERRNO_IS_PRIVILEGE(r
);
254 r
= nss_passwd_to_user_record(result
, r
>= 0 ? &spwd
: NULL
, ret
);
258 (*ret
)->incomplete
= incomplete
;