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