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