Home | History | Annotate | Download | only in lib
      1 /* password.c - password read/update helper functions.
      2  *
      3  * Copyright 2012 Ashwini Kumar <ak.ashwini (at) 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.
     12 int 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
     50 int 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 
     89 static 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  */
    108 int 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;
    114   struct flock lock;
    115 
    116   shadow = strstr(filename, "shadow");
    117   filenamesfx = xmprintf("%s+", filename);
    118   sfx = strchr(filenamesfx, '+');
    119 
    120   exfp = fopen(filename, "r+");
    121   if (!exfp) {
    122     perror_msg("Couldn't open file %s",filename);
    123     goto free_storage;
    124   }
    125 
    126   *sfx = '-';
    127   unlink(filenamesfx);
    128   ret = link(filename, filenamesfx);
    129   if (ret < 0) error_msg("can't create backup file");
    130 
    131   *sfx = '+';
    132   lock.l_type = F_WRLCK;
    133   lock.l_whence = SEEK_SET;
    134   lock.l_start = 0;
    135   lock.l_len = 0;
    136 
    137   ret = fcntl(fileno(exfp), F_SETLK, &lock);
    138   if (ret < 0) perror_msg("Couldn't lock file %s",filename);
    139 
    140   lock.l_type = F_UNLCK; //unlocking at a later stage
    141 
    142   newfp = fopen(filenamesfx, "w+");
    143   if (!newfp) {
    144     error_msg("couldn't open file for writing");
    145     ret = -1;
    146     fclose(exfp);
    147     goto free_storage;
    148   }
    149 
    150   ret = 0;
    151   namesfx = xmprintf("%s:",username);
    152   while ((line = get_line(fileno(exfp))) != NULL)
    153   {
    154     if (strncmp(line, namesfx, strlen(namesfx)))
    155       fprintf(newfp, "%s\n", line);
    156     else if (entry) {
    157       char *current_ptr = NULL;
    158 
    159       found = 1;
    160       if (!strcmp(toys.which->name, "passwd")) {
    161         fprintf(newfp, "%s%s:",namesfx, entry);
    162         current_ptr = get_nextcolon(line, 2); //past passwd
    163         if (shadow) {
    164           fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
    165           current_ptr = get_nextcolon(current_ptr, 1);
    166           fprintf(newfp, "%s\n",current_ptr);
    167         } else fprintf(newfp, "%s\n",current_ptr);
    168       } else if (!strcmp(toys.which->name, "groupadd") ||
    169           !strcmp(toys.which->name, "addgroup") ||
    170           !strcmp(toys.which->name, "delgroup") ||
    171           !strcmp(toys.which->name, "groupdel")){
    172         current_ptr = get_nextcolon(line, 3); //past gid/admin list
    173         *current_ptr = '\0';
    174         fprintf(newfp, "%s", line);
    175         fprintf(newfp, "%s\n", entry);
    176       }
    177     }
    178     free(line);
    179   }
    180   free(namesfx);
    181   if (!found && entry) fprintf(newfp, "%s\n", entry);
    182   fcntl(fileno(exfp), F_SETLK, &lock);
    183   fclose(exfp);
    184 
    185   errno = 0;
    186   fflush(newfp);
    187   fsync(fileno(newfp));
    188   fclose(newfp);
    189   rename(filenamesfx, filename);
    190   if (errno) {
    191     perror_msg("File Writing/Saving failed: ");
    192     unlink(filenamesfx);
    193     ret = -1;
    194   }
    195 
    196 free_storage:
    197   free(filenamesfx);
    198   return ret;
    199 }
    200