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