8 static char *itoa(char *p
, uint32_t x
)
10 // number of digits in a uint32_t + NUL
20 int __getpw_a(const char *name
, uid_t uid
, struct passwd
*pw
, char **buf
, size_t *size
, struct passwd
**res
)
28 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, &cs
);
30 f
= fopen("/etc/passwd", "rbe");
36 while (!(rv
= __getpwent_a(f
, pw
, buf
, size
, res
)) && *res
) {
37 if (name
&& !strcmp(name
, (*res
)->pw_name
)
38 || !name
&& (*res
)->pw_uid
== uid
)
43 if (!*res
&& (rv
== 0 || rv
== ENOENT
|| rv
== ENOTDIR
)) {
44 int32_t req
= name
? GETPWBYNAME
: GETPWBYUID
;
46 int32_t passwdbuf
[PW_LEN
] = {0};
48 char uidbuf
[11] = {0};
53 /* uid outside of this range can't be queried with the
54 * nscd interface, but might happen if uid_t ever
55 * happens to be a larger type (this is not true as of
58 if(uid
< 0 || uid
> UINT32_MAX
) {
62 key
= itoa(uidbuf
, uid
);
65 f
= __nscd_query(req
, key
, passwdbuf
, sizeof passwdbuf
, (int[]){0});
66 if (!f
) { rv
= errno
; goto done
; }
68 if(!passwdbuf
[PWFOUND
]) { rv
= 0; goto cleanup_f
; }
70 /* A zero length response from nscd is invalid. We ignore
71 * invalid responses and just report an error, rather than
72 * trying to do something with them.
74 if (!passwdbuf
[PWNAMELEN
] || !passwdbuf
[PWPASSWDLEN
]
75 || !passwdbuf
[PWGECOSLEN
] || !passwdbuf
[PWDIRLEN
]
76 || !passwdbuf
[PWSHELLLEN
]) {
81 if ((passwdbuf
[PWNAMELEN
]|passwdbuf
[PWPASSWDLEN
]
82 |passwdbuf
[PWGECOSLEN
]|passwdbuf
[PWDIRLEN
]
83 |passwdbuf
[PWSHELLLEN
]) >= SIZE_MAX
/8) {
88 len
= passwdbuf
[PWNAMELEN
] + passwdbuf
[PWPASSWDLEN
]
89 + passwdbuf
[PWGECOSLEN
] + passwdbuf
[PWDIRLEN
]
90 + passwdbuf
[PWSHELLLEN
];
92 if (len
> *size
|| !*buf
) {
93 char *tmp
= realloc(*buf
, len
);
102 if (!fread(*buf
, len
, 1, f
)) {
103 rv
= ferror(f
) ? errno
: EIO
;
108 pw
->pw_passwd
= pw
->pw_name
+ passwdbuf
[PWNAMELEN
];
109 pw
->pw_gecos
= pw
->pw_passwd
+ passwdbuf
[PWPASSWDLEN
];
110 pw
->pw_dir
= pw
->pw_gecos
+ passwdbuf
[PWGECOSLEN
];
111 pw
->pw_shell
= pw
->pw_dir
+ passwdbuf
[PWDIRLEN
];
112 pw
->pw_uid
= passwdbuf
[PWUID
];
113 pw
->pw_gid
= passwdbuf
[PWGID
];
115 /* Don't assume that nscd made sure to null terminate strings.
116 * It's supposed to, but malicious nscd should be ignored
117 * rather than causing a crash.
119 if (pw
->pw_passwd
[-1] || pw
->pw_gecos
[-1] || pw
->pw_dir
[-1]
120 || pw
->pw_shell
[passwdbuf
[PWSHELLLEN
]-1]) {
125 if (name
&& strcmp(name
, pw
->pw_name
)
126 || !name
&& uid
!= pw
->pw_uid
) {
139 pthread_setcancelstate(cs
, 0);