diff options
-rw-r--r-- | lib/lib.h | 5 | ||||
-rw-r--r-- | lib/password.c | 172 | ||||
-rw-r--r-- | toys/lsb/passwd.c | 113 |
3 files changed, 172 insertions, 118 deletions
@@ -187,6 +187,11 @@ mode_t string_to_mode(char *mode_str, mode_t base); void mode_to_string(mode_t mode, char *buf); // password helper functions +#define MAX_SALT_LEN 20 //3 for id, 16 for key, 1 for '\0' +#define SYS_FIRST_ID 100 +#define SYS_LAST_ID 999 +int get_salt(char *salt, char * algo); +void is_valid_username(const char *name); int read_password(char * buff, int buflen, char* mesg); int update_password(char *filename, char* username, char* encrypted); diff --git a/lib/password.c b/lib/password.c index d1489eb3..b340ef48 100644 --- a/lib/password.c +++ b/lib/password.c @@ -1,19 +1,87 @@ -/* pwdutils.c - password read/update helper functions. +/* password.c - password read/update helper functions. * * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com> */ #include "toys.h" +#include "xregcomp.h" #include <time.h> +static unsigned int random_number_generator(int fd) +{ + unsigned int randnum; + + xreadall(fd, &randnum, sizeof(randnum)); + return randnum; +} + +static char inttoc(int i) +{ + // salt value uses 64 chracters in "./0-9a-zA-Z" + const char character_set[]="./0123456789abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + i &= 0x3f; // masking for using 10 bits only + return character_set[i]; +} + +int get_salt(char *salt, char *algo) +{ + int i, randfd, salt_length = 0, offset; + + if (!strcmp(algo,"des")){ + // 2 bytes salt value is used in des + salt_length = 2; + offset = 0; + } else { + *salt++ = '$'; + if (!strcmp(algo,"md5")){ + *salt++ = '1'; + // 8 bytes salt value is used in md5 + salt_length = 8; + } else if (!strcmp(algo,"sha256")){ + *salt++ = '5'; + // 16 bytes salt value is used in sha256 + salt_length = 16; + } else if (!strcmp(algo,"sha512")){ + *salt++ = '6'; + // 16 bytes salt value is used in sha512 + salt_length = 16; + } else return -1; + + *salt++ = '$'; + offset = 3; + } + + randfd = xopen("/dev/urandom", O_RDONLY); + for (i=0; i<salt_length; i++) + salt[i] = inttoc(random_number_generator(randfd)); + salt[salt_length+1] = '\0'; + xclose(randfd); + + 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); @@ -25,7 +93,10 @@ int read_password(char * buff, int buflen, char* mesg) 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) { @@ -34,35 +105,45 @@ int read_password(char * buff, int buflen, char* mesg) } i++; } - + sigaction(SIGINT, &oldsa, NULL); tcsetattr(0, TCSANOW, &oldtermio); puts(""); fflush(stdout); return 0; } -static char *get_nextcolon(const char *line, char delim) +static char *get_nextcolon(char *line, int cnt) { - char *current_ptr = NULL; - if((current_ptr = strchr(line, ':')) == NULL) error_exit("Invalid Entry\n"); - return current_ptr; + while (cnt--) { + if (!(line = strchr(line, ':'))) error_exit("Invalid Entry\n"); + line++; //jump past the colon + } + return line; } -int update_password(char *filename, char* username, char* encrypted) +/*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 + */ +int update_password(char *filename, char* username, char* entry) { - char *filenamesfx = NULL, *namesfx = NULL; - char *shadow = NULL, *sfx = NULL; + char *filenamesfx = NULL, *namesfx = NULL, *shadow = NULL, + *sfx = NULL, *line = NULL; FILE *exfp, *newfp; - int ret = -1; //fail + int ret = -1, found = 0; struct flock lock; - char *line = NULL; shadow = strstr(filename, "shadow"); filenamesfx = xmsprintf("%s+", filename); sfx = strchr(filenamesfx, '+'); exfp = fopen(filename, "r+"); - if(!exfp) { + if (!exfp) { perror_msg("Couldn't open file %s",filename); goto free_storage; } @@ -70,7 +151,7 @@ int update_password(char *filename, char* username, char* encrypted) *sfx = '-'; ret = unlink(filenamesfx); ret = link(filename, filenamesfx); - if(ret < 0) error_msg("can't create backup file"); + if (ret < 0) error_msg("can't create backup file"); *sfx = '+'; lock.l_type = F_WRLCK; @@ -79,12 +160,12 @@ int update_password(char *filename, char* username, char* encrypted) lock.l_len = 0; ret = fcntl(fileno(exfp), F_SETLK, &lock); - if(ret < 0) perror_msg("Couldn't lock file %s",filename); + 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) { + if (!newfp) { error_msg("couldn't open file for writing"); ret = -1; fclose(exfp); @@ -93,29 +174,34 @@ int update_password(char *filename, char* username, char* encrypted) ret = 0; namesfx = xmsprintf("%s:",username); - while((line = get_line(fileno(exfp))) != NULL) + while ((line = get_line(fileno(exfp))) != NULL) { - if(strncmp(line, namesfx, strlen(namesfx)) != 0) + if (strncmp(line, namesfx, strlen(namesfx))) fprintf(newfp, "%s\n", line); else { char *current_ptr = NULL; - fprintf(newfp, "%s%s:",namesfx,encrypted); - current_ptr = get_nextcolon(line, ':'); //past username - current_ptr++; //past colon ':' after username - current_ptr = get_nextcolon(current_ptr, ':'); //past passwd - current_ptr++; //past colon ':' after passwd - if(shadow) { - fprintf(newfp, "%u:",(unsigned)(time(NULL))/(24*60*60)); - current_ptr = get_nextcolon(current_ptr, ':'); - current_ptr++; //past time stamp colon. - fprintf(newfp, "%s\n",current_ptr); + + 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")){ + current_ptr = get_nextcolon(line, 3); //past gid/admin list + *current_ptr = '\0'; + fprintf(newfp, "%s", line); + fprintf(newfp, "%s\n", entry); } - else fprintf(newfp, "%s\n",current_ptr); } - free(line); } free(namesfx); + if (!found) fprintf(newfp, "%s\n", entry); fcntl(fileno(exfp), F_SETLK, &lock); fclose(exfp); @@ -124,7 +210,7 @@ int update_password(char *filename, char* username, char* encrypted) fsync(fileno(newfp)); fclose(newfp); rename(filenamesfx, filename); - if(errno) { + if (errno) { perror_msg("File Writing/Saving failed: "); unlink(filenamesfx); ret = -1; @@ -134,3 +220,29 @@ 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")); +} diff --git a/toys/lsb/passwd.c b/toys/lsb/passwd.c index 167d5730..1784c049 100644 --- a/toys/lsb/passwd.c +++ b/toys/lsb/passwd.c @@ -11,9 +11,9 @@ config PASSWD bool "passwd" default y help - usage: passwd [-a ALGO] [-d] [-l] [-u] <account name> + usage: passwd [-a ALGO] [-dlu] <account name> - update user’s authentication tokens. Default : current user + update user's authentication tokens. Default : current user -a ALGO Encryption method (des, md5, sha256, sha512) default: des -d Set password to '' @@ -23,81 +23,25 @@ config PASSWD #define FOR_passwd #include "toys.h" -#include <time.h> GLOBALS( char *algo; ) -#define MAX_SALT_LEN 20 //3 for id, 16 for key, 1 for '\0' -#define URANDOM_PATH "/dev/urandom" - #ifndef _GNU_SOURCE char *strcasestr(const char *haystack, const char *needle); #endif -unsigned int random_number_generator(int fd) -{ - unsigned int randnum; - - xreadall(fd, &randnum, sizeof(randnum)); - return randnum; -} - -char inttoc(int i) -{ - // salt value uses 64 chracters in "./0-9a-zA-Z" - const char character_set[]="./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - i &= 0x3f; // masking for using 10 bits only - return character_set[i]; -} - -int get_salt(char *salt) -{ - int i, salt_length = 0; - int randfd; - - if (!strncmp(TT.algo,"des",3)) { - // 2 bytes salt value is used in des - salt_length = 2; - } else { - *salt++ = '$'; - if (!strncmp(TT.algo,"md5",3)) { - *salt++ = '1'; - // 8 bytes salt value is used in md5 - salt_length = 8; - } else if (!strncmp(TT.algo,"sha256",6)) { - *salt++ = '5'; - // 16 bytes salt value is used in sha256 - salt_length = 16; - } else if (!strncmp(TT.algo,"sha512",6)) { - *salt++ = '6'; - // 16 bytes salt value is used in sha512 - salt_length = 16; - } else return 1; - - *salt++ = '$'; - } - - randfd = xopen(URANDOM_PATH, O_RDONLY); - for (i=0; i<salt_length; i++) - salt[i] = inttoc(random_number_generator(randfd)); - salt[salt_length+1] = '\0'; - xclose(randfd); - - return 0; -} - static int str_check(char *s, char *p) { - if ((strcasestr(s, p) != NULL) || (strcasestr(p, s) != NULL)) return 1; + if (strcasestr(s, p) || strcasestr(p, s)) return 1; return 0; } static void strength_check(char *newp, char *oldp, char *user) { char *msg = NULL; + if (strlen(newp) < 6) { //Min passwd len msg = "too short"; xprintf("BAD PASSWORD: %s\n",msg); @@ -123,7 +67,7 @@ static int verify_passwd(char * pwd) if (pwd[0] == '!' || pwd[0] == '*') return 1; pass = crypt(toybuf, pwd); - if (pass != NULL && strcmp(pass, pwd)==0) return 0; + if (pass && !strcmp(pass, pwd)) return 0; return 1; } @@ -142,32 +86,27 @@ static char *new_password(char *oldp, char *user) return NULL; //may be due to Ctrl-C } - if (strcmp(newp, toybuf) == 0) return newp; + if (!strcmp(newp, toybuf)) return newp; else error_msg("Passwords do not match.\n"); - /*Failure Case */ + // Failure Case free(newp); - return NULL; } - void passwd_main(void) { uid_t myuid; struct passwd *pw; struct spwd *sp; - char *name = NULL; - char *pass = NULL, *encrypted = NULL, *newp = NULL; - char *orig = (char *)""; - char salt[MAX_SALT_LEN]; + char *name = NULL, *pass = NULL, *encrypted = NULL, *newp = NULL, + *orig = (char *)"", salt[MAX_SALT_LEN]; int ret = -1; myuid = getuid(); - if ((myuid != 0) && (toys.optflags & (FLAG_l | FLAG_u | FLAG_d))) + if ((myuid) && (toys.optflags & (FLAG_l | FLAG_u | FLAG_d))) error_exit("You need to be root to do these actions\n"); pw = getpwuid(myuid); - if (!pw) error_exit("Unknown uid '%u'",myuid); if (toys.optargs[0]) name = toys.optargs[0]; @@ -176,23 +115,28 @@ void passwd_main(void) pw = getpwnam(name); if (!pw) error_exit("Unknown user '%s'",name); - if (myuid != 0 && (myuid != pw->pw_uid)) + if (myuid && (myuid != pw->pw_uid)) error_exit("You need to be root to change '%s' password\n", name); pass = pw->pw_passwd; if (pw->pw_passwd[0] == 'x') { - /*get shadow passwd */ + //get shadow passwd sp = getspnam(name); if (sp) pass = sp->sp_pwdp; } if (!(toys.optflags & (FLAG_l | FLAG_u | FLAG_d))) { + + if (!(toys.optflags & FLAG_a)) TT.algo = "des"; + if (get_salt(salt, TT.algo) == -1) + error_exit("Error: Unkown encryption algorithm\n"); + printf("Changing password for %s\n",name); - if (pass[0] == '!') + if (myuid && pass[0] == '!') error_exit("Can't change, password is locked for %s",name); - if (myuid != 0) { - /*Validate user */ + if (myuid) { + //Validate user if (read_password(toybuf, sizeof(toybuf), "Origial password:")) { if (!toys.optargs[0]) free(name); @@ -204,7 +148,7 @@ void passwd_main(void) orig = xstrdup(orig); - /*Get new password */ + // Get new password newp = new_password(orig, name); if (!newp) { free(orig); @@ -212,31 +156,24 @@ void passwd_main(void) return; //new password is not set well. } - /*Encrypt the passwd */ - if (!(toys.optflags & FLAG_a)) TT.algo = "des"; - - if (get_salt(salt)) error_exit("Error: Unkown encryption algorithm\n"); - encrypted = crypt(newp, salt); free(newp); free(orig); } else if (toys.optflags & FLAG_l) { - if (pass[0] == '!') - error_exit("password is already locked for %s",name); + if (pass[0] == '!') error_exit("password is already locked for %s",name); printf("Locking password for %s\n",name); encrypted = xmsprintf("!%s",pass); } else if (toys.optflags & FLAG_u) { - if (pass[0] != '!') - error_exit("password is already unlocked for %s",name); + if (pass[0] != '!') error_exit("password is already unlocked for %s",name); printf("Unlocking password for %s\n",name); encrypted = xstrdup(&pass[1]); } else if (toys.optflags & FLAG_d) { printf("Deleting password for %s\n",name); - encrypted = (char*)xzalloc(sizeof(char)*2); //1 = "", 2 = '\0' + encrypted = xstrdup(""); //1 = "", 2 = '\0' } - /*Update the passwd */ + // Update the passwd if (pw->pw_passwd[0] == 'x') ret = update_password("/etc/shadow", name, encrypted); else ret = update_password("/etc/passwd", name, encrypted); |