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 appropriate random salt string 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       i = xopenro("/dev/urandom");
     28       xreadall(i, libbuf, ((len*6)+7)/8);
     29       close(i);
     30 
     31       // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
     32       for (i=0; i<len; i++) {
     33         int bitpos = i*6, bits = bitpos/8;
     34 
     35         bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
     36         bits += 46;
     37         if (bits > 57) bits += 7;
     38         if (bits > 90) bits += 6;
     39 
     40         s[i] = bits;
     41       }
     42       salt[len] = 0;
     43 
     44       return s-salt;
     45     }
     46   }
     47 
     48   return -1;
     49 }
     50 
     51 // Prompt with mesg, read password into buf, return 0 for success 1 for fail
     52 int read_password(char *buf, int buflen, char *mesg)
     53 {
     54   struct termios oldtermio;
     55   struct sigaction sa, oldsa;
     56   int i, ret = 1;
     57 
     58   // NOP signal handler to return from the read
     59   memset(&sa, 0, sizeof(sa));
     60   sa.sa_handler = generic_signal;
     61   sigaction(SIGINT, &sa, &oldsa);
     62 
     63   tcflush(0, TCIFLUSH);
     64   set_terminal(0, 1, &oldtermio);
     65 
     66   xprintf("%s", mesg);
     67 
     68   for (i=0; i < buflen-1; i++) {
     69     if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) {
     70       i = 0;
     71       ret = 1;
     72 
     73       break;
     74     } else if (!ret || buf[i] == '\n' || buf[i] == '\r') {
     75       ret = 0;
     76 
     77       break;
     78     } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1;
     79   }
     80 
     81   // Restore terminal/signal state, terminate string
     82   sigaction(SIGINT, &oldsa, NULL);
     83   tcsetattr(0, TCSANOW, &oldtermio);
     84   buf[i] = 0;
     85   xputc('\n');
     86 
     87   return ret;
     88 }
     89 
     90 static char *get_nextcolon(char *line, int cnt)
     91 {
     92   while (cnt--) {
     93     if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
     94     line++; //jump past the colon
     95   }
     96   return line;
     97 }
     98 
     99 /*update_password is used by multiple utilities to update /etc/passwd,
    100  * /etc/shadow, /etc/group and /etc/gshadow files,
    101  * which are used as user, group databeses
    102  * entry can be
    103  * 1. encrypted password, when updating user password.
    104  * 2. complete entry for user details, when creating new user
    105  * 3. group members comma',' separated list, when adding user to group
    106  * 4. complete entry for group details, when creating new group
    107  * 5. entry = NULL, delete the named entry user/group
    108  */
    109 int update_password(char *filename, char* username, char* entry)
    110 {
    111   char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
    112        *sfx = NULL, *line = NULL;
    113   FILE *exfp, *newfp;
    114   int ret = -1, found = 0;
    115   struct flock lock;
    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 ((line = get_line(fileno(exfp))) != NULL)
    154   {
    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     free(line);
    180   }
    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 
    197 free_storage:
    198   free(filenamesfx);
    199   return ret;
    200 }
    201