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 }
82#endif
83
84 cryptpass = crypt(pass, stored);
85 if (!cryptpass) {
86 weprintf("crypt:");
87 return -1;
88 }
89 p = estrdup(cryptpass);
90 if (strcmp(p, stored) != 0) {
91 free(p);
92 weprintf("incorrect password\n");
93 return 0;
94 }
95 free(p);
96 return 1;
97}
98
99int
100pwdb_lookup(struct pwdb_entry *ent, const char *name)
101{
102 struct passwd *pw;
103 const char *hash;
104
105 errno = 0;
106 pw = getpwnam(name);
107 if (!pw) {
108 if (errno)
109 weprintf("getpwnam: %s:", name);
110 else
111 weprintf("who are you?\n");
112 return -1;
113 }
114
115 ent->name = estrdup(pw->pw_name);
116 ent->uid = pw->pw_uid;
117 ent->gid = pw->pw_gid;
118 hash = pw->pw_passwd;
119
120#if defined(HAVE_SHADOW)
121 if (hash[0] == 'x' && hash[1] == '\0') {
122 struct spwd *spw;
123 errno = 0;
124 spw = getspnam(name);
125 if (spw)
126 hash = spw->sp_pwdp;
127 }
128#endif
129
130 ent->hash = estrdup(hash);
131 return 0;
132}
133
134static int
135fill_random(void *buf, size_t n)
136{
137#if defined(__linux__)
138 if (getentropy(buf, n) == 0)
139 return 0;
140#endif
141 {
142 int fd;
143 size_t off;
144 fd = open(ARUU_PATH_DEVURANDOM, O_RDONLY);
145 if (fd < 0) {
146 weprintf("open /dev/urandom:");
147 return -1;
148 }
149 off = 0;
150 while (off < n) {
151 ssize_t r = read(fd, (char *)buf + off, n - off);
152 if (r <= 0) {
153 close(fd);
154 weprintf("read /dev/urandom:");
155 return -1;
156 }
157 off += (size_t)r;
158 }
159 close(fd);
160 }
161 return 0;
162}
163
164int
165pw_gensalt_cipher(char *salt, size_t salt_sz, const char *prefix, size_t rand_len)
166{
167 static const char b64[] = "./"
168 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
169 "ijklmnopqrstuvwxyz";
170 unsigned char raw[32];
171 char body[48];
172 size_t i, n, body_len;
173 unsigned v;
174 int prefix_len;
175
176 if (rand_len > sizeof(raw))
177 rand_len = sizeof(raw);
178 if (rand_len == 0) {
179 salt[0] = '\0';
180 return 0;
181 }
182
183 prefix_len = 0;
184 if (prefix) {
185 prefix_len = snprintf(salt, salt_sz, "%s", prefix);
186 if (prefix_len < 0 || (size_t)prefix_len >= salt_sz) {
187 weprintf("snprintf:");
188 return -1;
189 }
190 }
191
192 if (fill_random(raw, rand_len) < 0)
193 return -1;
194
195 body_len = (rand_len * 4 + 2) / 3;
196 if (body_len >= sizeof(body))
197 body_len = sizeof(body) - 1;
198 n = 0;
199 for (i = 0; i + 3 <= rand_len; i += 3) {
200 v = (raw[i] << 16) | (raw[i + 1] << 8) | raw[i + 2];
201 body[n++] = b64[v & 0x3f];
202 v >>= 6;
203 body[n++] = b64[v & 0x3f];
204 v >>= 6;
205 body[n++] = b64[v & 0x3f];
206 v >>= 6;
207 body[n++] = b64[v & 0x3f];
208 }
209 if (i < rand_len) {
210 v = raw[i] << 16;
211 if (i + 1 < rand_len)
212 v |= raw[i + 1] << 8;
213 body[n++] = b64[v & 0x3f];
214 v >>= 6;
215 body[n++] = b64[v & 0x3f];
216 v >>= 6;
217 if (i + 1 < rand_len)
218 body[n++] = b64[v & 0x3f];
219 }
220 body[n] = '\0';
221
222 if ((size_t)prefix_len + n + 1 >= salt_sz) {
223 weprintf("salt buffer too small\n");
224 return -1;
225 }
226 memcpy(salt + prefix_len, body, n + 1);
227 return 0;
228}
229
230int
231pw_gensalt(char *salt, size_t salt_sz)
232{
233 return pw_gensalt_cipher(salt, salt_sz, PW_CIPHER, 16);
234}
235
236#if defined(HAVE_SHADOW)
237static int
238update_shadow(const char *name, const char *newhash)
239{
240 struct spwd *spw, cur;
241 FILE *fp, *tfp;
242 int wrote = 0;
243 char path[256];
244
245 snprintf(path, sizeof(path), ARUU_PATH_ETC "/tcb/%s/shadow", name);
246 fp = fopen(path, "r+");
247 if (!fp) {
248 memcpy(path, ARUU_PATH_SHADOW, sizeof(ARUU_PATH_SHADOW));
249 fp = fopen(path, "r+");
250 }
251 if (!fp) {
252 weprintf("fopen %s:", path);
253 return -1;
254 }
255
256 tfp = tmpfile();
257 if (!tfp) {
258 weprintf("tmpfile:");
259 fclose(fp);
260 return -1;
261 }
262
263 while ((spw = getspent())) {
264 cur = *spw;
265 if (strcmp(cur.sp_namp, name) == 0) {
266 cur.sp_pwdp = (char *)newhash;
267 wrote = 1;
268 }
269 errno = 0;
270 if (putspent(&cur, tfp) == -1) {
271 weprintf("putspent:");
272 fclose(tfp);
273 fclose(fp);
274 return -1;
275 }
276 }
277 if (!wrote) {
278 weprintf("shadow: no matching entry\n");
279 fclose(tfp);
280 fclose(fp);
281 return -1;
282 }
283 fflush(tfp);
284 rewind(tfp);
285 rewind(fp);
286 fconcat(tfp, "tmpfile", fp, "shadow");
287 ftruncate(fileno(fp), ftell(tfp));
288 fclose(tfp);
289 {
290 int fd = fileno(fp);
291 if (fd >= 0)
292 fsync(fd);
293 }
294 fclose(fp);
295 return 0;
296}
297#endif
298
299static int
300update_passwd_file(const char *path, const char *name, const char *newhash)
301{
302 struct passwd *pw, cur;
303 FILE *fp, *tfp;
304 int wrote = 0;
305
306 fp = fopen(path, "r+");
307 if (!fp) {
308 weprintf("fopen %s:", path);
309 return -1;
310 }
311 tfp = tmpfile();
312 if (!tfp) {
313 weprintf("tmpfile:");
314 fclose(fp);
315 return -1;
316 }
317 while ((pw = fgetpwent(fp))) {
318 cur = *pw;
319 if (strcmp(cur.pw_name, name) == 0) {
320 cur.pw_passwd = (char *)newhash;
321 wrote = 1;
322 }
323 errno = 0;
324 if (putpwent(&cur, tfp) == -1) {
325 weprintf("putpwent:");
326 fclose(tfp);
327 fclose(fp);
328 return -1;
329 }
330 }
331 if (!wrote) {
332 weprintf("passwd: no matching entry\n");
333 fclose(tfp);
334 fclose(fp);
335 return -1;
336 }
337 fflush(tfp);
338 rewind(tfp);
339 rewind(fp);
340 fconcat(tfp, "tmpfile", fp, path);
341 ftruncate(fileno(fp), ftell(tfp));
342 {
343 int fd = fileno(fp);
344 if (fd >= 0)
345 fsync(fd);
346 }
347 fclose(tfp);
348 fclose(fp);
349 return 0;
350}
351
352#if defined(HAVE_MASTER_PASSWD)
353static int
354update_master_passwd(const char *name, const char *newhash)
355{
356 int r;
357 pid_t pid;
358 int st;
359
360 r = update_passwd_file(ARUU_BSD_PATH_MASTER_PASSWD, name, newhash);
361 if (r != 0)
362 return r;
363 pid = fork();
364 if (pid == 0) {
365 execl(ARUU_BSD_PATH_PWD_MKDB, "pwd_mkdb", "-p", NULL);
366 _exit(127);
367 }
368 if (pid > 0)
369 waitpid(pid, &st, 0);
370 return 0;
371}
372#endif
373
374int
375pwdb_update(const char *name, const char *newhash)
376{
377#if defined(HAVE_SHADOW)
378 {
379 struct spwd *spw;
380 errno = 0;
381 spw = getspnam(name);
382 if (spw)
383 return update_shadow(name, newhash);
384 }
385#elif defined(HAVE_MASTER_PASSWD)
386 return update_master_passwd(name, newhash);
387#endif
388 return update_passwd_file(ARUU_PATH_PASSWD, name, newhash);
389}