toybox/lib/password.c
<<
>>
Prefs
   1/* password.c - password read/update helper functions.
   2 *
   3 * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
   4 *
   5 * TODO: cleanup
   6 */
   7
   8#include "toys.h"
   9
  10// generate ID prefix and random salt for given encryption algorithm.
  11int get_salt(char *salt, char *algo)
  12{
  13  struct {
  14    char *type, id, len;
  15  } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
  16  int i;
  17
  18  for (i = 0; i < ARRAY_LEN(al); i++) {
  19    if (!strcmp(algo, al[i].type)) {
  20      int len = al[i].len;
  21      char *s = salt;
  22
  23      if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
  24
  25      // Read appropriate number of random bytes for salt
  26      xgetrandom(libbuf, ((len*6)+7)/8, 0);
  27
  28      // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
  29      for (i = 0; i<len; i++) {
  30        int bitpos = i*6, bits = bitpos/8;
  31
  32        bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
  33        bits += 46;
  34        if (bits > 57) bits += 7;
  35        if (bits > 90) bits += 6;
  36
  37        s[i] = bits;
  38      }
  39      salt[len] = 0;
  40
  41      return s-salt;
  42    }
  43  }
  44
  45  return -1;
  46}
  47
  48// Prompt with mesg, read password into buf, return 0 for success 1 for fail
  49int read_password(char *buf, int buflen, char *mesg)
  50{
  51  struct termios oldtermio;
  52  struct sigaction sa = {.sa_handler = generic_signal}, oldsa;
  53  int i, tty = tty_fd(), ret = 1;
  54
  55  // Set NOP signal handler to return from the read.
  56  sigaction(SIGINT, &sa, &oldsa);
  57  tcflush(tty, TCIFLUSH);
  58  xset_terminal(tty, 1, 0, &oldtermio);
  59  dprintf(tty, "%s", mesg);
  60
  61  // Loop assembling password. (Too long = fail)
  62  for (i = 0; i<buflen-1; i++) {
  63    // tty closed, or EOF or ctrl-D at start, or ctrl-C anywhere: fail.
  64    if ((ret = read(tty, buf+i, 1))<0 || (!ret&&!i) || *buf==4 || buf[i]==3)
  65      break;
  66    // EOF or newline: return success
  67    else if (!ret || buf[i]=='\n' || buf[i]=='\r') {
  68      ret = 0;
  69      break;
  70    } else if (buf[i] == 8 || buf[i] == 127) i -= 2-!i;
  71  }
  72
  73  // Restore terminal/signal state, terminate string
  74  tcsetattr(0, TCSANOW, &oldtermio);
  75  sigaction(SIGINT, &oldsa, 0);
  76  xputc('\n');
  77  buf[i*!ret] = 0;
  78
  79  return ret;
  80}
  81
  82/* update colon-separated text files ala /etc/{passwd,shadow,group,gshadow}
  83 * username = string match for first entry in line
  84 * entry = new entry (NULL deletes matching line from file)
  85 * pos = which entry to replace with "entry" (0 is first)
  86 */
  87// filename+ = new copy being written, filename- = backup of old version
  88// returns 1 for success, 0 for failure
  89int update_password(char *filename, char *username, char *entry, int pos)
  90{
  91  char *filenamesfx = xmprintf("%s-", filename), *line = 0, *start, *end;
  92  FILE *ofp, *nfp;
  93  int ret = 0, found = 0, len = strlen(username)*!strchr(username, ':'), ii;
  94  struct flock lock = {.l_type = F_WRLCK};
  95  long long ll = 0;
  96
  97  // Open old filename ("r" won't let us lock), get blocking lock
  98  if (!(ofp = fopen(filename, "w+")) || 0>fcntl(fileno(ofp), F_SETLK, &lock)) {
  99    perror_msg("%s", filename);
 100    goto free_storage;
 101  }
 102
 103  // Delete old backup, link new backup. (Failure here isn't fatal.)
 104  unlink(filenamesfx);
 105  if (0>link(filename, filenamesfx)) perror_msg("%s", filenamesfx);
 106
 107  // Open new file to copy entries to
 108  filenamesfx[strlen(filenamesfx)-1] = '+';
 109  if (!(nfp = fopen(filenamesfx, "w+"))) {
 110    perror_msg("%s", filenamesfx);
 111    goto free_storage;
 112  }
 113
 114  // Loop through lines
 115  while (getline(&line, (void *)&ll, ofp)) {
 116    // find matching line
 117    start = end = line;
 118    if (strncmp(chomp(line), username, len) || line[len]!=':') {
 119      found++;
 120      if (!entry) continue;
 121
 122      // Find start and end of span to replace
 123      for (ii = pos;;) {
 124        while (*end != ':') {
 125          if (!*end) break;
 126          end++;
 127        }
 128        if (ii) {
 129          start = ++end;
 130          ii--;
 131        } else break;
 132      }
 133      if (ii) start = end = line;
 134    }
 135
 136    // Write with replacement (if any)
 137    fprintf(nfp, "%*s%s%s\n", (int)(start-line), line,
 138            (start==line) ? "" : entry, end);
 139    memset(line, 0, strlen(line));
 140  }
 141  free(line);
 142  fflush(nfp);
 143  fsync(fileno(nfp));
 144  fclose(nfp);  // automatically unlocks
 145
 146  if (!found || rename(filenamesfx, filename)) {
 147    if (found) perror_msg("%s -> %s", filenamesfx, filename);
 148    else if (entry) fprintf(nfp, "%s\n", entry);
 149    unlink(filenamesfx);
 150  } else ret = 1;
 151
 152free_storage:
 153  if (ofp) fclose(ofp);
 154  free(filenamesfx);
 155
 156  return ret;
 157}
 158