/* simple password generator by Nelson Minar (minar@reed.edu) * copyright 1991, all rights reserved. * You can use this code as long as my name stays with it. * * md5 patch by W. Campbell * Modernization, getopt, etc for the Hybrid IRCD team * by W. Campbell * * /dev/random for salt generation added by * Aaron Sethman */ #include #include #include #include #include #include #include "rb_lib.h" #ifndef __MINGW32__ #include #endif #define FLAG_MD5 0x00000001 #define FLAG_SALT 0x00000002 #define FLAG_PASS 0x00000004 #define FLAG_LENGTH 0x00000008 #define FLAG_BLOWFISH 0x00000010 #define FLAG_ROUNDS 0x00000020 #define FLAG_SHA256 0x00000040 #define FLAG_SHA512 0x00000080 static char *make_md5_salt(int); static char *make_md5_salt_para(char *); static char *make_sha256_salt(int); static char *make_sha256_salt_para(char *); static char *make_sha512_salt(int); static char *make_sha512_salt_para(char *); static char *make_bf_salt(int, int); static char *make_bf_salt_para(int, char *); static char *generate_random_salt(char *, int); static char *generate_poor_salt(char *, int); static void full_usage(void) __attribute__((noreturn)); static void brief_usage(void) __attribute__((noreturn)); static char saltChars[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* 0 .. 63, ascii - 64 */ #ifdef __MINGW32__ #include #ifdef PASS_MAX #undef PASS_MAX #endif #define PASS_MAX 256 static char getpassbuf[PASS_MAX + 1]; static char * getpass(const char *prompt) { int c; int i = 0; memset(getpassbuf, 0, sizeof(getpassbuf)); fputs(prompt, stderr); for(;;) { c = _getch(); if(c == '\r') { getpassbuf[i] = '\0'; break; } else if(i < PASS_MAX) { getpassbuf[i++] = c; } } fputs("\r\n", stderr); return getpassbuf; } #endif int main(int argc, char *argv[]) { char *plaintext = NULL; int c; char *saltpara = NULL; char *salt; char *hashed; int flag = 0; int length = 0; /* Not Set */ int rounds = 0; /* Not set, since blowfish needs 4 by default, a side effect * of this being the encryption type parameter must be * specified before the rounds parameter. */ while((c = getopt(argc, argv, "xymbr:h?l:s:p:")) != -1) { switch (c) { case 'm': flag |= FLAG_MD5; break; case 'b': flag |= FLAG_BLOWFISH; rounds = 4; break; case 'l': flag |= FLAG_LENGTH; length = atoi(optarg); break; case 'r': flag |= FLAG_ROUNDS; rounds = atoi(optarg); break; case 's': flag |= FLAG_SALT; saltpara = optarg; break; case 'p': flag |= FLAG_PASS; plaintext = optarg; break; case 'x': flag |= FLAG_SHA256; break; case 'y': flag |= FLAG_SHA512; break; case 'h': full_usage(); /* NOT REACHED */ break; case '?': brief_usage(); /* NOT REACHED */ break; default: printf("Invalid Option: -%c\n", c); break; } } if(flag & FLAG_MD5) { if(length == 0) length = 8; if(flag & FLAG_SALT) salt = make_md5_salt_para(saltpara); else salt = make_md5_salt(length); } else if(flag & FLAG_BLOWFISH) { if(length == 0) length = 22; if(flag & FLAG_SALT) salt = make_bf_salt_para(rounds, saltpara); else salt = make_bf_salt(rounds, length); } else if(flag & FLAG_SHA256) { if(length == 0) length = 16; if(flag & FLAG_SALT) salt = make_sha256_salt_para(saltpara); else salt = make_sha256_salt(length); } else { if(length == 0) length = 16; if(flag & FLAG_SALT) salt = make_sha512_salt_para(saltpara); else salt = make_sha512_salt(length); } if(flag & FLAG_PASS) { if(!plaintext) { fprintf(stderr, "Please enter a valid password\n"); return 1; } hashed = rb_crypt(plaintext, salt); } else { hashed = strdup(rb_crypt(getpass("plaintext: "), salt)); plaintext = getpass("again: "); if (strcmp(rb_crypt(plaintext, salt), hashed) != 0) { fprintf(stderr, "Passwords do not match\n"); return 1; } } printf("%s\n", hashed); return 0; } char * make_md5_salt_para(char *saltpara) { static char salt[21]; if(saltpara && (strlen(saltpara) <= 16)) { /* sprintf used because of portability requirements, the length ** is checked above, so it should not be too much of a concern */ sprintf(salt, "$1$%s$", saltpara); return salt; } printf("Invalid Salt, please use up to 16 random alphanumeric characters\n"); exit(1); /* NOT REACHED */ return NULL; } char * make_md5_salt(int length) { static char salt[21]; if(length > 16) { printf("MD5 salt length too long\n"); exit(0); } salt[0] = '$'; salt[1] = '1'; salt[2] = '$'; generate_random_salt(&salt[3], length); salt[length + 3] = '$'; salt[length + 4] = '\0'; return salt; } char * make_sha256_salt_para(char *saltpara) { static char salt[21]; if(saltpara && (strlen(saltpara) <= 16)) { /* sprintf used because of portability requirements, the length ** is checked above, so it should not be too much of a concern */ sprintf(salt, "$5$%s$", saltpara); return salt; } printf("Invalid Salt, please use up to 16 random alphanumeric characters\n"); exit(1); /* NOT REACHED */ return NULL; } char * make_sha512_salt_para(char *saltpara) { static char salt[21]; if(saltpara && (strlen(saltpara) <= 16)) { /* sprintf used because of portability requirements, the length ** is checked above, so it should not be too much of a concern */ sprintf(salt, "$6$%s$", saltpara); return salt; } printf("Invalid Salt, please use up to 16 random alphanumeric characters\n"); exit(1); /* NOT REACHED */ return NULL; } char * make_sha256_salt(int length) { static char salt[21]; if(length > 16) { printf("SHA256 salt length too long\n"); exit(0); } salt[0] = '$'; salt[1] = '5'; salt[2] = '$'; generate_random_salt(&salt[3], length); salt[length + 3] = '$'; salt[length + 4] = '\0'; return salt; } char * make_sha512_salt(int length) { static char salt[21]; if(length > 16) { printf("SHA512 salt length too long\n"); exit(0); } salt[0] = '$'; salt[1] = '6'; salt[2] = '$'; generate_random_salt(&salt[3], length); salt[length + 3] = '$'; salt[length + 4] = '\0'; return salt; } char * make_bf_salt_para(int rounds, char *saltpara) { static char salt[31]; char tbuf[3]; if(saltpara && (strlen(saltpara) <= 22)) { /* sprintf used because of portability requirements, the length ** is checked above, so it should not be too much of a concern */ sprintf(tbuf, "%02d", rounds); sprintf(salt, "$2a$%s$%s$", tbuf, saltpara); return salt; } printf("Invalid Salt, please use up to 22 random alphanumeric characters\n"); exit(1); /* NOT REACHED */ return NULL; } char * make_bf_salt(int rounds, int length) { static char salt[31]; char tbuf[3]; if(length > 22) { printf("Blowfish salt length too long\n"); exit(0); } sprintf(tbuf, "%02d", rounds); sprintf(salt, "$2a$%s$", tbuf); generate_random_salt(&salt[7], length); salt[length + 7] = '$'; salt[length + 8] = '\0'; return salt; } char * generate_poor_salt(char *salt, int length) { int i; srand(time(NULL)); for(i = 0; i < length; i++) { salt[i] = saltChars[rand() % 64]; } return (salt); } char * generate_random_salt(char *salt, int length) { char *buf; int fd, i; if((fd = open("/dev/random", O_RDONLY)) < 0) { return (generate_poor_salt(salt, length)); } buf = calloc(1, length); if(read(fd, buf, length) != length) { free(buf); return (generate_poor_salt(salt, length)); } for(i = 0; i < length; i++) { salt[i] = saltChars[abs(buf[i]) % 64]; } free(buf); return (salt); } void full_usage() { printf("mkpasswd [-m|-b|-x|-y] [-l saltlength] [-r rounds] [-s salt] [-p plaintext]\n"); printf("-x Generate a SHA256 password\n"); printf("-y Generate a SHA512 password\n"); printf("-m Generate an MD5 password\n"); printf("-b Generate a Blowfish password\n"); printf("-l Specify a length for a random MD5 or Blowfish salt\n"); printf("-r Specify a number of rounds for a Blowfish password\n"); printf(" Default 4, no more than 6 recommended\n"); printf("-s Specify a salt, up to 16 for MD5, SHA256, and SHA512\n"); printf(" up to 22 for Blowfish\n"); printf("-p Specify a plaintext password to use\n"); printf("Example: mkpasswd -m -s 3dr -p test\n"); exit(0); } void brief_usage() { printf("mkpasswd - password hash generator\n"); printf(" SHA512: mkpasswd [-y] [-l saltlength] [-s salt] [-p plaintext]\n"); printf(" SHA256: mkpasswd -x [-l saltlength] [-s salt] [-p plaintext]\n"); printf(" MD5: mkpasswd -m [-l saltlength] [-s salt] [-p plaintext]\n"); printf("Blowfish: mkpasswd -b [-r rounds] [-l saltlength] [-s salt]\n"); printf(" [-p plaintext]\n"); printf("Use -h for full usage\n"); exit(0); }