10f66f451Sopenharmony_ci/* password.c - password read/update helper functions. 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * TODO: cleanup 60f66f451Sopenharmony_ci */ 70f66f451Sopenharmony_ci 80f66f451Sopenharmony_ci#include "toys.h" 90f66f451Sopenharmony_ci#include <time.h> 100f66f451Sopenharmony_ci 110f66f451Sopenharmony_ci// generate ID prefix and random salt for given encryption algorithm. 120f66f451Sopenharmony_ciint get_salt(char *salt, char *algo) 130f66f451Sopenharmony_ci{ 140f66f451Sopenharmony_ci struct { 150f66f451Sopenharmony_ci char *type, id, len; 160f66f451Sopenharmony_ci } al[] = {{"des", 0, 2}, {"md5", 1, 8}, {"sha256", 5, 16}, {"sha512", 6, 16}}; 170f66f451Sopenharmony_ci int i; 180f66f451Sopenharmony_ci 190f66f451Sopenharmony_ci for (i = 0; i < ARRAY_LEN(al); i++) { 200f66f451Sopenharmony_ci if (!strcmp(algo, al[i].type)) { 210f66f451Sopenharmony_ci int len = al[i].len; 220f66f451Sopenharmony_ci char *s = salt; 230f66f451Sopenharmony_ci 240f66f451Sopenharmony_ci if (al[i].id) s += sprintf(s, "$%c$", '0'+al[i].id); 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_ci // Read appropriate number of random bytes for salt 270f66f451Sopenharmony_ci xgetrandom(libbuf, ((len*6)+7)/8, 0); 280f66f451Sopenharmony_ci 290f66f451Sopenharmony_ci // Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z 300f66f451Sopenharmony_ci for (i=0; i<len; i++) { 310f66f451Sopenharmony_ci int bitpos = i*6, bits = bitpos/8; 320f66f451Sopenharmony_ci 330f66f451Sopenharmony_ci bits = ((libbuf[i]+(libbuf[i+1]<<8)) >> (bitpos&7)) & 0x3f; 340f66f451Sopenharmony_ci bits += 46; 350f66f451Sopenharmony_ci if (bits > 57) bits += 7; 360f66f451Sopenharmony_ci if (bits > 90) bits += 6; 370f66f451Sopenharmony_ci 380f66f451Sopenharmony_ci s[i] = bits; 390f66f451Sopenharmony_ci } 400f66f451Sopenharmony_ci salt[len] = 0; 410f66f451Sopenharmony_ci 420f66f451Sopenharmony_ci return s-salt; 430f66f451Sopenharmony_ci } 440f66f451Sopenharmony_ci } 450f66f451Sopenharmony_ci 460f66f451Sopenharmony_ci return -1; 470f66f451Sopenharmony_ci} 480f66f451Sopenharmony_ci 490f66f451Sopenharmony_ci// Prompt with mesg, read password into buf, return 0 for success 1 for fail 500f66f451Sopenharmony_ciint read_password(char *buf, int buflen, char *mesg) 510f66f451Sopenharmony_ci{ 520f66f451Sopenharmony_ci struct termios oldtermio; 530f66f451Sopenharmony_ci struct sigaction sa, oldsa; 540f66f451Sopenharmony_ci int i, ret = 1; 550f66f451Sopenharmony_ci 560f66f451Sopenharmony_ci // NOP signal handler to return from the read. Use sigaction() instead 570f66f451Sopenharmony_ci // of xsignal() because we want to restore the old handler afterwards. 580f66f451Sopenharmony_ci memset(&sa, 0, sizeof(sa)); 590f66f451Sopenharmony_ci sa.sa_handler = generic_signal; 600f66f451Sopenharmony_ci sigaction(SIGINT, &sa, &oldsa); 610f66f451Sopenharmony_ci 620f66f451Sopenharmony_ci tcflush(0, TCIFLUSH); 630f66f451Sopenharmony_ci xset_terminal(0, 1, 0, &oldtermio); 640f66f451Sopenharmony_ci 650f66f451Sopenharmony_ci xprintf("%s", mesg); 660f66f451Sopenharmony_ci 670f66f451Sopenharmony_ci for (i=0; i < buflen-1; i++) { 680f66f451Sopenharmony_ci if ((ret = read(0, buf+i, 1)) < 0 || (!ret && !i)) { 690f66f451Sopenharmony_ci i = 0; 700f66f451Sopenharmony_ci ret = 1; 710f66f451Sopenharmony_ci 720f66f451Sopenharmony_ci break; 730f66f451Sopenharmony_ci } else if (!ret || buf[i] == '\n' || buf[i] == '\r') { 740f66f451Sopenharmony_ci ret = 0; 750f66f451Sopenharmony_ci 760f66f451Sopenharmony_ci break; 770f66f451Sopenharmony_ci } else if (buf[i] == 8 || buf[i] == 127) i -= i ? 2 : 1; 780f66f451Sopenharmony_ci } 790f66f451Sopenharmony_ci 800f66f451Sopenharmony_ci // Restore terminal/signal state, terminate string 810f66f451Sopenharmony_ci sigaction(SIGINT, &oldsa, NULL); 820f66f451Sopenharmony_ci tcsetattr(0, TCSANOW, &oldtermio); 830f66f451Sopenharmony_ci buf[i] = 0; 840f66f451Sopenharmony_ci xputc('\n'); 850f66f451Sopenharmony_ci 860f66f451Sopenharmony_ci return ret; 870f66f451Sopenharmony_ci} 880f66f451Sopenharmony_ci 890f66f451Sopenharmony_cistatic char *get_nextcolon(char *line, int cnt) 900f66f451Sopenharmony_ci{ 910f66f451Sopenharmony_ci while (cnt--) { 920f66f451Sopenharmony_ci if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n"); 930f66f451Sopenharmony_ci line++; //jump past the colon 940f66f451Sopenharmony_ci } 950f66f451Sopenharmony_ci return line; 960f66f451Sopenharmony_ci} 970f66f451Sopenharmony_ci 980f66f451Sopenharmony_ci/*update_password is used by multiple utilities to update /etc/passwd, 990f66f451Sopenharmony_ci * /etc/shadow, /etc/group and /etc/gshadow files, 1000f66f451Sopenharmony_ci * which are used as user, group databeses 1010f66f451Sopenharmony_ci * entry can be 1020f66f451Sopenharmony_ci * 1. encrypted password, when updating user password. 1030f66f451Sopenharmony_ci * 2. complete entry for user details, when creating new user 1040f66f451Sopenharmony_ci * 3. group members comma',' separated list, when adding user to group 1050f66f451Sopenharmony_ci * 4. complete entry for group details, when creating new group 1060f66f451Sopenharmony_ci * 5. entry = NULL, delete the named entry user/group 1070f66f451Sopenharmony_ci */ 1080f66f451Sopenharmony_ciint update_password(char *filename, char* username, char* entry) 1090f66f451Sopenharmony_ci{ 1100f66f451Sopenharmony_ci char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL, 1110f66f451Sopenharmony_ci *sfx = NULL, *line = NULL; 1120f66f451Sopenharmony_ci FILE *exfp, *newfp; 1130f66f451Sopenharmony_ci int ret = -1, found = 0, n; 1140f66f451Sopenharmony_ci struct flock lock; 1150f66f451Sopenharmony_ci size_t allocated_length; 1160f66f451Sopenharmony_ci 1170f66f451Sopenharmony_ci shadow = strstr(filename, "shadow"); 1180f66f451Sopenharmony_ci filenamesfx = xmprintf("%s+", filename); 1190f66f451Sopenharmony_ci sfx = strchr(filenamesfx, '+'); 1200f66f451Sopenharmony_ci 1210f66f451Sopenharmony_ci exfp = fopen(filename, "r+"); 1220f66f451Sopenharmony_ci if (!exfp) { 1230f66f451Sopenharmony_ci perror_msg("Couldn't open file %s",filename); 1240f66f451Sopenharmony_ci goto free_storage; 1250f66f451Sopenharmony_ci } 1260f66f451Sopenharmony_ci 1270f66f451Sopenharmony_ci *sfx = '-'; 1280f66f451Sopenharmony_ci unlink(filenamesfx); 1290f66f451Sopenharmony_ci ret = link(filename, filenamesfx); 1300f66f451Sopenharmony_ci if (ret < 0) error_msg("can't create backup file"); 1310f66f451Sopenharmony_ci 1320f66f451Sopenharmony_ci *sfx = '+'; 1330f66f451Sopenharmony_ci lock.l_type = F_WRLCK; 1340f66f451Sopenharmony_ci lock.l_whence = SEEK_SET; 1350f66f451Sopenharmony_ci lock.l_start = 0; 1360f66f451Sopenharmony_ci lock.l_len = 0; 1370f66f451Sopenharmony_ci 1380f66f451Sopenharmony_ci ret = fcntl(fileno(exfp), F_SETLK, &lock); 1390f66f451Sopenharmony_ci if (ret < 0) perror_msg("Couldn't lock file %s",filename); 1400f66f451Sopenharmony_ci 1410f66f451Sopenharmony_ci lock.l_type = F_UNLCK; //unlocking at a later stage 1420f66f451Sopenharmony_ci 1430f66f451Sopenharmony_ci newfp = fopen(filenamesfx, "w+"); 1440f66f451Sopenharmony_ci if (!newfp) { 1450f66f451Sopenharmony_ci error_msg("couldn't open file for writing"); 1460f66f451Sopenharmony_ci ret = -1; 1470f66f451Sopenharmony_ci fclose(exfp); 1480f66f451Sopenharmony_ci goto free_storage; 1490f66f451Sopenharmony_ci } 1500f66f451Sopenharmony_ci 1510f66f451Sopenharmony_ci ret = 0; 1520f66f451Sopenharmony_ci namesfx = xmprintf("%s:",username); 1530f66f451Sopenharmony_ci while ((n = getline(&line, &allocated_length, exfp)) > 0) { 1540f66f451Sopenharmony_ci line[n-1] = 0; 1550f66f451Sopenharmony_ci if (strncmp(line, namesfx, strlen(namesfx))) 1560f66f451Sopenharmony_ci fprintf(newfp, "%s\n", line); 1570f66f451Sopenharmony_ci else if (entry) { 1580f66f451Sopenharmony_ci char *current_ptr = NULL; 1590f66f451Sopenharmony_ci 1600f66f451Sopenharmony_ci found = 1; 1610f66f451Sopenharmony_ci if (!strcmp(toys.which->name, "passwd")) { 1620f66f451Sopenharmony_ci fprintf(newfp, "%s%s:",namesfx, entry); 1630f66f451Sopenharmony_ci current_ptr = get_nextcolon(line, 2); //past passwd 1640f66f451Sopenharmony_ci if (shadow) { 1650f66f451Sopenharmony_ci fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60)); 1660f66f451Sopenharmony_ci current_ptr = get_nextcolon(current_ptr, 1); 1670f66f451Sopenharmony_ci fprintf(newfp, "%s\n",current_ptr); 1680f66f451Sopenharmony_ci } else fprintf(newfp, "%s\n",current_ptr); 1690f66f451Sopenharmony_ci } else if (!strcmp(toys.which->name, "groupadd") || 1700f66f451Sopenharmony_ci !strcmp(toys.which->name, "addgroup") || 1710f66f451Sopenharmony_ci !strcmp(toys.which->name, "delgroup") || 1720f66f451Sopenharmony_ci !strcmp(toys.which->name, "groupdel")){ 1730f66f451Sopenharmony_ci current_ptr = get_nextcolon(line, 3); //past gid/admin list 1740f66f451Sopenharmony_ci *current_ptr = '\0'; 1750f66f451Sopenharmony_ci fprintf(newfp, "%s", line); 1760f66f451Sopenharmony_ci fprintf(newfp, "%s\n", entry); 1770f66f451Sopenharmony_ci } 1780f66f451Sopenharmony_ci } 1790f66f451Sopenharmony_ci } 1800f66f451Sopenharmony_ci free(line); 1810f66f451Sopenharmony_ci free(namesfx); 1820f66f451Sopenharmony_ci if (!found && entry) fprintf(newfp, "%s\n", entry); 1830f66f451Sopenharmony_ci fcntl(fileno(exfp), F_SETLK, &lock); 1840f66f451Sopenharmony_ci fclose(exfp); 1850f66f451Sopenharmony_ci 1860f66f451Sopenharmony_ci errno = 0; 1870f66f451Sopenharmony_ci fflush(newfp); 1880f66f451Sopenharmony_ci fsync(fileno(newfp)); 1890f66f451Sopenharmony_ci fclose(newfp); 1900f66f451Sopenharmony_ci rename(filenamesfx, filename); 1910f66f451Sopenharmony_ci if (errno) { 1920f66f451Sopenharmony_ci perror_msg("File Writing/Saving failed: "); 1930f66f451Sopenharmony_ci unlink(filenamesfx); 1940f66f451Sopenharmony_ci ret = -1; 1950f66f451Sopenharmony_ci } 1960f66f451Sopenharmony_ci 1970f66f451Sopenharmony_cifree_storage: 1980f66f451Sopenharmony_ci free(filenamesfx); 1990f66f451Sopenharmony_ci return ret; 2000f66f451Sopenharmony_ci} 201