xref: /aosp_15_r20/external/toybox/lib/password.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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