10f66f451Sopenharmony_ci/* password.c - password read/update helper functions.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * TODO: cleanup
60f66f451Sopenharmony_ci */
70f66f451Sopenharmony_ci
80f66f451Sopenharmony_ci#include "toys.h"
90f66f451Sopenharmony_ci#include <time.h>
100f66f451Sopenharmony_ci
110f66f451Sopenharmony_ci// generate ID prefix and random salt for given encryption algorithm.
120f66f451Sopenharmony_ciint get_salt(char *salt, char *algo)
130f66f451Sopenharmony_ci{
140f66f451Sopenharmony_ci  struct {
150f66f451Sopenharmony_ci    char *type, id, len;
160f66f451Sopenharmony_ci  } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
170f66f451Sopenharmony_ci  int i;
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci  for (i = 0; i < ARRAY_LEN(al); i++) {
200f66f451Sopenharmony_ci    if (!strcmp(algo, al[i].type)) {
210f66f451Sopenharmony_ci      int len = al[i].len;
220f66f451Sopenharmony_ci      char *s = salt;
230f66f451Sopenharmony_ci
240f66f451Sopenharmony_ci      if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
250f66f451Sopenharmony_ci
260f66f451Sopenharmony_ci      // Read appropriate number of random bytes for salt
270f66f451Sopenharmony_ci      xgetrandom(libbuf, ((len*6)+7)/8, 0);
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_ci      // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
300f66f451Sopenharmony_ci      for (i=0; i<len; i++) {
310f66f451Sopenharmony_ci        int bitpos = i*6, bits = bitpos/8;
320f66f451Sopenharmony_ci
330f66f451Sopenharmony_ci        bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
340f66f451Sopenharmony_ci        bits += 46;
350f66f451Sopenharmony_ci        if (bits > 57) bits += 7;
360f66f451Sopenharmony_ci        if (bits > 90) bits += 6;
370f66f451Sopenharmony_ci
380f66f451Sopenharmony_ci        s[i] = bits;
390f66f451Sopenharmony_ci      }
400f66f451Sopenharmony_ci      salt[len] = 0;
410f66f451Sopenharmony_ci
420f66f451Sopenharmony_ci      return s-salt;
430f66f451Sopenharmony_ci    }
440f66f451Sopenharmony_ci  }
450f66f451Sopenharmony_ci
460f66f451Sopenharmony_ci  return -1;
470f66f451Sopenharmony_ci}
480f66f451Sopenharmony_ci
490f66f451Sopenharmony_ci// Prompt with mesg, read password into buf, return 0 for success 1 for fail
500f66f451Sopenharmony_ciint read_password(char *buf, int buflen, char *mesg)
510f66f451Sopenharmony_ci{
520f66f451Sopenharmony_ci  struct termios oldtermio;
530f66f451Sopenharmony_ci  struct sigaction sa, oldsa;
540f66f451Sopenharmony_ci  int i, ret = 1;
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ci  // NOP signal handler to return from the read. Use sigaction() instead
570f66f451Sopenharmony_ci  // of xsignal() because we want to restore the old handler afterwards.
580f66f451Sopenharmony_ci  memset(&sa, 0, sizeof(sa));
590f66f451Sopenharmony_ci  sa.sa_handler = generic_signal;
600f66f451Sopenharmony_ci  sigaction(SIGINT, &sa, &oldsa);
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ci  tcflush(0, TCIFLUSH);
630f66f451Sopenharmony_ci  xset_terminal(0, 1, 0, &oldtermio);
640f66f451Sopenharmony_ci
650f66f451Sopenharmony_ci  xprintf("%s", mesg);
660f66f451Sopenharmony_ci
670f66f451Sopenharmony_ci  for (i=0; i < buflen-1; i++) {
680f66f451Sopenharmony_ci    if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) {
690f66f451Sopenharmony_ci      i = 0;
700f66f451Sopenharmony_ci      ret = 1;
710f66f451Sopenharmony_ci
720f66f451Sopenharmony_ci      break;
730f66f451Sopenharmony_ci    } else if (!ret || buf[i] == '\n' || buf[i] == '\r') {
740f66f451Sopenharmony_ci      ret = 0;
750f66f451Sopenharmony_ci
760f66f451Sopenharmony_ci      break;
770f66f451Sopenharmony_ci    } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1;
780f66f451Sopenharmony_ci  }
790f66f451Sopenharmony_ci
800f66f451Sopenharmony_ci  // Restore terminal/signal state, terminate string
810f66f451Sopenharmony_ci  sigaction(SIGINT, &oldsa, NULL);
820f66f451Sopenharmony_ci  tcsetattr(0, TCSANOW, &oldtermio);
830f66f451Sopenharmony_ci  buf[i] = 0;
840f66f451Sopenharmony_ci  xputc('\n');
850f66f451Sopenharmony_ci
860f66f451Sopenharmony_ci  return ret;
870f66f451Sopenharmony_ci}
880f66f451Sopenharmony_ci
890f66f451Sopenharmony_cistatic char *get_nextcolon(char *line, int cnt)
900f66f451Sopenharmony_ci{
910f66f451Sopenharmony_ci  while (cnt--) {
920f66f451Sopenharmony_ci    if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
930f66f451Sopenharmony_ci    line++; //jump past the colon
940f66f451Sopenharmony_ci  }
950f66f451Sopenharmony_ci  return line;
960f66f451Sopenharmony_ci}
970f66f451Sopenharmony_ci
980f66f451Sopenharmony_ci/*update_password is used by multiple utilities to update /etc/passwd,
990f66f451Sopenharmony_ci * /etc/shadow, /etc/group and /etc/gshadow files,
1000f66f451Sopenharmony_ci * which are used as user, group databeses
1010f66f451Sopenharmony_ci * entry can be
1020f66f451Sopenharmony_ci * 1. encrypted password, when updating user password.
1030f66f451Sopenharmony_ci * 2. complete entry for user details, when creating new user
1040f66f451Sopenharmony_ci * 3. group members comma',' separated list, when adding user to group
1050f66f451Sopenharmony_ci * 4. complete entry for group details, when creating new group
1060f66f451Sopenharmony_ci * 5. entry = NULL, delete the named entry user/group
1070f66f451Sopenharmony_ci */
1080f66f451Sopenharmony_ciint update_password(char *filename, char* username, char* entry)
1090f66f451Sopenharmony_ci{
1100f66f451Sopenharmony_ci  char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
1110f66f451Sopenharmony_ci       *sfx = NULL, *line = NULL;
1120f66f451Sopenharmony_ci  FILE *exfp, *newfp;
1130f66f451Sopenharmony_ci  int ret = -1, found = 0, n;
1140f66f451Sopenharmony_ci  struct flock lock;
1150f66f451Sopenharmony_ci  size_t allocated_length;
1160f66f451Sopenharmony_ci
1170f66f451Sopenharmony_ci  shadow = strstr(filename, "shadow");
1180f66f451Sopenharmony_ci  filenamesfx = xmprintf("%s+", filename);
1190f66f451Sopenharmony_ci  sfx = strchr(filenamesfx, '+');
1200f66f451Sopenharmony_ci
1210f66f451Sopenharmony_ci  exfp = fopen(filename, "r+");
1220f66f451Sopenharmony_ci  if (!exfp) {
1230f66f451Sopenharmony_ci    perror_msg("Couldn't open file %s",filename);
1240f66f451Sopenharmony_ci    goto free_storage;
1250f66f451Sopenharmony_ci  }
1260f66f451Sopenharmony_ci
1270f66f451Sopenharmony_ci  *sfx = '-';
1280f66f451Sopenharmony_ci  unlink(filenamesfx);
1290f66f451Sopenharmony_ci  ret = link(filename, filenamesfx);
1300f66f451Sopenharmony_ci  if (ret < 0) error_msg("can't create backup file");
1310f66f451Sopenharmony_ci
1320f66f451Sopenharmony_ci  *sfx = '+';
1330f66f451Sopenharmony_ci  lock.l_type = F_WRLCK;
1340f66f451Sopenharmony_ci  lock.l_whence = SEEK_SET;
1350f66f451Sopenharmony_ci  lock.l_start = 0;
1360f66f451Sopenharmony_ci  lock.l_len = 0;
1370f66f451Sopenharmony_ci
1380f66f451Sopenharmony_ci  ret = fcntl(fileno(exfp), F_SETLK, &lock);
1390f66f451Sopenharmony_ci  if (ret < 0) perror_msg("Couldn't lock file %s",filename);
1400f66f451Sopenharmony_ci
1410f66f451Sopenharmony_ci  lock.l_type = F_UNLCK; //unlocking at a later stage
1420f66f451Sopenharmony_ci
1430f66f451Sopenharmony_ci  newfp = fopen(filenamesfx, "w+");
1440f66f451Sopenharmony_ci  if (!newfp) {
1450f66f451Sopenharmony_ci    error_msg("couldn't open file for writing");
1460f66f451Sopenharmony_ci    ret = -1;
1470f66f451Sopenharmony_ci    fclose(exfp);
1480f66f451Sopenharmony_ci    goto free_storage;
1490f66f451Sopenharmony_ci  }
1500f66f451Sopenharmony_ci
1510f66f451Sopenharmony_ci  ret = 0;
1520f66f451Sopenharmony_ci  namesfx = xmprintf("%s:",username);
1530f66f451Sopenharmony_ci  while ((n = getline(&line, &allocated_length, exfp)) > 0) {
1540f66f451Sopenharmony_ci    line[n-1] = 0;
1550f66f451Sopenharmony_ci    if (strncmp(line, namesfx, strlen(namesfx)))
1560f66f451Sopenharmony_ci      fprintf(newfp, "%s\n", line);
1570f66f451Sopenharmony_ci    else if (entry) {
1580f66f451Sopenharmony_ci      char *current_ptr = NULL;
1590f66f451Sopenharmony_ci
1600f66f451Sopenharmony_ci      found = 1;
1610f66f451Sopenharmony_ci      if (!strcmp(toys.which->name, "passwd")) {
1620f66f451Sopenharmony_ci        fprintf(newfp, "%s%s:",namesfx, entry);
1630f66f451Sopenharmony_ci        current_ptr = get_nextcolon(line, 2); //past passwd
1640f66f451Sopenharmony_ci        if (shadow) {
1650f66f451Sopenharmony_ci          fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
1660f66f451Sopenharmony_ci          current_ptr = get_nextcolon(current_ptr, 1);
1670f66f451Sopenharmony_ci          fprintf(newfp, "%s\n",current_ptr);
1680f66f451Sopenharmony_ci        } else fprintf(newfp, "%s\n",current_ptr);
1690f66f451Sopenharmony_ci      } else if (!strcmp(toys.which->name, "groupadd") ||
1700f66f451Sopenharmony_ci          !strcmp(toys.which->name, "addgroup") ||
1710f66f451Sopenharmony_ci          !strcmp(toys.which->name, "delgroup") ||
1720f66f451Sopenharmony_ci          !strcmp(toys.which->name, "groupdel")){
1730f66f451Sopenharmony_ci        current_ptr = get_nextcolon(line, 3); //past gid/admin list
1740f66f451Sopenharmony_ci        *current_ptr = '\0';
1750f66f451Sopenharmony_ci        fprintf(newfp, "%s", line);
1760f66f451Sopenharmony_ci        fprintf(newfp, "%s\n", entry);
1770f66f451Sopenharmony_ci      }
1780f66f451Sopenharmony_ci    }
1790f66f451Sopenharmony_ci  }
1800f66f451Sopenharmony_ci  free(line);
1810f66f451Sopenharmony_ci  free(namesfx);
1820f66f451Sopenharmony_ci  if (!found && entry) fprintf(newfp, "%s\n", entry);
1830f66f451Sopenharmony_ci  fcntl(fileno(exfp), F_SETLK, &lock);
1840f66f451Sopenharmony_ci  fclose(exfp);
1850f66f451Sopenharmony_ci
1860f66f451Sopenharmony_ci  errno = 0;
1870f66f451Sopenharmony_ci  fflush(newfp);
1880f66f451Sopenharmony_ci  fsync(fileno(newfp));
1890f66f451Sopenharmony_ci  fclose(newfp);
1900f66f451Sopenharmony_ci  rename(filenamesfx, filename);
1910f66f451Sopenharmony_ci  if (errno) {
1920f66f451Sopenharmony_ci    perror_msg("File Writing/Saving failed: ");
1930f66f451Sopenharmony_ci    unlink(filenamesfx);
1940f66f451Sopenharmony_ci    ret = -1;
1950f66f451Sopenharmony_ci  }
1960f66f451Sopenharmony_ci
1970f66f451Sopenharmony_cifree_storage:
1980f66f451Sopenharmony_ci  free(filenamesfx);
1990f66f451Sopenharmony_ci  return ret;
2000f66f451Sopenharmony_ci}
201