master xplshn/aruu / shared / libutil / passwd.c
  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}