1 /* password.c - password read/update helper functions. 2 * 3 * Copyright 2012 Ashwini Kumar <ak.ashwini (at) gmail.com> 4 * 5 * TODO: cleanup 6 */ 7 8 #include "toys.h" 9 #include <time.h> 10 11 // generate ID prefix and random salt for given encryption algorithm. 12 int 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 xgetrandom(libbuf, ((len*6)+7)/8, 0); 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. Use sigaction() instead 57 // of xsignal() because we want to restore the old handler afterwards. 58 memset(&sa, 0, sizeof(sa)); 59 sa.sa_handler = generic_signal; 60 sigaction(SIGINT, &sa, &oldsa); 61 62 tcflush(0, TCIFLUSH); 63 xset_terminal(0, 1, 0, &oldtermio); 64 65 xprintf("%s", mesg); 66 67 for (i=0; i < buflen-1; i++) { 68 if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) { 69 i = 0; 70 ret = 1; 71 72 break; 73 } else if (!ret || buf[i] == '\n' || buf[i] == '\r') { 74 ret = 0; 75 76 break; 77 } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1; 78 } 79 80 // Restore terminal/signal state, terminate string 81 sigaction(SIGINT, &oldsa, NULL); 82 tcsetattr(0, TCSANOW, &oldtermio); 83 buf[i] = 0; 84 xputc('\n'); 85 86 return ret; 87 } 88 89 static char *get_nextcolon(char *line, int cnt) 90 { 91 while (cnt--) { 92 if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n"); 93 line++; //jump past the colon 94 } 95 return line; 96 } 97 98 /*update_password is used by multiple utilities to update /etc/passwd, 99 * /etc/shadow, /etc/group and /etc/gshadow files, 100 * which are used as user, group databeses 101 * entry can be 102 * 1. encrypted password, when updating user password. 103 * 2. complete entry for user details, when creating new user 104 * 3. group members comma',' separated list, when adding user to group 105 * 4. complete entry for group details, when creating new group 106 * 5. entry = NULL, delete the named entry user/group 107 */ 108 int update_password(char *filename, char* username, char* entry) 109 { 110 char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL, 111 *sfx = NULL, *line = NULL; 112 FILE *exfp, *newfp; 113 int ret = -1, found = 0; 114 struct flock lock; 115 116 shadow = strstr(filename, "shadow"); 117 filenamesfx = xmprintf("%s+", filename); 118 sfx = strchr(filenamesfx, '+'); 119 120 exfp = fopen(filename, "r+"); 121 if (!exfp) { 122 perror_msg("Couldn't open file %s",filename); 123 goto free_storage; 124 } 125 126 *sfx = '-'; 127 unlink(filenamesfx); 128 ret = link(filename, filenamesfx); 129 if (ret < 0) error_msg("can't create backup file"); 130 131 *sfx = '+'; 132 lock.l_type = F_WRLCK; 133 lock.l_whence = SEEK_SET; 134 lock.l_start = 0; 135 lock.l_len = 0; 136 137 ret = fcntl(fileno(exfp), F_SETLK, &lock); 138 if (ret < 0) perror_msg("Couldn't lock file %s",filename); 139 140 lock.l_type = F_UNLCK; //unlocking at a later stage 141 142 newfp = fopen(filenamesfx, "w+"); 143 if (!newfp) { 144 error_msg("couldn't open file for writing"); 145 ret = -1; 146 fclose(exfp); 147 goto free_storage; 148 } 149 150 ret = 0; 151 namesfx = xmprintf("%s:",username); 152 while ((line = get_line(fileno(exfp))) != NULL) 153 { 154 if (strncmp(line, namesfx, strlen(namesfx))) 155 fprintf(newfp, "%s\n", line); 156 else if (entry) { 157 char *current_ptr = NULL; 158 159 found = 1; 160 if (!strcmp(toys.which->name, "passwd")) { 161 fprintf(newfp, "%s%s:",namesfx, entry); 162 current_ptr = get_nextcolon(line, 2); //past passwd 163 if (shadow) { 164 fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60)); 165 current_ptr = get_nextcolon(current_ptr, 1); 166 fprintf(newfp, "%s\n",current_ptr); 167 } else fprintf(newfp, "%s\n",current_ptr); 168 } else if (!strcmp(toys.which->name, "groupadd") || 169 !strcmp(toys.which->name, "addgroup") || 170 !strcmp(toys.which->name, "delgroup") || 171 !strcmp(toys.which->name, "groupdel")){ 172 current_ptr = get_nextcolon(line, 3); //past gid/admin list 173 *current_ptr = '\0'; 174 fprintf(newfp, "%s", line); 175 fprintf(newfp, "%s\n", entry); 176 } 177 } 178 free(line); 179 } 180 free(namesfx); 181 if (!found && entry) fprintf(newfp, "%s\n", entry); 182 fcntl(fileno(exfp), F_SETLK, &lock); 183 fclose(exfp); 184 185 errno = 0; 186 fflush(newfp); 187 fsync(fileno(newfp)); 188 fclose(newfp); 189 rename(filenamesfx, filename); 190 if (errno) { 191 perror_msg("File Writing/Saving failed: "); 192 unlink(filenamesfx); 193 ret = -1; 194 } 195 196 free_storage: 197 free(filenamesfx); 198 return ret; 199 } 200