1 /* password.c - password read/update helper functions.
2 *
3 * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
4 */
5
6 #include "toys.h"
7 #include <time.h>
8
9 // generate appropriate random salt string for given encryption algorithm.
get_salt(char * salt,char * algo)10 int get_salt(char *salt, char *algo)
11 {
12 struct {
13 char *type, id, len;
14 } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}};
15 int i;
16
17 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;
21
22 if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id);
23
24 // Read appropriate number of random bytes for salt
25 i = xopen("/dev/urandom", O_RDONLY);
26 xreadall(i, libbuf, ((len*6)+7)/8);
27 close(i);
28
29 // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
30 for (i=0; i<len; i++) {
31 int bitpos = i*6, bits = bitpos/8;
32
33 bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
34 bits += 46;
35 if (bits > 57) bits += 7;
36 if (bits > 90) bits += 6;
37
38 s[i] = bits;
39 }
40 salt[len] = 0;
41
42 return s-salt;
43 }
44 }
45
46 return -1;
47 }
48
49 // Prompt with mesg, read password into buf, return 0 for success 1 for fail
read_password(char * buf,int buflen,char * mesg)50 int read_password(char *buf, int buflen, char *mesg)
51 {
52 struct termios oldtermio;
53 struct sigaction sa, oldsa;
54 int i, ret = 1;
55
56 // NOP signal handler to return from the read
57 memset(&sa, 0, sizeof(sa));
58 sa.sa_handler = generic_signal;
59 sigaction(SIGINT, &sa, &oldsa);
60
61 tcflush(0, TCIFLUSH);
62 set_terminal(0, 1, &oldtermio);
63
64 xprintf("%s", mesg);
65
66 for (i=0; i < buflen-1; i++) {
67 if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) {
68 i = 0;
69 ret = 1;
70
71 break;
72 } else if (!ret || buf[i] == '\n' || buf[i] == '\r') {
73 ret = 0;
74
75 break;
76 } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1;
77 }
78
79 // Restore terminal/signal state, terminate string
80 sigaction(SIGINT, &oldsa, NULL);
81 tcsetattr(0, TCSANOW, &oldtermio);
82 buf[i] = 0;
83 xputc('\n');
84
85 return ret;
86 }
87
get_nextcolon(char * line,int cnt)88 static char *get_nextcolon(char *line, int cnt)
89 {
90 while (cnt--) {
91 if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
92 line++; //jump past the colon
93 }
94 return line;
95 }
96
97 /*update_password is used by multiple utilities to update /etc/passwd,
98 * /etc/shadow, /etc/group and /etc/gshadow files,
99 * which are used as user, group databeses
100 * entry can be
101 * 1. encrypted password, when updating user password.
102 * 2. complete entry for user details, when creating new user
103 * 3. group members comma',' separated list, when adding user to group
104 * 4. complete entry for group details, when creating new group
105 * 5. entry = NULL, delete the named entry user/group
106 */
update_password(char * filename,char * username,char * entry)107 int update_password(char *filename, char* username, char* entry)
108 {
109 char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
110 *sfx = NULL, *line = NULL;
111 FILE *exfp, *newfp;
112 int ret = -1, found = 0;
113 struct flock lock;
114
115 shadow = strstr(filename, "shadow");
116 filenamesfx = xmprintf("%s+", filename);
117 sfx = strchr(filenamesfx, '+');
118
119 exfp = fopen(filename, "r+");
120 if (!exfp) {
121 perror_msg("Couldn't open file %s",filename);
122 goto free_storage;
123 }
124
125 *sfx = '-';
126 unlink(filenamesfx);
127 ret = link(filename, filenamesfx);
128 if (ret < 0) error_msg("can't create backup file");
129
130 *sfx = '+';
131 lock.l_type = F_WRLCK;
132 lock.l_whence = SEEK_SET;
133 lock.l_start = 0;
134 lock.l_len = 0;
135
136 ret = fcntl(fileno(exfp), F_SETLK, &lock);
137 if (ret < 0) perror_msg("Couldn't lock file %s",filename);
138
139 lock.l_type = F_UNLCK; //unlocking at a later stage
140
141 newfp = fopen(filenamesfx, "w+");
142 if (!newfp) {
143 error_msg("couldn't open file for writing");
144 ret = -1;
145 fclose(exfp);
146 goto free_storage;
147 }
148
149 ret = 0;
150 namesfx = xmprintf("%s:",username);
151 while ((line = get_line(fileno(exfp))) != NULL)
152 {
153 if (strncmp(line, namesfx, strlen(namesfx)))
154 fprintf(newfp, "%s\n", line);
155 else if (entry) {
156 char *current_ptr = NULL;
157
158 found = 1;
159 if (!strcmp(toys.which->name, "passwd")) {
160 fprintf(newfp, "%s%s:",namesfx, entry);
161 current_ptr = get_nextcolon(line, 2); //past passwd
162 if (shadow) {
163 fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
164 current_ptr = get_nextcolon(current_ptr, 1);
165 fprintf(newfp, "%s\n",current_ptr);
166 } else fprintf(newfp, "%s\n",current_ptr);
167 } else if (!strcmp(toys.which->name, "groupadd") ||
168 !strcmp(toys.which->name, "addgroup") ||
169 !strcmp(toys.which->name, "delgroup") ||
170 !strcmp(toys.which->name, "groupdel")){
171 current_ptr = get_nextcolon(line, 3); //past gid/admin list
172 *current_ptr = '\0';
173 fprintf(newfp, "%s", line);
174 fprintf(newfp, "%s\n", entry);
175 }
176 }
177 free(line);
178 }
179 free(namesfx);
180 if (!found && entry) fprintf(newfp, "%s\n", entry);
181 fcntl(fileno(exfp), F_SETLK, &lock);
182 fclose(exfp);
183
184 errno = 0;
185 fflush(newfp);
186 fsync(fileno(newfp));
187 fclose(newfp);
188 rename(filenamesfx, filename);
189 if (errno) {
190 perror_msg("File Writing/Saving failed: ");
191 unlink(filenamesfx);
192 ret = -1;
193 }
194
195 free_storage:
196 free(filenamesfx);
197 return ret;
198 }
199