blob: 13a431ac2f35cb5ac4a009aca9b372d092bfa245 [file] [log] [blame]
/* password.c - password read/update helper functions.
*
* Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
*/
#include "toys.h"
#include <time.h>
int get_salt(char *salt, char *algo)
{
int i, len = 0, offset = 0;
char buf[12];
if (!strcmp(algo,"des")) len = 2;
else {
*salt++ = '$';
if (!strcmp(algo,"md5")) {
*salt++ = '1';
len = 8;
} else if (!strcmp(algo,"sha256")) {
*salt++ = '5';
len = 16;
} else if (!strcmp(algo,"sha512")) {
*salt++ = '6';
len = 16;
} else return -1;
*salt++ = '$';
offset = 3;
}
// Read appropriate number of random bytes for salt
i = xopen("/dev/urandom", O_RDONLY);
xreadall(i, buf, ((len*6)+7)/8);
close(i);
// Grab 6 bit chunks and convert to characters in ./0-9a-zA-Z
for (i=0; i<len; i++) {
int bitpos = i*6, bits = bitpos/8;
bits = ((buf[i]+(buf[i+1]<<8)) >> (bitpos&7)) & 0x3f;
bits += 46;
if (bits > 57) bits += 7;
if (bits > 90) bits += 6;
salt[i] = bits;
}
salt[i] = 0;
return offset;
}
static void handle(int signo)
{
//Dummy.. so that read breaks on the signal,
//instead of the applocation exit
}
int read_password(char * buff, int buflen, char* mesg)
{
int i = 0;
struct termios termio, oldtermio;
struct sigaction sa, oldsa;
tcgetattr(0, &oldtermio);
tcflush(0, TCIFLUSH);
termio = oldtermio;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handle;
sigaction(SIGINT, &sa, &oldsa);
termio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
termio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
tcsetattr(0, TCSANOW, &termio);
fputs(mesg, stdout);
fflush(stdout);
while (1) {
int ret = read(0, &buff[i], 1);
if ( ret < 0 ) {
buff[0] = 0;
sigaction(SIGINT, &oldsa, NULL);
tcsetattr(0, TCSANOW, &oldtermio);
xputc('\n');
fflush(stdout);
return 1;
} else if (ret == 0 || buff[i] == '\n' || buff[i] == '\r' || buflen == i+1)
{
buff[i] = '\0';
break;
}
i++;
}
sigaction(SIGINT, &oldsa, NULL);
tcsetattr(0, TCSANOW, &oldtermio);
puts("");
fflush(stdout);
return 0;
}
static char *get_nextcolon(char *line, int cnt)
{
while (cnt--) {
if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n");
line++; //jump past the colon
}
return line;
}
/*update_password is used by multiple utilities to update /etc/passwd,
* /etc/shadow, /etc/group and /etc/gshadow files,
* which are used as user, group databeses
* entry can be
* 1. encrypted password, when updating user password.
* 2. complete entry for user details, when creating new user
* 3. group members comma',' separated list, when adding user to group
* 4. complete entry for group details, when creating new group
* 5. entry = NULL, delete the named entry user/group
*/
int update_password(char *filename, char* username, char* entry)
{
char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL,
*sfx = NULL, *line = NULL;
FILE *exfp, *newfp;
int ret = -1, found = 0;
struct flock lock;
shadow = strstr(filename, "shadow");
filenamesfx = xmprintf("%s+", filename);
sfx = strchr(filenamesfx, '+');
exfp = fopen(filename, "r+");
if (!exfp) {
perror_msg("Couldn't open file %s",filename);
goto free_storage;
}
*sfx = '-';
ret = unlink(filenamesfx);
ret = link(filename, filenamesfx);
if (ret < 0) error_msg("can't create backup file");
*sfx = '+';
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
ret = fcntl(fileno(exfp), F_SETLK, &lock);
if (ret < 0) perror_msg("Couldn't lock file %s",filename);
lock.l_type = F_UNLCK; //unlocking at a later stage
newfp = fopen(filenamesfx, "w+");
if (!newfp) {
error_msg("couldn't open file for writing");
ret = -1;
fclose(exfp);
goto free_storage;
}
ret = 0;
namesfx = xmprintf("%s:",username);
while ((line = get_line(fileno(exfp))) != NULL)
{
if (strncmp(line, namesfx, strlen(namesfx)))
fprintf(newfp, "%s\n", line);
else if (entry) {
char *current_ptr = NULL;
found = 1;
if (!strcmp(toys.which->name, "passwd")) {
fprintf(newfp, "%s%s:",namesfx, entry);
current_ptr = get_nextcolon(line, 2); //past passwd
if (shadow) {
fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60));
current_ptr = get_nextcolon(current_ptr, 1);
fprintf(newfp, "%s\n",current_ptr);
} else fprintf(newfp, "%s\n",current_ptr);
} else if (!strcmp(toys.which->name, "groupadd") ||
!strcmp(toys.which->name, "addgroup") ||
!strcmp(toys.which->name, "delgroup") ||
!strcmp(toys.which->name, "groupdel")){
current_ptr = get_nextcolon(line, 3); //past gid/admin list
*current_ptr = '\0';
fprintf(newfp, "%s", line);
fprintf(newfp, "%s\n", entry);
}
}
free(line);
}
free(namesfx);
if (!found && entry) fprintf(newfp, "%s\n", entry);
fcntl(fileno(exfp), F_SETLK, &lock);
fclose(exfp);
errno = 0;
fflush(newfp);
fsync(fileno(newfp));
fclose(newfp);
rename(filenamesfx, filename);
if (errno) {
perror_msg("File Writing/Saving failed: ");
unlink(filenamesfx);
ret = -1;
}
free_storage:
free(filenamesfx);
return ret;
}
void is_valid_username(const char *name)
{
regex_t rp;
regmatch_t rm[1];
int eval;
char *regex = "^[_.A-Za-z0-9][-_.A-Za-z0-9]*"; //User name REGEX
xregcomp(&rp, regex, REG_NEWLINE);
/* compare string against pattern -- remember that patterns
are anchored to the beginning of the line */
eval = regexec(&rp, name, 1, rm, 0);
regfree(&rp);
if (!eval && !rm[0].rm_so) {
int len = strlen(name);
if ((rm[0].rm_eo == len) ||
(rm[0].rm_eo == len - 1 && name[len - 1] == '$')) {
if (len >= LOGIN_NAME_MAX) error_exit("name is too long");
else return;
}
}
error_exit("'%s', not valid %sname",name,
(((toys.which->name[3] == 'g') ||
(toys.which->name[0] == 'g'))? "group" : "user"));
}