1/* see LICENSE file for copyright and license details */
2#include "../passwd.h"
3#include "../paths.h"
4#include "../text.h"
5#include "../util.h"
6
7#include <errno.h>
8#include <fcntl.h>
9#include <pwd.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/resource.h>
14#include <sys/stat.h>
15#include <sys/wait.h>
16#include <unistd.h>
17
18#include "../config.h"
19
20#if defined(__linux__) || defined(__GLIBC__)
21#include <crypt.h>
22#endif
23
24#if defined(__linux__) && !defined(__ANDROID__)
25#define HAVE_SHADOW 1
26#include <shadow.h>
27#endif
28
29#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
30#define HAVE_MASTER_PASSWD 1
31#endif
32
33int
34pw_init(void)
35{
36 struct rlimit rlim;
37
38 rlim.rlim_cur = 0;
39 rlim.rlim_max = 0;
40 if (setrlimit(RLIMIT_CORE, &rlim) < 0)
41 eprintf("setrlimit:");
42 return 0;
43}
44
45int
46pw_check(const struct passwd *pw, const char *pass)
47{
48 char *cryptpass, *p;
49 const char *stored;
50
51 stored = pw->pw_passwd;
52 if (stored[0] == '!' || stored[0] == '*') {
53 weprintf("denied\n");
54 return -1;
55 }
56
57 if (stored[0] == '\0') {
58 if (pass[0] == '\0')
59 return 1;
60 weprintf("incorrect password\n");
61 return 0;
62 }
63
64#if defined(HAVE_SHADOW)
65 if (stored[0] == 'x' && stored[1] == '\0') {
66 struct spwd *spw;
67 errno = 0;
68 spw = getspnam(pw->pw_name);
69 if (!spw) {
70 if (errno)
71 weprintf("getspnam: %s:", pw->pw_name);
72 else
73 weprintf("who are you?\n");
74 return -1;
75 }
76 stored = spw->sp_pwdp;
77 if (stored[0] == '!' || stored[0] == '*') {
78 weprintf("denied\n");
79 return -1;
80 }
81 /* same empty-hash fast path as above, re-checked here: stored was
82 * just reassigned from the shadow entry's own field, and an empty
83 * shadow hash means "passwordless account", not "match nothing"
84 * (falling through to crypt() with an empty salt does the latter,
85 * since crypt("", "") is never itself the empty string) */
86 if (stored[0] == '\0') {
87 if (pass[0] == '\0')
88 return 1;
89 weprintf("incorrect password\n");
90 return 0;
91 }
92 }
93#endif
94
95 cryptpass = crypt(pass, stored);
96 if (!cryptpass) {
97 weprintf("crypt:");
98 return -1;
99 }
100 p = estrdup(cryptpass);
101 if (strcmp(p, stored) != 0) {
102 free(p);
103 weprintf("incorrect password\n");
104 return 0;
105 }
106 free(p);
107 return 1;
108}
109
110int
111pwdb_lookup(struct pwdb_entry *ent, const char *name)
112{
113 struct passwd *pw;
114 const char *hash;
115
116 errno = 0;
117 pw = getpwnam(name);
118 if (!pw) {
119 if (errno)
120 weprintf("getpwnam: %s:", name);
121 else
122 weprintf("who are you?\n");
123 return -1;
124 }
125
126 ent->name = estrdup(pw->pw_name);
127 ent->uid = pw->pw_uid;
128 ent->gid = pw->pw_gid;
129 hash = pw->pw_passwd;
130
131#if defined(HAVE_SHADOW)
132 if (hash[0] == 'x' && hash[1] == '\0') {
133 struct spwd *spw;
134 errno = 0;
135 spw = getspnam(name);
136 if (spw)
137 hash = spw->sp_pwdp;
138 }
139#endif
140
141 ent->hash = estrdup(hash);
142 return 0;
143}
144
145static int
146fill_random(void *buf, size_t n)
147{
148#if defined(__linux__)
149 if (getentropy(buf, n) == 0)
150 return 0;
151#endif
152 {
153 int fd;
154 size_t off;
155 fd = open(ARUU_PATH_DEVURANDOM, O_RDONLY);
156 if (fd < 0) {
157 weprintf("open /dev/urandom:");
158 return -1;
159 }
160 off = 0;
161 while (off < n) {
162 ssize_t r = read(fd, (char *)buf + off, n - off);
163 if (r <= 0) {
164 close(fd);
165 weprintf("read /dev/urandom:");
166 return -1;
167 }
168 off += (size_t)r;
169 }
170 close(fd);
171 }
172 return 0;
173}
174
175int
176pw_gensalt_cipher(char *salt, size_t salt_sz, const char *prefix, size_t rand_len)
177{
178 static const char b64[] = "./"
179 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
180 "ijklmnopqrstuvwxyz";
181 unsigned char raw[32];
182 char body[48];
183 size_t i, n, body_len;
184 unsigned v;
185 int prefix_len;
186
187 if (rand_len > sizeof(raw))
188 rand_len = sizeof(raw);
189 if (rand_len == 0) {
190 salt[0] = '\0';
191 return 0;
192 }
193
194 prefix_len = 0;
195 if (prefix) {
196 prefix_len = snprintf(salt, salt_sz, "%s", prefix);
197 if (prefix_len < 0 || (size_t)prefix_len >= salt_sz) {
198 weprintf("snprintf:");
199 return -1;
200 }
201 }
202
203 if (fill_random(raw, rand_len) < 0)
204 return -1;
205
206 body_len = (rand_len * 4 + 2) / 3;
207 if (body_len >= sizeof(body))
208 body_len = sizeof(body) - 1;
209 n = 0;
210 for (i = 0; i + 3 <= rand_len; i += 3) {
211 v = (raw[i] << 16) | (raw[i + 1] << 8) | raw[i + 2];
212 body[n++] = b64[v & 0x3f];
213 v >>= 6;
214 body[n++] = b64[v & 0x3f];
215 v >>= 6;
216 body[n++] = b64[v & 0x3f];
217 v >>= 6;
218 body[n++] = b64[v & 0x3f];
219 }
220 if (i < rand_len) {
221 v = raw[i] << 16;
222 if (i + 1 < rand_len)
223 v |= raw[i + 1] << 8;
224 body[n++] = b64[v & 0x3f];
225 v >>= 6;
226 body[n++] = b64[v & 0x3f];
227 v >>= 6;
228 if (i + 1 < rand_len)
229 body[n++] = b64[v & 0x3f];
230 }
231 body[n] = '\0';
232
233 if ((size_t)prefix_len + n + 1 >= salt_sz) {
234 weprintf("salt buffer too small\n");
235 return -1;
236 }
237 memcpy(salt + prefix_len, body, n + 1);
238 return 0;
239}
240
241int
242pw_gensalt(char *salt, size_t salt_sz)
243{
244 return pw_gensalt_cipher(salt, salt_sz, PW_CIPHER, 16);
245}
246
247#if defined(HAVE_SHADOW)
248static int
249update_shadow(const char *name, const char *newhash)
250{
251 struct spwd *spw, cur;
252 FILE *fp, *tfp;
253 int wrote = 0;
254 char path[256];
255
256 snprintf(path, sizeof(path), ARUU_PATH_ETC "/tcb/%s/shadow", name);
257 fp = fopen(path, "r+");
258 if (!fp) {
259 memcpy(path, ARUU_PATH_SHADOW, sizeof(ARUU_PATH_SHADOW));
260 fp = fopen(path, "r+");
261 }
262 if (!fp) {
263 weprintf("fopen %s:", path);
264 return -1;
265 }
266
267 tfp = tmpfile();
268 if (!tfp) {
269 weprintf("tmpfile:");
270 fclose(fp);
271 return -1;
272 }
273
274 /* fgetspent(fp) reads from the stream we already opened above;
275 * getspent() reads an implicit global shadow stream that musl
276 * never actually backs with this (or any) file and always
277 * returns null for, silently turning every update into a
278 * "no matching entry" failure */
279 while ((spw = fgetspent(fp))) {
280 cur = *spw;
281 if (strcmp(cur.sp_namp, name) == 0) {
282 cur.sp_pwdp = (char *)newhash;
283 wrote = 1;
284 }
285 errno = 0;
286 if (putspent(&cur, tfp) == -1) {
287 weprintf("putspent:");
288 fclose(tfp);
289 fclose(fp);
290 return -1;
291 }
292 }
293 if (!wrote) {
294 weprintf("shadow: no matching entry\n");
295 fclose(tfp);
296 fclose(fp);
297 return -1;
298 }
299 fflush(tfp);
300 rewind(tfp);
301 rewind(fp);
302 fconcat(tfp, "tmpfile", fp, "shadow");
303 ftruncate(fileno(fp), ftell(tfp));
304 fclose(tfp);
305 {
306 int fd = fileno(fp);
307 if (fd >= 0)
308 fsync(fd);
309 }
310 fclose(fp);
311 return 0;
312}
313#endif
314
315static int
316update_passwd_file(const char *path, const char *name, const char *newhash)
317{
318 struct passwd *pw, cur;
319 FILE *fp, *tfp;
320 int wrote = 0;
321
322 fp = fopen(path, "r+");
323 if (!fp) {
324 weprintf("fopen %s:", path);
325 return -1;
326 }
327 tfp = tmpfile();
328 if (!tfp) {
329 weprintf("tmpfile:");
330 fclose(fp);
331 return -1;
332 }
333 while ((pw = fgetpwent(fp))) {
334 cur = *pw;
335 if (strcmp(cur.pw_name, name) == 0) {
336 cur.pw_passwd = (char *)newhash;
337 wrote = 1;
338 }
339 errno = 0;
340 if (putpwent(&cur, tfp) == -1) {
341 weprintf("putpwent:");
342 fclose(tfp);
343 fclose(fp);
344 return -1;
345 }
346 }
347 if (!wrote) {
348 weprintf("passwd: no matching entry\n");
349 fclose(tfp);
350 fclose(fp);
351 return -1;
352 }
353 fflush(tfp);
354 rewind(tfp);
355 rewind(fp);
356 fconcat(tfp, "tmpfile", fp, path);
357 ftruncate(fileno(fp), ftell(tfp));
358 {
359 int fd = fileno(fp);
360 if (fd >= 0)
361 fsync(fd);
362 }
363 fclose(tfp);
364 fclose(fp);
365 return 0;
366}
367
368#if defined(HAVE_MASTER_PASSWD)
369static int
370update_master_passwd(const char *name, const char *newhash)
371{
372 int r;
373 pid_t pid;
374 int st;
375
376 r = update_passwd_file(ARUU_BSD_PATH_MASTER_PASSWD, name, newhash);
377 if (r != 0)
378 return r;
379 pid = fork();
380 if (pid == 0) {
381 execl(ARUU_BSD_PATH_PWD_MKDB, "pwd_mkdb", "-p", NULL);
382 _exit(127);
383 }
384 if (pid > 0)
385 waitpid(pid, &st, 0);
386 return 0;
387}
388#endif
389
390int
391pwdb_update(const char *name, const char *newhash)
392{
393#if defined(HAVE_SHADOW)
394 {
395 struct spwd *spw;
396 errno = 0;
397 spw = getspnam(name);
398 if (spw)
399 return update_shadow(name, newhash);
400 }
401#elif defined(HAVE_MASTER_PASSWD)
402 return update_master_passwd(name, newhash);
403#endif
404 return update_passwd_file(ARUU_PATH_PASSWD, name, newhash);
405}