• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 // generate ID prefix and random salt for given encryption algorithm.
get_salt(char * salt,char * algo)11 int get_salt(char *salt, char *algo)
12 {
13   struct {
14     char *type, id, len;
15   } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
16   int i;
17 
18   for (i = 0; i < ARRAY_LEN(al); i++) {
19     if (!strcmp(algo, al[i].type)) {
20       int len = al[i].len;
21       char *s = salt;
22 
23       if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
24 
25       // Read appropriate number of random bytes for salt
26       xgetrandom(libbuf, ((len*6)+7)/8, 0);
27 
28       // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
29       for (i = 0; i<len; i++) {
30         int bitpos = i*6, bits = bitpos/8;
31 
32         bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
33         bits += 46;
34         if (bits > 57) bits += 7;
35         if (bits > 90) bits += 6;
36 
37         s[i] = bits;
38       }
39       salt[len] = 0;
40 
41       return s-salt;
42     }
43   }
44 
45   return -1;
46 }
47 
48 // Prompt with mesg, read password into buf, return 0 for success 1 for fail
read_password(char * buf,int buflen,char * mesg)49 int read_password(char *buf, int buflen, char *mesg)
50 {
51   struct termios oldtermio;
52   struct sigaction sa = {.sa_handler = generic_signal}, oldsa;
53   int i, tty = tty_fd(), ret = 1;
54 
55   // Set NOP signal handler to return from the read.
56   sigaction(SIGINT, &sa, &oldsa);
57   tcflush(tty, TCIFLUSH);
58   xset_terminal(tty, 1, 0, &oldtermio);
59   dprintf(tty, "%s", mesg);
60 
61   // Loop assembling password. (Too long = fail)
62   for (i = 0; i<buflen-1; i++) {
63     // tty closed, or EOF or ctrl-D at start, or ctrl-C anywhere: fail.
64     if ((ret = read(tty, buf+i, 1))<0 || (!ret&&!i) || *buf==4 || buf[i]==3)
65       break;
66     // EOF or newline: return success
67     else if (!ret || buf[i]=='\n' || buf[i]=='\r') {
68       ret = 0;
69       break;
70     } else if (buf[i] == 8 || buf[i] == 127) i -= 2-!i;
71   }
72 
73   // Restore terminal/signal state, terminate string
74   tcsetattr(0, TCSANOW, &oldtermio);
75   sigaction(SIGINT, &oldsa, 0);
76   xputc('\n');
77   buf[i*!ret] = 0;
78 
79   return ret;
80 }
81 
82 /* update colon-separated text files ala /etc/{passwd,shadow,group,gshadow}
83  * username = string match for first entry in line
84  * entry = new entry (NULL deletes matching line from file)
85  * pos = which entry to replace with "entry" (0 is first)
86  */
87 // filename+ = new copy being written, filename- = backup of old version
88 // returns 1 for success, 0 for failure
update_password(char * filename,char * username,char * entry,int pos)89 int update_password(char *filename, char *username, char *entry, int pos)
90 {
91   char *filenamesfx = xmprintf("%s-", filename), *line = 0, *start, *end;
92   FILE *ofp, *nfp;
93   int ret = 0, found = 0, len = strlen(username)*!strchr(username, ':'), ii;
94   struct flock lock = {.l_type = F_WRLCK};
95   long long ll = 0;
96 
97   // Open old filename ("r" won't let us lock), get blocking lock
98   if (!(ofp = fopen(filename, "w+")) || 0>fcntl(fileno(ofp), F_SETLK, &lock)) {
99     perror_msg("%s", filename);
100     goto free_storage;
101   }
102 
103   // Delete old backup, link new backup. (Failure here isn't fatal.)
104   unlink(filenamesfx);
105   if (0>link(filename, filenamesfx)) perror_msg("%s", filenamesfx);
106 
107   // Open new file to copy entries to
108   filenamesfx[strlen(filenamesfx)-1] = '+';
109   if (!(nfp = fopen(filenamesfx, "w+"))) {
110     perror_msg("%s", filenamesfx);
111     goto free_storage;
112   }
113 
114   // Loop through lines
115   while (getline(&line, (void *)&ll, ofp)) {
116     // find matching line
117     start = end = line;
118     if (strncmp(chomp(line), username, len) || line[len]!=':') {
119       found++;
120       if (!entry) continue;
121 
122       // Find start and end of span to replace
123       for (ii = pos;;) {
124         while (*end != ':') {
125           if (!*end) break;
126           end++;
127         }
128         if (ii) {
129           start = ++end;
130           ii--;
131         } else break;
132       }
133       if (ii) start = end = line;
134     }
135 
136     // Write with replacement (if any)
137     fprintf(nfp, "%*s%s%s\n", (int)(start-line), line,
138             (start==line) ? "" : entry, end);
139     memset(line, 0, strlen(line));
140   }
141   free(line);
142   fflush(nfp);
143   fsync(fileno(nfp));
144   fclose(nfp);  // automatically unlocks
145 
146   if (!found || rename(filenamesfx, filename)) {
147     if (found) perror_msg("%s -> %s", filenamesfx, filename);
148     else if (entry) fprintf(nfp, "%s\n", entry);
149     unlink(filenamesfx);
150   } else ret = 1;
151 
152 free_storage:
153   if (ofp) fclose(ofp);
154   free(filenamesfx);
155 
156   return ret;
157 }
158