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  }
 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}