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#include <time.h>
  10
  11// generate ID prefix and random salt for given encryption algorithm.
  12int get_salt(char *salt, char *algo)
  13{
  14  struct {
  15    char *type, id, len;
  16  } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
  17  int i;
  18
  19  for (i = 0; i < ARRAY_LEN(al); i++) {
  20    if (!strcmp(algo, al[i].type)) {
  21      int len = al[i].len;
  22      char *s = salt;
  23
  24      if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
  25
  26      // Read appropriate number of random bytes for salt
  27      xgetrandom(libbuf, ((len*6)+7)/8, 0);
  28
  29      // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
  30      for (i=0; i<len; i++) {
  31        int bitpos = i*6, bits = bitpos/8;
  32
  33        bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
  34        bits += 46;
  35        if (bits > 57) bits += 7;
  36        if (bits > 90) bits += 6;
  37
  38        s[i] = bits;
  39      }
  40      salt[len] = 0;
  41
  42      return s-salt;
  43    }
  44  }
  45
  46  return -1;
  47}
  48
  49// Prompt with mesg, read password into buf, return 0 for success 1 for fail
  50int read_password(char *buf, int buflen, char *mesg)
  51{
  52  struct termios oldtermio;
  53  struct sigaction sa, oldsa;
  54  int i, ret = 1;
  55
  56  // NOP signal handler to return from the read. Use sigaction() instead
  57  // of xsignal() because we want to restore the old handler afterwards.
  58  memset(&sa, 0, sizeof(sa));
  59  sa.sa_handler = generic_signal;
  60  sigaction(SIGINT, &sa, &oldsa);
  61
  62  tcflush(0, TCIFLUSH);
  63  xset_terminal(0, 1, 0, &oldtermio);
  64
  65  xprintf("%s", mesg);
  66
  67  for (i=0; i < buflen-1; i++) {
  68    if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) {
  69      i = 0;
  70      ret = 1;
  71
  72      break;
  73    } else if (!ret || buf[i] == '\n' || buf[i] == '\r') {
  74      ret = 0;
  75
  76      break;
  77    } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1;
  78  }
  79
  80  // Restore terminal/signal state, terminate string
  81  sigaction(SIGINT, &oldsa, NULL);
  82  tcsetattr(0, TCSANOW, &oldtermio);
  83  buf[i] = 0;
  84  xputc('\n');
  85
  86  return ret;
  87}
  88
  89static char *get_nextcolon(char *line, int cnt)
  90{
  91  while (cnt--) {
  92    if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
  93    line++; //jump past the colon
  94  }
  95  return line;
  96}
  97
  98/*update_password is used by multiple utilities to update /etc/passwd,
  99 * /etc/shadow, /etc/group and /etc/gshadow files,
 100 * which are used as user, group databeses
 101 * entry can be
 102 * 1. encrypted password, when updating user password.
 103 * 2. complete entry for user details, when creating new user
 104 * 3. group members comma',' separated list, when adding user to group
 105 * 4. complete entry for group details, when creating new group
 106 * 5. entry = NULL, delete the named entry user/group
 107 */
 108int update_password(char *filename, char* username, char* entry)
 109{
 110  char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
 111       *sfx = NULL, *line = NULL;
 112  FILE *exfp, *newfp;
 113  int ret = -1, found = 0, n;
 114  struct flock lock;
 115  size_t allocated_length;
 116
 117  shadow = strstr(filename, "shadow");
 118  filenamesfx = xmprintf("%s+", filename);
 119  sfx = strchr(filenamesfx, '+');
 120
 121  exfp = fopen(filename, "r+");
 122  if (!exfp) {
 123    perror_msg("Couldn't open file %s",filename);
 124    goto free_storage;
 125  }
 126
 127  *sfx = '-';
 128  unlink(filenamesfx);
 129  ret = link(filename, filenamesfx);
 130  if (ret < 0) error_msg("can't create backup file");
 131
 132  *sfx = '+';
 133  lock.l_type = F_WRLCK;
 134  lock.l_whence = SEEK_SET;
 135  lock.l_start = 0;
 136  lock.l_len = 0;
 137
 138  ret = fcntl(fileno(exfp), F_SETLK, &lock);
 139  if (ret < 0) perror_msg("Couldn't lock file %s",filename);
 140
 141  lock.l_type = F_UNLCK; //unlocking at a later stage
 142
 143  newfp = fopen(filenamesfx, "w+");
 144  if (!newfp) {
 145    error_msg("couldn't open file for writing");
 146    ret = -1;
 147    fclose(exfp);
 148    goto free_storage;
 149  }
 150
 151  ret = 0;
 152  namesfx = xmprintf("%s:",username);
 153  while ((n = getline(&line, &allocated_length, exfp)) > 0) {
 154    line[n-1] = 0;
 155    if (strncmp(line, namesfx, strlen(namesfx)))
 156      fprintf(newfp, "%s\n", line);
 157    else if (entry) {
 158      char *current_ptr = NULL;
 159
 160      found = 1;
 161      if (!strcmp(toys.which->name, "passwd")) {
 162        fprintf(newfp, "%s%s:",namesfx, entry);
 163        current_ptr = get_nextcolon(line, 2); //past passwd
 164        if (shadow) {
 165          fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
 166          current_ptr = get_nextcolon(current_ptr, 1);
 167          fprintf(newfp, "%s\n",current_ptr);
 168        } else fprintf(newfp, "%s\n",current_ptr);
 169      } else if (!strcmp(toys.which->name, "groupadd") ||
 170          !strcmp(toys.which->name, "addgroup") ||
 171          !strcmp(toys.which->name, "delgroup") ||
 172          !strcmp(toys.which->name, "groupdel")){
 173        current_ptr = get_nextcolon(line, 3); //past gid/admin list
 174        *current_ptr = '\0';
 175        fprintf(newfp, "%s", line);
 176        fprintf(newfp, "%s\n", entry);
 177      }
 178    }
 179  }
 180  free(line);
 181  free(namesfx);
 182  if (!found && entry) fprintf(newfp, "%s\n", entry);
 183  fcntl(fileno(exfp), F_SETLK, &lock);
 184  fclose(exfp);
 185
 186  errno = 0;
 187  fflush(newfp);
 188  fsync(fileno(newfp));
 189  fclose(newfp);
 190  rename(filenamesfx, filename);
 191  if (errno) {
 192    perror_msg("File Writing/Saving failed: ");
 193    unlink(filenamesfx);
 194    ret = -1;
 195  }
 196
 197free_storage:
 198  free(filenamesfx);
 199  return ret;
 200}
 201