blob: 2578dfe0ba598d7bc31856075280127439e2b89c [file] [log] [blame]
Rob Landleyd0f79352013-10-16 19:30:17 -05001/* password.c - password read/update helper functions.
Rob Landley2c917f52012-07-17 08:54:47 -05002 *
3 * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
4 */
5
6#include "toys.h"
7#include <time.h>
8
Rob Landley6d15f0d2014-06-25 22:54:59 -05009// generate appropriate random salt string for given encryption algorithm.
Rob Landleyd0f79352013-10-16 19:30:17 -050010int get_salt(char *salt, char *algo)
11{
Rob Landley6d15f0d2014-06-25 22:54:59 -050012 struct {
13 char *type, id, len;
14 } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
15 int i;
Rob Landleyd0f79352013-10-16 19:30:17 -050016
Rob Landley6d15f0d2014-06-25 22:54:59 -050017 for (i = 0; i < ARRAY_LEN(al); i++) {
18 if (!strcmp(algo, al[i].type)) {
19 int len = al[i].len;
20 char *s = salt;
Rob Landleyd0f79352013-10-16 19:30:17 -050021
Rob Landley6d15f0d2014-06-25 22:54:59 -050022 if (al[i].id) {
23 *s++ = '$';
24 *s++ = '0'+al[i].id;
Rob Landleyc0e5ff32014-06-28 20:02:01 -050025 *s++ = '$';
Rob Landley6d15f0d2014-06-25 22:54:59 -050026 }
Rob Landleyd0f79352013-10-16 19:30:17 -050027
Rob Landley6d15f0d2014-06-25 22:54:59 -050028 // Read appropriate number of random bytes for salt
29 i = xopen("/dev/urandom", O_RDONLY);
30 xreadall(i, libbuf, ((len*6)+7)/8);
31 close(i);
Rob Landley34037422013-10-16 20:01:46 -050032
Rob Landley6d15f0d2014-06-25 22:54:59 -050033 // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
34 for (i=0; i<len; i++) {
35 int bitpos = i*6, bits = bitpos/8;
Rob Landley34037422013-10-16 20:01:46 -050036
Rob Landley6d15f0d2014-06-25 22:54:59 -050037 bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
38 bits += 46;
39 if (bits > 57) bits += 7;
40 if (bits > 90) bits += 6;
Rob Landley34037422013-10-16 20:01:46 -050041
Rob Landley6d15f0d2014-06-25 22:54:59 -050042 s[i] = bits;
43 }
44 salt[len] = 0;
45
46 return s-salt;
47 }
Rob Landley34037422013-10-16 20:01:46 -050048 }
Rob Landleyd0f79352013-10-16 19:30:17 -050049
Rob Landley6d15f0d2014-06-25 22:54:59 -050050 return -1;
Rob Landleyd0f79352013-10-16 19:30:17 -050051}
52
Rob Landleyc0e5ff32014-06-28 20:02:01 -050053// Reset terminal to known state, returning old state if old != NULL.
54int set_terminal(int fd, int raw, struct termios *old)
Rob Landleyd0f79352013-10-16 19:30:17 -050055{
Rob Landleyc0e5ff32014-06-28 20:02:01 -050056 struct termios termio;
57
58 if (!tcgetattr(fd, &termio) && old) *old = termio;
59
60 // the following are the bits set for an xterm. Linux text mode TTYs by
61 // default add two additional bits that only matter for serial processing
62 // (turn serial line break into an interrupt, and XON/XOFF flow control)
63
64 // Any key unblocks output, swap CR and NL on input
65 termio.c_iflag = IXANY|ICRNL|INLCR;
66 if (toys.which->flags & TOYFLAG_LOCALE) termio.c_iflag |= IUTF8;
67
68 // Output appends CR to NL, does magic undocumented postprocessing
69 termio.c_oflag = ONLCR|OPOST;
70
71 // Leave serial port speed alone
72 // termio.c_cflag = C_READ|CS8|EXTB;
73
74 // Generate signals, input entire line at once, echo output
75 // erase, line kill, escape control characters with ^
76 // erase line char at a time
77 // "extended" behavior: ctrl-V quotes next char, ctrl-R reprints unread chars,
78 // ctrl-W erases word
79 termio.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN;
80
81 if (raw) cfmakeraw(&termio);
82
83 return tcsetattr(fd, TCSANOW, &termio);
Rob Landleyd0f79352013-10-16 19:30:17 -050084}
85
Rob Landleyc0e5ff32014-06-28 20:02:01 -050086// Prompt with mesg, read password into buf, return 0 for success 1 for fail
87int read_password(char *buf, int buflen, char *mesg)
Rob Landley7aa651a2012-11-13 17:14:08 -060088{
Rob Landleyc0e5ff32014-06-28 20:02:01 -050089 struct termios oldtermio;
Rob Landleyd0f79352013-10-16 19:30:17 -050090 struct sigaction sa, oldsa;
Rob Landleyc0e5ff32014-06-28 20:02:01 -050091 int i, ret = 1;
Rob Landleyd0f79352013-10-16 19:30:17 -050092
Rob Landleyc0e5ff32014-06-28 20:02:01 -050093 // NOP signal handler to return from the read
Rob Landleyd0f79352013-10-16 19:30:17 -050094 memset(&sa, 0, sizeof(sa));
Rob Landleyc0e5ff32014-06-28 20:02:01 -050095 sa.sa_handler = generic_signal;
Rob Landleyd0f79352013-10-16 19:30:17 -050096 sigaction(SIGINT, &sa, &oldsa);
97
Rob Landleyc0e5ff32014-06-28 20:02:01 -050098 tcflush(0, TCIFLUSH);
99 set_terminal(0, 1, &oldtermio);
Rob Landley2c917f52012-07-17 08:54:47 -0500100
Rob Landleyc0e5ff32014-06-28 20:02:01 -0500101 xprintf("%s", mesg);
Rob Landley2c917f52012-07-17 08:54:47 -0500102
Rob Landleyc0e5ff32014-06-28 20:02:01 -0500103 for (i=0; i < buflen-1; i++) {
104 if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) {
105 i = 0;
106 ret = 1;
107
Rob Landley7aa651a2012-11-13 17:14:08 -0600108 break;
Rob Landleyc0e5ff32014-06-28 20:02:01 -0500109 } else if (!ret || buf[i] == '\n' || buf[i] == '\r') {
110 ret = 0;
111
112 break;
113 } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1;
Rob Landley7aa651a2012-11-13 17:14:08 -0600114 }
Rob Landleyc0e5ff32014-06-28 20:02:01 -0500115
116 // Restore terminal/signal state, terminate string
Rob Landleyd0f79352013-10-16 19:30:17 -0500117 sigaction(SIGINT, &oldsa, NULL);
Rob Landley7aa651a2012-11-13 17:14:08 -0600118 tcsetattr(0, TCSANOW, &oldtermio);
Rob Landleyc0e5ff32014-06-28 20:02:01 -0500119 buf[i] = 0;
120 xputc('\n');
121
122 return ret;
Rob Landley2c917f52012-07-17 08:54:47 -0500123}
124
Rob Landleyd0f79352013-10-16 19:30:17 -0500125static char *get_nextcolon(char *line, int cnt)
Rob Landley2c917f52012-07-17 08:54:47 -0500126{
Rob Landleyd0f79352013-10-16 19:30:17 -0500127 while (cnt--) {
128 if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
129 line++; //jump past the colon
130 }
131 return line;
Rob Landley2c917f52012-07-17 08:54:47 -0500132}
133
Rob Landleyd0f79352013-10-16 19:30:17 -0500134/*update_password is used by multiple utilities to update /etc/passwd,
135 * /etc/shadow, /etc/group and /etc/gshadow files,
136 * which are used as user, group databeses
137 * entry can be
138 * 1. encrypted password, when updating user password.
139 * 2. complete entry for user details, when creating new user
140 * 3. group members comma',' separated list, when adding user to group
141 * 4. complete entry for group details, when creating new group
Ashwini Sharma656d5042013-12-23 07:23:28 -0600142 * 5. entry = NULL, delete the named entry user/group
Rob Landleyd0f79352013-10-16 19:30:17 -0500143 */
144int update_password(char *filename, char* username, char* entry)
Rob Landley2c917f52012-07-17 08:54:47 -0500145{
Rob Landleyd0f79352013-10-16 19:30:17 -0500146 char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
147 *sfx = NULL, *line = NULL;
Rob Landley7aa651a2012-11-13 17:14:08 -0600148 FILE *exfp, *newfp;
Rob Landleyd0f79352013-10-16 19:30:17 -0500149 int ret = -1, found = 0;
Rob Landley7aa651a2012-11-13 17:14:08 -0600150 struct flock lock;
Rob Landley2c917f52012-07-17 08:54:47 -0500151
Rob Landley7aa651a2012-11-13 17:14:08 -0600152 shadow = strstr(filename, "shadow");
Rob Landley59d85e22014-01-16 09:26:50 -0600153 filenamesfx = xmprintf("%s+", filename);
Rob Landley7aa651a2012-11-13 17:14:08 -0600154 sfx = strchr(filenamesfx, '+');
Rob Landley2c917f52012-07-17 08:54:47 -0500155
Rob Landley7aa651a2012-11-13 17:14:08 -0600156 exfp = fopen(filename, "r+");
Rob Landleyd0f79352013-10-16 19:30:17 -0500157 if (!exfp) {
Rob Landley7aa651a2012-11-13 17:14:08 -0600158 perror_msg("Couldn't open file %s",filename);
159 goto free_storage;
160 }
Rob Landley2c917f52012-07-17 08:54:47 -0500161
Rob Landley7aa651a2012-11-13 17:14:08 -0600162 *sfx = '-';
163 ret = unlink(filenamesfx);
164 ret = link(filename, filenamesfx);
Rob Landleyd0f79352013-10-16 19:30:17 -0500165 if (ret < 0) error_msg("can't create backup file");
Rob Landley2c917f52012-07-17 08:54:47 -0500166
Rob Landley7aa651a2012-11-13 17:14:08 -0600167 *sfx = '+';
168 lock.l_type = F_WRLCK;
169 lock.l_whence = SEEK_SET;
170 lock.l_start = 0;
171 lock.l_len = 0;
Rob Landley2c917f52012-07-17 08:54:47 -0500172
Rob Landley7aa651a2012-11-13 17:14:08 -0600173 ret = fcntl(fileno(exfp), F_SETLK, &lock);
Rob Landleyd0f79352013-10-16 19:30:17 -0500174 if (ret < 0) perror_msg("Couldn't lock file %s",filename);
Rob Landley2c917f52012-07-17 08:54:47 -0500175
Rob Landley7aa651a2012-11-13 17:14:08 -0600176 lock.l_type = F_UNLCK; //unlocking at a later stage
Rob Landley2c917f52012-07-17 08:54:47 -0500177
Rob Landley7aa651a2012-11-13 17:14:08 -0600178 newfp = fopen(filenamesfx, "w+");
Rob Landleyd0f79352013-10-16 19:30:17 -0500179 if (!newfp) {
Rob Landley7aa651a2012-11-13 17:14:08 -0600180 error_msg("couldn't open file for writing");
181 ret = -1;
Rob Landley2c917f52012-07-17 08:54:47 -0500182 fclose(exfp);
Rob Landley7aa651a2012-11-13 17:14:08 -0600183 goto free_storage;
184 }
185
186 ret = 0;
Rob Landley59d85e22014-01-16 09:26:50 -0600187 namesfx = xmprintf("%s:",username);
Rob Landleyd0f79352013-10-16 19:30:17 -0500188 while ((line = get_line(fileno(exfp))) != NULL)
Rob Landley7aa651a2012-11-13 17:14:08 -0600189 {
Rob Landleyd0f79352013-10-16 19:30:17 -0500190 if (strncmp(line, namesfx, strlen(namesfx)))
Rob Landley7aa651a2012-11-13 17:14:08 -0600191 fprintf(newfp, "%s\n", line);
Ashwini Sharma656d5042013-12-23 07:23:28 -0600192 else if (entry) {
Rob Landley7aa651a2012-11-13 17:14:08 -0600193 char *current_ptr = NULL;
Rob Landley7aa651a2012-11-13 17:14:08 -0600194
Rob Landleyd0f79352013-10-16 19:30:17 -0500195 found = 1;
196 if (!strcmp(toys.which->name, "passwd")) {
197 fprintf(newfp, "%s%s:",namesfx, entry);
198 current_ptr = get_nextcolon(line, 2); //past passwd
199 if (shadow) {
200 fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
201 current_ptr = get_nextcolon(current_ptr, 1);
202 fprintf(newfp, "%s\n",current_ptr);
203 } else fprintf(newfp, "%s\n",current_ptr);
204 } else if (!strcmp(toys.which->name, "groupadd") ||
Ashwini Sharma656d5042013-12-23 07:23:28 -0600205 !strcmp(toys.which->name, "addgroup") ||
206 !strcmp(toys.which->name, "delgroup") ||
207 !strcmp(toys.which->name, "groupdel")){
Rob Landleyd0f79352013-10-16 19:30:17 -0500208 current_ptr = get_nextcolon(line, 3); //past gid/admin list
209 *current_ptr = '\0';
210 fprintf(newfp, "%s", line);
211 fprintf(newfp, "%s\n", entry);
212 }
213 }
Rob Landley7aa651a2012-11-13 17:14:08 -0600214 free(line);
215 }
216 free(namesfx);
Ashwini Sharma656d5042013-12-23 07:23:28 -0600217 if (!found && entry) fprintf(newfp, "%s\n", entry);
Rob Landley7aa651a2012-11-13 17:14:08 -0600218 fcntl(fileno(exfp), F_SETLK, &lock);
219 fclose(exfp);
220
221 errno = 0;
222 fflush(newfp);
223 fsync(fileno(newfp));
224 fclose(newfp);
225 rename(filenamesfx, filename);
Rob Landleyd0f79352013-10-16 19:30:17 -0500226 if (errno) {
Rob Landley7aa651a2012-11-13 17:14:08 -0600227 perror_msg("File Writing/Saving failed: ");
228 unlink(filenamesfx);
229 ret = -1;
230 }
Rob Landley2c917f52012-07-17 08:54:47 -0500231
232free_storage:
Rob Landley7aa651a2012-11-13 17:14:08 -0600233 free(filenamesfx);
234 return ret;
235}
Rob Landleyd0f79352013-10-16 19:30:17 -0500236
237void is_valid_username(const char *name)
238{
239 regex_t rp;
240 regmatch_t rm[1];
241 int eval;
242 char *regex = "^[_.A-Za-z0-9][-_.A-Za-z0-9]*"; //User name REGEX
243
244 xregcomp(&rp, regex, REG_NEWLINE);
245
246 /* compare string against pattern -- remember that patterns
247 are anchored to the beginning of the line */
248 eval = regexec(&rp, name, 1, rm, 0);
249 regfree(&rp);
250 if (!eval && !rm[0].rm_so) {
251 int len = strlen(name);
252 if ((rm[0].rm_eo == len) ||
253 (rm[0].rm_eo == len - 1 && name[len - 1] == '$')) {
254 if (len >= LOGIN_NAME_MAX) error_exit("name is too long");
255 else return;
256 }
257 }
258 error_exit("'%s', not valid %sname",name,
259 (((toys.which->name[3] == 'g') ||
260 (toys.which->name[0] == 'g'))? "group" : "user"));
261}