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