1*cf5a6c84SAndroid Build Coastguard Worker /* password.c - password read/update helper functions.
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2012 Ashwini Kumar <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker */
5*cf5a6c84SAndroid Build Coastguard Worker
6*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
7*cf5a6c84SAndroid Build Coastguard Worker
8*cf5a6c84SAndroid Build Coastguard Worker // generate $id$salt$hash given a password and algorithm. Generates salt if NULL
9*cf5a6c84SAndroid Build Coastguard Worker //char *toy_crypt(char *pass, char *salt, char *algo)
10*cf5a6c84SAndroid Build Coastguard Worker // generate ID prefix and random salt for given encryption algorithm.
get_salt(char * salt,char * algo,int rand)11*cf5a6c84SAndroid Build Coastguard Worker int get_salt(char *salt, char *algo, int rand)
12*cf5a6c84SAndroid Build Coastguard Worker {
13*cf5a6c84SAndroid Build Coastguard Worker struct {
14*cf5a6c84SAndroid Build Coastguard Worker char *type, id, len;
15*cf5a6c84SAndroid Build Coastguard Worker } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
16*cf5a6c84SAndroid Build Coastguard Worker char *s;
17*cf5a6c84SAndroid Build Coastguard Worker int i, len;
18*cf5a6c84SAndroid Build Coastguard Worker
19*cf5a6c84SAndroid Build Coastguard Worker for (i = 0; i < ARRAY_LEN(al); i++) {
20*cf5a6c84SAndroid Build Coastguard Worker for (s = al[i].type, len = 0; algo[len]; len++) {
21*cf5a6c84SAndroid Build Coastguard Worker while (ispunct(algo[len])) len++;
22*cf5a6c84SAndroid Build Coastguard Worker if (tolower(algo[len]) != tolower(*s++)) break;
23*cf5a6c84SAndroid Build Coastguard Worker }
24*cf5a6c84SAndroid Build Coastguard Worker if (algo[len]) continue;
25*cf5a6c84SAndroid Build Coastguard Worker
26*cf5a6c84SAndroid Build Coastguard Worker len = al[i].len;
27*cf5a6c84SAndroid Build Coastguard Worker s = salt + (al[i].id ? sprintf(salt, "$%c$", '0'+al[i].id) : 0);
28*cf5a6c84SAndroid Build Coastguard Worker
29*cf5a6c84SAndroid Build Coastguard Worker // Read appropriate number of random bytes for salt
30*cf5a6c84SAndroid Build Coastguard Worker if (rand) xgetrandom(libbuf, ((len*6)+7)/8);
31*cf5a6c84SAndroid Build Coastguard Worker
32*cf5a6c84SAndroid Build Coastguard Worker // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
33*cf5a6c84SAndroid Build Coastguard Worker for (i = 0; i<len; i++) {
34*cf5a6c84SAndroid Build Coastguard Worker int bitpos = i*6, bits = bitpos/8;
35*cf5a6c84SAndroid Build Coastguard Worker
36*cf5a6c84SAndroid Build Coastguard Worker bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
37*cf5a6c84SAndroid Build Coastguard Worker bits += 46;
38*cf5a6c84SAndroid Build Coastguard Worker if (bits > 57) bits += 7;
39*cf5a6c84SAndroid Build Coastguard Worker if (bits > 90) bits += 6;
40*cf5a6c84SAndroid Build Coastguard Worker
41*cf5a6c84SAndroid Build Coastguard Worker s[i] = bits;
42*cf5a6c84SAndroid Build Coastguard Worker }
43*cf5a6c84SAndroid Build Coastguard Worker s[len] = 0;
44*cf5a6c84SAndroid Build Coastguard Worker
45*cf5a6c84SAndroid Build Coastguard Worker return s-salt;
46*cf5a6c84SAndroid Build Coastguard Worker }
47*cf5a6c84SAndroid Build Coastguard Worker
48*cf5a6c84SAndroid Build Coastguard Worker return -1;
49*cf5a6c84SAndroid Build Coastguard Worker }
50*cf5a6c84SAndroid Build Coastguard Worker
51*cf5a6c84SAndroid Build Coastguard Worker // Prompt with mesg, read password into buf, return 0 for success 1 for fail
read_password(char * buf,int buflen,char * mesg)52*cf5a6c84SAndroid Build Coastguard Worker int read_password(char *buf, int buflen, char *mesg)
53*cf5a6c84SAndroid Build Coastguard Worker {
54*cf5a6c84SAndroid Build Coastguard Worker struct termios oldtermio;
55*cf5a6c84SAndroid Build Coastguard Worker struct sigaction sa = {.sa_handler = generic_signal}, oldsa;
56*cf5a6c84SAndroid Build Coastguard Worker int i, tty = tty_fd(), ret = 1;
57*cf5a6c84SAndroid Build Coastguard Worker
58*cf5a6c84SAndroid Build Coastguard Worker // Set NOP signal handler to return from the read.
59*cf5a6c84SAndroid Build Coastguard Worker fflush(0);
60*cf5a6c84SAndroid Build Coastguard Worker sigaction(SIGINT, &sa, &oldsa);
61*cf5a6c84SAndroid Build Coastguard Worker tcflush(tty, TCIFLUSH);
62*cf5a6c84SAndroid Build Coastguard Worker xset_terminal(tty, 1, 0, &oldtermio);
63*cf5a6c84SAndroid Build Coastguard Worker dprintf(tty, "%s", mesg);
64*cf5a6c84SAndroid Build Coastguard Worker
65*cf5a6c84SAndroid Build Coastguard Worker // Loop assembling password. (Too long = fail)
66*cf5a6c84SAndroid Build Coastguard Worker for (i = 0; i<buflen-1; i++) {
67*cf5a6c84SAndroid Build Coastguard Worker // tty closed, or EOF or ctrl-D at start, or ctrl-C anywhere: fail.
68*cf5a6c84SAndroid Build Coastguard Worker if ((ret = read(tty, buf+i, 1))<0 || (!ret&&!i) || *buf==4 || buf[i]==3)
69*cf5a6c84SAndroid Build Coastguard Worker break;
70*cf5a6c84SAndroid Build Coastguard Worker // EOF or newline: return success
71*cf5a6c84SAndroid Build Coastguard Worker else if (!ret || buf[i]=='\n' || buf[i]=='\r') {
72*cf5a6c84SAndroid Build Coastguard Worker ret = 0;
73*cf5a6c84SAndroid Build Coastguard Worker break;
74*cf5a6c84SAndroid Build Coastguard Worker } else if (buf[i] == 8 || buf[i] == 127) i -= 2-!i;
75*cf5a6c84SAndroid Build Coastguard Worker }
76*cf5a6c84SAndroid Build Coastguard Worker
77*cf5a6c84SAndroid Build Coastguard Worker // Restore terminal/signal state, terminate string
78*cf5a6c84SAndroid Build Coastguard Worker tcsetattr(0, TCSANOW, &oldtermio);
79*cf5a6c84SAndroid Build Coastguard Worker sigaction(SIGINT, &oldsa, 0);
80*cf5a6c84SAndroid Build Coastguard Worker xputc('\n');
81*cf5a6c84SAndroid Build Coastguard Worker fflush(0);
82*cf5a6c84SAndroid Build Coastguard Worker buf[i*!ret] = 0;
83*cf5a6c84SAndroid Build Coastguard Worker
84*cf5a6c84SAndroid Build Coastguard Worker return ret;
85*cf5a6c84SAndroid Build Coastguard Worker }
86*cf5a6c84SAndroid Build Coastguard Worker
87*cf5a6c84SAndroid Build Coastguard Worker // Read array of colon separated strings from file with given first entry
get_userline(char * filename,char * username)88*cf5a6c84SAndroid Build Coastguard Worker char **get_userline(char *filename, char *username)
89*cf5a6c84SAndroid Build Coastguard Worker {
90*cf5a6c84SAndroid Build Coastguard Worker FILE *fp = xfopen(filename, "r");
91*cf5a6c84SAndroid Build Coastguard Worker int len = strlen(username);
92*cf5a6c84SAndroid Build Coastguard Worker char *line = 0, **data;
93*cf5a6c84SAndroid Build Coastguard Worker size_t n = 0;
94*cf5a6c84SAndroid Build Coastguard Worker
95*cf5a6c84SAndroid Build Coastguard Worker while (getline(&line, &n, fp)) {
96*cf5a6c84SAndroid Build Coastguard Worker if (!strncmp(line, username, len) && line[len]==':') {
97*cf5a6c84SAndroid Build Coastguard Worker data = xzalloc(10*sizeof(char *));
98*cf5a6c84SAndroid Build Coastguard Worker for (len = 0; len<9; len++) {
99*cf5a6c84SAndroid Build Coastguard Worker data[len] = line;
100*cf5a6c84SAndroid Build Coastguard Worker if (!(line = strchr(line, ':'))) break;
101*cf5a6c84SAndroid Build Coastguard Worker *line++ = 0;
102*cf5a6c84SAndroid Build Coastguard Worker }
103*cf5a6c84SAndroid Build Coastguard Worker return data;
104*cf5a6c84SAndroid Build Coastguard Worker }
105*cf5a6c84SAndroid Build Coastguard Worker memset(line, 0, strlen(line));
106*cf5a6c84SAndroid Build Coastguard Worker free(line);
107*cf5a6c84SAndroid Build Coastguard Worker line = 0;
108*cf5a6c84SAndroid Build Coastguard Worker n = 0;
109*cf5a6c84SAndroid Build Coastguard Worker }
110*cf5a6c84SAndroid Build Coastguard Worker
111*cf5a6c84SAndroid Build Coastguard Worker return 0;
112*cf5a6c84SAndroid Build Coastguard Worker }
113*cf5a6c84SAndroid Build Coastguard Worker
114*cf5a6c84SAndroid Build Coastguard Worker // update colon-separated text files ala /etc/{passwd,shadow,group,gshadow}
115*cf5a6c84SAndroid Build Coastguard Worker // returns 1 for success, 0 for failure
116*cf5a6c84SAndroid Build Coastguard Worker
117*cf5a6c84SAndroid Build Coastguard Worker // username = string match for first entry in line
118*cf5a6c84SAndroid Build Coastguard Worker // entry = new entry (NULL deletes matching line, contains : adds/replaces line)
119*cf5a6c84SAndroid Build Coastguard Worker // pos = if no : in "entry", which field to replace (0 is first)
120*cf5a6c84SAndroid Build Coastguard Worker // filename+ = new copy being written, filename- = backup of old version
121*cf5a6c84SAndroid Build Coastguard Worker
update_password(char * filename,char * username,char * entry,int pos)122*cf5a6c84SAndroid Build Coastguard Worker int update_password(char *filename, char *username, char *entry, int pos)
123*cf5a6c84SAndroid Build Coastguard Worker {
124*cf5a6c84SAndroid Build Coastguard Worker char *ff = xmprintf("%s-", filename), *line = 0, *start, *end, *out, *oo;
125*cf5a6c84SAndroid Build Coastguard Worker FILE *ofp;
126*cf5a6c84SAndroid Build Coastguard Worker int len = strlen(username)*!strchr(username,':'), rc = 0, ret = 0, found = 0,
127*cf5a6c84SAndroid Build Coastguard Worker nfd;
128*cf5a6c84SAndroid Build Coastguard Worker struct flock lock = {.l_type = F_WRLCK};
129*cf5a6c84SAndroid Build Coastguard Worker struct stat st;
130*cf5a6c84SAndroid Build Coastguard Worker long long ll = 0;
131*cf5a6c84SAndroid Build Coastguard Worker
132*cf5a6c84SAndroid Build Coastguard Worker // Open old filename ("r" won't let us lock) and get blocking lock
133*cf5a6c84SAndroid Build Coastguard Worker if (!(ofp = fopen(filename, "w+")) || 0>fcntl(fileno(ofp), F_SETLK, &lock)\
134*cf5a6c84SAndroid Build Coastguard Worker || fstat(fileno(ofp), &st))
135*cf5a6c84SAndroid Build Coastguard Worker {
136*cf5a6c84SAndroid Build Coastguard Worker perror_msg_raw(filename);
137*cf5a6c84SAndroid Build Coastguard Worker goto free_storage;
138*cf5a6c84SAndroid Build Coastguard Worker }
139*cf5a6c84SAndroid Build Coastguard Worker
140*cf5a6c84SAndroid Build Coastguard Worker // Delete old backup, link new backup. (Failure here isn't fatal.)
141*cf5a6c84SAndroid Build Coastguard Worker unlink(ff);
142*cf5a6c84SAndroid Build Coastguard Worker if (0>link(filename, ff)) perror_msg_raw(ff);
143*cf5a6c84SAndroid Build Coastguard Worker
144*cf5a6c84SAndroid Build Coastguard Worker // Open new file to copy entries to
145*cf5a6c84SAndroid Build Coastguard Worker ff[strlen(ff)-1] = '+';
146*cf5a6c84SAndroid Build Coastguard Worker if (-1 == (nfd = xcreate(ff, O_CREAT|O_EXCL|O_WRONLY, st.st_mode))) {
147*cf5a6c84SAndroid Build Coastguard Worker perror_msg_raw(ff);
148*cf5a6c84SAndroid Build Coastguard Worker goto free_storage;
149*cf5a6c84SAndroid Build Coastguard Worker }
150*cf5a6c84SAndroid Build Coastguard Worker
151*cf5a6c84SAndroid Build Coastguard Worker // Loop through lines
152*cf5a6c84SAndroid Build Coastguard Worker for (; getline(&line, (void *)&ll, ofp)!=-1; memset(line, 0, strlen(line))) {
153*cf5a6c84SAndroid Build Coastguard Worker // find matching line
154*cf5a6c84SAndroid Build Coastguard Worker oo = 0;
155*cf5a6c84SAndroid Build Coastguard Worker start = end = chomp(line);
156*cf5a6c84SAndroid Build Coastguard Worker if (strncmp(line, username, len) || !(line[len] && line[len]!=':'))
157*cf5a6c84SAndroid Build Coastguard Worker out = line;
158*cf5a6c84SAndroid Build Coastguard Worker else {
159*cf5a6c84SAndroid Build Coastguard Worker found++;
160*cf5a6c84SAndroid Build Coastguard Worker
161*cf5a6c84SAndroid Build Coastguard Worker // Delete or replace whole line?
162*cf5a6c84SAndroid Build Coastguard Worker if (!entry) out = 0;
163*cf5a6c84SAndroid Build Coastguard Worker else if (strchr(entry, ':')) out = entry;
164*cf5a6c84SAndroid Build Coastguard Worker
165*cf5a6c84SAndroid Build Coastguard Worker // Replace entry at pos
166*cf5a6c84SAndroid Build Coastguard Worker else {
167*cf5a6c84SAndroid Build Coastguard Worker for (;; pos--, start = ++end) {
168*cf5a6c84SAndroid Build Coastguard Worker while (*end && *end != ':') end++;
169*cf5a6c84SAndroid Build Coastguard Worker if (!pos || !*end) break;
170*cf5a6c84SAndroid Build Coastguard Worker }
171*cf5a6c84SAndroid Build Coastguard Worker if (pos>=0) out = line;
172*cf5a6c84SAndroid Build Coastguard Worker else oo = out = xmprintf("%*s%s%s\n", (int)(start-line),line,entry,end);
173*cf5a6c84SAndroid Build Coastguard Worker }
174*cf5a6c84SAndroid Build Coastguard Worker }
175*cf5a6c84SAndroid Build Coastguard Worker if (out) {
176*cf5a6c84SAndroid Build Coastguard Worker rc = dprintf(nfd, "%s\n", out);
177*cf5a6c84SAndroid Build Coastguard Worker free(out);
178*cf5a6c84SAndroid Build Coastguard Worker if (rc<0) {
179*cf5a6c84SAndroid Build Coastguard Worker perror_msg_raw(ff);
180*cf5a6c84SAndroid Build Coastguard Worker goto free_storage;
181*cf5a6c84SAndroid Build Coastguard Worker }
182*cf5a6c84SAndroid Build Coastguard Worker free(oo);
183*cf5a6c84SAndroid Build Coastguard Worker }
184*cf5a6c84SAndroid Build Coastguard Worker }
185*cf5a6c84SAndroid Build Coastguard Worker free(line);
186*cf5a6c84SAndroid Build Coastguard Worker if (!found && entry && strchr(entry, ':')) dprintf(nfd, "%s\n", entry);
187*cf5a6c84SAndroid Build Coastguard Worker fsync(nfd);
188*cf5a6c84SAndroid Build Coastguard Worker close(nfd); // automatically unlocks
189*cf5a6c84SAndroid Build Coastguard Worker
190*cf5a6c84SAndroid Build Coastguard Worker if (!found || rename(ff, filename)) {
191*cf5a6c84SAndroid Build Coastguard Worker if (found) perror_msg("%s -> %s", ff, filename);
192*cf5a6c84SAndroid Build Coastguard Worker else if (entry) error_msg("No %s in %s", username, filename);
193*cf5a6c84SAndroid Build Coastguard Worker } else ret = 1, *ff = 0;
194*cf5a6c84SAndroid Build Coastguard Worker
195*cf5a6c84SAndroid Build Coastguard Worker free_storage:
196*cf5a6c84SAndroid Build Coastguard Worker if (ofp) fclose(ofp);
197*cf5a6c84SAndroid Build Coastguard Worker if (ff && *ff) unlink(ff);
198*cf5a6c84SAndroid Build Coastguard Worker free(ff);
199*cf5a6c84SAndroid Build Coastguard Worker
200*cf5a6c84SAndroid Build Coastguard Worker return ret;
201*cf5a6c84SAndroid Build Coastguard Worker }
202