1 /* password.c - password read/update helper functions. 2 * 3 * Copyright 2012 Ashwini Kumar <ak.ashwini (at) gmail.com> 4 */ 5 6 #include "toys.h" 7 #include <time.h> 8 9 // generate appropriate random salt string for given encryption algorithm. 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 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 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 */ 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