X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=apps%2Fpasswd.c;h=e305bd268d3f32c8b20947c96a5a9adcd8cf306b;hp=e991fefaa5e0ad2cf49a47ab2c45f7991500a3bb;hb=5388f9862d9aaf3c7cf7a70c1e36e7e983c26cfc;hpb=26a3a48d65c7464b400ec1de439994d7f0d25fed diff --git a/apps/passwd.c b/apps/passwd.c index e991fefaa5..e305bd268d 100644 --- a/apps/passwd.c +++ b/apps/passwd.c @@ -1,479 +1,861 @@ -/* apps/passwd.c */ - -#if defined NO_MD5 || defined CHARSET_EBCDIC -# define NO_APR1 -#endif - -#if !defined(NO_DES) || !defined(NO_APR1) +/* + * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ -#include #include #include "apps.h" +#include "progs.h" #include #include #include #include - -#ifndef NO_DES +#ifndef OPENSSL_NO_DES # include #endif -#ifndef NO_APR1 -# include -#endif +#include +#include + +static unsigned const char cov_2char[64] = { + /* from crypto/des/fcrypt.c */ + 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, + 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A +}; +static const char ascii_dollar[] = { 0x24, 0x00 }; -#undef PROG -#define PROG passwd_main +typedef enum { + passwd_unset = 0, + passwd_crypt, + passwd_md5, + passwd_apr1, + passwd_sha256, + passwd_sha512, + passwd_aixmd5 +} passwd_modes; +static int do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p, + char *passwd, BIO *out, int quiet, int table, + int reverse, size_t pw_maxlen, passwd_modes mode); + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_IN, + OPT_NOVERIFY, OPT_QUIET, OPT_TABLE, OPT_REVERSE, OPT_APR1, + OPT_1, OPT_5, OPT_6, OPT_CRYPT, OPT_AIXMD5, OPT_SALT, OPT_STDIN, + OPT_R_ENUM +} OPTION_CHOICE; + +const OPTIONS passwd_options[] = { + OPT_SECTION("General"), + {"help", OPT_HELP, '-', "Display this summary"}, + + OPT_SECTION("Input"), + {"in", OPT_IN, '<', "Read passwords from file"}, + {"noverify", OPT_NOVERIFY, '-', + "Never verify when reading password from terminal"}, + {"stdin", OPT_STDIN, '-', "Read passwords from stdin"}, + + OPT_SECTION("Output"), + {"quiet", OPT_QUIET, '-', "No warnings"}, + {"table", OPT_TABLE, '-', "Format output as table"}, + {"reverse", OPT_REVERSE, '-', "Switch table columns"}, + + OPT_SECTION("Cryptographic"), + {"salt", OPT_SALT, 's', "Use provided salt"}, + {"6", OPT_6, '-', "SHA512-based password algorithm"}, + {"5", OPT_5, '-', "SHA256-based password algorithm"}, + {"apr1", OPT_APR1, '-', "MD5-based password algorithm, Apache variant"}, + {"1", OPT_1, '-', "MD5-based password algorithm"}, + {"aixmd5", OPT_AIXMD5, '-', "AIX MD5-based password algorithm"}, +#ifndef OPENSSL_NO_DES + {"crypt", OPT_CRYPT, '-', "Standard Unix password algorithm (default)"}, +#endif -static unsigned const char cov_2char[64]={ - /* from crypto/des/fcrypt.c */ - 0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35, - 0x36,0x37,0x38,0x39,0x41,0x42,0x43,0x44, - 0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C, - 0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54, - 0x55,0x56,0x57,0x58,0x59,0x5A,0x61,0x62, - 0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A, - 0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72, - 0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A + OPT_R_OPTIONS, + {NULL} }; -static int do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p, - char *passwd, BIO *out, int quiet, int table, int reverse, - size_t pw_maxlen, int usecrypt, int useapr1); - -/* -crypt - standard Unix password algorithm (default, only choice) - * -apr1 - MD5-based password algorithm - * -salt string - salt - * -in file - read passwords from file - * -stdin - read passwords from stdin - * -quiet - no warnings - * -table - format output as table - * -reverse - switch table columns - */ +int passwd_main(int argc, char **argv) +{ + BIO *in = NULL; + char *infile = NULL, *salt = NULL, *passwd = NULL, **passwds = NULL; + char *salt_malloc = NULL, *passwd_malloc = NULL, *prog; + OPTION_CHOICE o; + int in_stdin = 0, pw_source_defined = 0; +#ifndef OPENSSL_NO_UI_CONSOLE + int in_noverify = 0; +#endif + int passed_salt = 0, quiet = 0, table = 0, reverse = 0; + int ret = 1; + passwd_modes mode = passwd_unset; + size_t passwd_malloc_size = 0; + size_t pw_maxlen = 256; /* arbitrary limit, should be enough for most + * passwords */ + + prog = opt_init(argc, argv, passwd_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + opthelp: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(passwd_options); + ret = 0; + goto end; + case OPT_IN: + if (pw_source_defined) + goto opthelp; + infile = opt_arg(); + pw_source_defined = 1; + break; + case OPT_NOVERIFY: +#ifndef OPENSSL_NO_UI_CONSOLE + in_noverify = 1; +#endif + break; + case OPT_QUIET: + quiet = 1; + break; + case OPT_TABLE: + table = 1; + break; + case OPT_REVERSE: + reverse = 1; + break; + case OPT_1: + if (mode != passwd_unset) + goto opthelp; + mode = passwd_md5; + break; + case OPT_5: + if (mode != passwd_unset) + goto opthelp; + mode = passwd_sha256; + break; + case OPT_6: + if (mode != passwd_unset) + goto opthelp; + mode = passwd_sha512; + break; + case OPT_APR1: + if (mode != passwd_unset) + goto opthelp; + mode = passwd_apr1; + break; + case OPT_AIXMD5: + if (mode != passwd_unset) + goto opthelp; + mode = passwd_aixmd5; + break; + case OPT_CRYPT: +#ifndef OPENSSL_NO_DES + if (mode != passwd_unset) + goto opthelp; + mode = passwd_crypt; +#endif + break; + case OPT_SALT: + passed_salt = 1; + salt = opt_arg(); + break; + case OPT_STDIN: + if (pw_source_defined) + goto opthelp; + in_stdin = 1; + pw_source_defined = 1; + break; + case OPT_R_CASES: + if (!opt_rand(o)) + goto end; + break; + } + } + argc = opt_num_rest(); + argv = opt_rest(); + + if (*argv != NULL) { + if (pw_source_defined) + goto opthelp; + pw_source_defined = 1; + passwds = argv; + } + + if (mode == passwd_unset) { + /* use default */ + mode = passwd_crypt; + } + +#ifdef OPENSSL_NO_DES + if (mode == passwd_crypt) + goto opthelp; +#endif -int MAIN(int, char **); - -int MAIN(int argc, char **argv) - { - int ret = 1; - char *infile = NULL; - int in_stdin = 0; - char *salt = NULL, *passwd = NULL, **passwds = NULL; - char *salt_malloc = NULL, *passwd_malloc = NULL; - size_t passwd_malloc_size = 0; - int pw_source_defined = 0; - BIO *in = NULL, *out = NULL; - int i, badopt, opt_done; - int passed_salt = 0, quiet = 0, table = 0, reverse = 0; - int usecrypt = 0, useapr1 = 0; - size_t pw_maxlen = 0; - - apps_startup(); - - if (bio_err == NULL) - if ((bio_err=BIO_new(BIO_s_file())) != NULL) - BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT); - out = BIO_new(BIO_s_file()); - if (out == NULL) - goto err; - BIO_set_fp(out, stdout, BIO_NOCLOSE | BIO_FP_TEXT); - - badopt = 0, opt_done = 0; - i = 0; - while (!badopt && !opt_done && argv[++i] != NULL) - { - if (strcmp(argv[i], "-crypt") == 0) - usecrypt = 1; - else if (strcmp(argv[i], "-apr1") == 0) - useapr1 = 1; - else if (strcmp(argv[i], "-salt") == 0) - { - if ((argv[i+1] != NULL) && (salt == NULL)) - { - passed_salt = 1; - salt = argv[++i]; - } - else - badopt = 1; - } - else if (strcmp(argv[i], "-in") == 0) - { - if ((argv[i+1] != NULL) && !pw_source_defined) - { - pw_source_defined = 1; - infile = argv[++i]; - } - else - badopt = 1; - } - else if (strcmp(argv[i], "-stdin") == 0) - { - if (!pw_source_defined) - { - pw_source_defined = 1; - in_stdin = 1; - } - else - badopt = 1; - } - else if (strcmp(argv[i], "-quiet") == 0) - quiet = 1; - else if (strcmp(argv[i], "-table") == 0) - table = 1; - else if (strcmp(argv[i], "-reverse") == 0) - reverse = 1; - else if (argv[i][0] == '-') - badopt = 1; - else if (!pw_source_defined) - /* non-option arguments, use as passwords */ - { - pw_source_defined = 1; - passwds = &argv[i]; - opt_done = 1; - } - else - badopt = 1; - } - - if (!usecrypt && !useapr1) /* use default */ - usecrypt = 1; - if (usecrypt + useapr1 > 1) /* conflict */ - badopt = 1; - - /* reject unsupported algorithms */ -#ifdef NO_DES - if (usecrypt) badopt = 1; + if (infile != NULL && in_stdin) { + BIO_printf(bio_err, "%s: Can't combine -in and -stdin\n", prog); + goto end; + } + + if (infile != NULL || in_stdin) { + /* + * If in_stdin is true, we know that infile is NULL, and that + * bio_open_default() will give us back an alias for stdin. + */ + in = bio_open_default(infile, 'r', FORMAT_TEXT); + if (in == NULL) + goto end; + } + + if (mode == passwd_crypt) + pw_maxlen = 8; + + if (passwds == NULL) { + /* no passwords on the command line */ + + passwd_malloc_size = pw_maxlen + 2; + /* longer than necessary so that we can warn about truncation */ + passwd = passwd_malloc = + app_malloc(passwd_malloc_size, "password buffer"); + } + + if ((in == NULL) && (passwds == NULL)) { + /* + * we use the following method to make sure what + * in the 'else' section is always compiled, to + * avoid rot of not-frequently-used code. + */ + if (1) { +#ifndef OPENSSL_NO_UI_CONSOLE + /* build a null-terminated list */ + static char *passwds_static[2] = { NULL, NULL }; + + passwds = passwds_static; + if (in == NULL) { + if (EVP_read_pw_string + (passwd_malloc, passwd_malloc_size, "Password: ", + !(passed_salt || in_noverify)) != 0) + goto end; + } + passwds[0] = passwd_malloc; + } else { #endif -#ifdef NO_APR1 - if (useapr1) badopt = 1; + BIO_printf(bio_err, "password required\n"); + goto end; + } + } + + if (in == NULL) { + assert(passwds != NULL); + assert(*passwds != NULL); + + do { /* loop over list of passwords */ + passwd = *passwds++; + if (!do_passwd(passed_salt, &salt, &salt_malloc, passwd, bio_out, + quiet, table, reverse, pw_maxlen, mode)) + goto end; + } while (*passwds != NULL); + } else { + /* in != NULL */ + int done; + + assert(passwd != NULL); + do { + int r = BIO_gets(in, passwd, pw_maxlen + 1); + if (r > 0) { + char *c = (strchr(passwd, '\n')); + if (c != NULL) { + *c = 0; /* truncate at newline */ + } else { + /* ignore rest of line */ + char trash[BUFSIZ]; + do + r = BIO_gets(in, trash, sizeof(trash)); + while ((r > 0) && (!strchr(trash, '\n'))); + } + + if (!do_passwd + (passed_salt, &salt, &salt_malloc, passwd, bio_out, quiet, + table, reverse, pw_maxlen, mode)) + goto end; + } + done = (r <= 0); + } while (!done); + } + ret = 0; + + end: +#if 0 + ERR_print_errors(bio_err); +#endif + OPENSSL_free(salt_malloc); + OPENSSL_free(passwd_malloc); + BIO_free(in); + return ret; +} + +/* + * MD5-based password algorithm (should probably be available as a library + * function; then the static buffer would not be acceptable). For magic + * string "1", this should be compatible to the MD5-based BSD password + * algorithm. For 'magic' string "apr1", this is compatible to the MD5-based + * Apache password algorithm. (Apparently, the Apache password algorithm is + * identical except that the 'magic' string was changed -- the laziest + * application of the NIH principle I've ever encountered.) + */ +static char *md5crypt(const char *passwd, const char *magic, const char *salt) +{ + /* "$apr1$..salt..$.......md5hash..........\0" */ + static char out_buf[6 + 9 + 24 + 2]; + unsigned char buf[MD5_DIGEST_LENGTH]; + char ascii_magic[5]; /* "apr1" plus '\0' */ + char ascii_salt[9]; /* Max 8 chars plus '\0' */ + char *ascii_passwd = NULL; + char *salt_out; + int n; + unsigned int i; + EVP_MD_CTX *md = NULL, *md2 = NULL; + size_t passwd_len, salt_len, magic_len; + + passwd_len = strlen(passwd); + + out_buf[0] = 0; + magic_len = strlen(magic); + OPENSSL_strlcpy(ascii_magic, magic, sizeof(ascii_magic)); +#ifdef CHARSET_EBCDIC + if ((magic[0] & 0x80) != 0) /* High bit is 1 in EBCDIC alnums */ + ebcdic2ascii(ascii_magic, ascii_magic, magic_len); #endif - if (badopt) - { - BIO_printf(bio_err, "Usage: passwd [options] [passwords]\n"); - BIO_printf(bio_err, "where options are\n"); -#ifndef NO_DES - BIO_printf(bio_err, "-crypt standard Unix password algorithm (default)\n"); + /* The salt gets truncated to 8 chars */ + OPENSSL_strlcpy(ascii_salt, salt, sizeof(ascii_salt)); + salt_len = strlen(ascii_salt); +#ifdef CHARSET_EBCDIC + ebcdic2ascii(ascii_salt, ascii_salt, salt_len); #endif -#ifndef NO_APR1 - BIO_printf(bio_err, "-apr1 MD5-based password algorithm\n"); + +#ifdef CHARSET_EBCDIC + ascii_passwd = OPENSSL_strdup(passwd); + if (ascii_passwd == NULL) + return NULL; + ebcdic2ascii(ascii_passwd, ascii_passwd, passwd_len); + passwd = ascii_passwd; #endif - BIO_printf(bio_err, "-salt string use provided salt\n"); - BIO_printf(bio_err, "-in file read passwords from file\n"); - BIO_printf(bio_err, "-stdin read passwords from stdin\n"); - BIO_printf(bio_err, "-quiet no warnings\n"); - BIO_printf(bio_err, "-table format output as table\n"); - BIO_printf(bio_err, "-reverse switch table columns\n"); - - goto err; - } - - if ((infile != NULL) || in_stdin) - { - in = BIO_new(BIO_s_file()); - if (in == NULL) - goto err; - if (infile != NULL) - { - assert(in_stdin == 0); - if (BIO_read_filename(in, infile) <= 0) - goto err; - } - else - { - assert(in_stdin); - BIO_set_fp(in, stdin, BIO_NOCLOSE); - } - } - - if (usecrypt) - pw_maxlen = 8; - else if (useapr1) - pw_maxlen = 256; /* arbitrary limit, should be enough for most passwords */ - - if (passwds == NULL) - { - /* no passwords on the command line */ - - passwd_malloc_size = pw_maxlen + 2; - /* longer than necessary so that we can warn about truncation */ - passwd = passwd_malloc = OPENSSL_malloc(passwd_malloc_size); - if (passwd_malloc == NULL) - goto err; - } - - if ((in == NULL) && (passwds == NULL)) - { - /* build a null-terminated list */ - static char *passwds_static[2] = {NULL, NULL}; - - passwds = passwds_static; - if (in == NULL) - if (EVP_read_pw_string(passwd_malloc, passwd_malloc_size, "Password: ", 0) != 0) - goto err; - passwds[0] = passwd_malloc; - } - - if (in == NULL) - { - assert(passwds != NULL); - assert(*passwds != NULL); - - do /* loop over list of passwords */ - { - passwd = *passwds++; - if (!do_passwd(passed_salt, &salt, &salt_malloc, passwd, out, - quiet, table, reverse, pw_maxlen, usecrypt, useapr1)) - goto err; - } - while (*passwds != NULL); - } - else - /* in != NULL */ - { - int done; - - assert (passwd != NULL); - do - { - int r = BIO_gets(in, passwd, pw_maxlen + 1); - if (r > 0) - { - char *c = (strchr(passwd, '\n')) ; - if (c != NULL) - *c = 0; /* truncate at newline */ - else - { - /* ignore rest of line */ - char trash[BUFSIZ]; - do - r = BIO_gets(in, trash, sizeof trash); - while ((r > 0) && (!strchr(trash, '\n'))); - } - - if (!do_passwd(passed_salt, &salt, &salt_malloc, passwd, out, - quiet, table, reverse, pw_maxlen, usecrypt, useapr1)) - goto err; - } - done = (r <= 0); - } - while (!done); - } - -err: - ERR_print_errors(bio_err); - if (salt_malloc) - OPENSSL_free(salt_malloc); - if (passwd_malloc) - OPENSSL_free(passwd_malloc); - if (in) - BIO_free(in); - if (out) - BIO_free(out); - EXIT(ret); - } - - -#ifndef NO_APR1 -/* MD5-based password algorithm compatible to the one found in Apache - * (should probably be available as a library function; - * then the static buffer would not be acceptable) */ -static char *apr1_crypt(const char *passwd, const char *salt) - { - static char out_buf[6 + 9 + 24 + 2]; /* "$apr1$..salt..$.......md5hash..........\0" */ - unsigned char buf[MD5_DIGEST_LENGTH]; - char *salt_out; - int n, i; - MD5_CTX md; - size_t passwd_len, salt_len; - - passwd_len = strlen(passwd); - strcpy(out_buf, "$apr1$"); - strncat(out_buf, salt, 8); - assert(strlen(out_buf) <= 6 + 8); /* "$apr1$..salt.." */ - salt_out = out_buf + 6; - salt_len = strlen(salt_out); - assert(salt_len <= 8); - - MD5_Init(&md); - MD5_Update(&md, passwd, passwd_len); - MD5_Update(&md, "$apr1$", 6); - MD5_Update(&md, salt_out, salt_len); - - { - MD5_CTX md2; - - MD5_Init(&md2); - MD5_Update(&md2, passwd, passwd_len); - MD5_Update(&md2, salt_out, salt_len); - MD5_Update(&md2, passwd, passwd_len); - MD5_Final(buf, &md2); - } - for (i = passwd_len; i > sizeof buf; i -= sizeof buf) - MD5_Update(&md, buf, sizeof buf); - MD5_Update(&md, buf, i); - - n = passwd_len; - while (n) - { - MD5_Update(&md, (n & 1) ? "\0" : passwd, 1); - n >>= 1; - } - MD5_Final(buf, &md); - - for (i = 0; i < 1000; i++) - { - MD5_CTX md2; - - MD5_Init(&md2); - MD5_Update(&md2, (i & 1) ? (unsigned char *) passwd : buf, - (i & 1) ? passwd_len : sizeof buf); - if (i % 3) - MD5_Update(&md2, salt_out, salt_len); - if (i % 7) - MD5_Update(&md2, passwd, passwd_len); - MD5_Update(&md2, (i & 1) ? buf : (unsigned char *) passwd, - (i & 1) ? sizeof buf : passwd_len); - MD5_Final(buf, &md2); - } - - { - /* transform buf into output string */ - - unsigned char buf_perm[sizeof buf]; - int dest, source; - char *output; - - /* silly output permutation */ - for (dest = 0, source = 0; dest < 14; dest++, source = (source + 6) % 17) - buf_perm[dest] = buf[source]; - buf_perm[14] = buf[5]; - buf_perm[15] = buf[11]; -#ifndef PEDANTIC /* Unfortunately, this generates a "no effect" warning */ - assert(16 == sizeof buf_perm); + + if (magic_len > 0) { + OPENSSL_strlcat(out_buf, ascii_dollar, sizeof(out_buf)); + + if (magic_len > 4) /* assert it's "1" or "apr1" */ + goto err; + + OPENSSL_strlcat(out_buf, ascii_magic, sizeof(out_buf)); + OPENSSL_strlcat(out_buf, ascii_dollar, sizeof(out_buf)); + } + + OPENSSL_strlcat(out_buf, ascii_salt, sizeof(out_buf)); + + if (strlen(out_buf) > 6 + 8) /* assert "$apr1$..salt.." */ + goto err; + + salt_out = out_buf; + if (magic_len > 0) + salt_out += 2 + magic_len; + + if (salt_len > 8) + goto err; + + md = EVP_MD_CTX_new(); + if (md == NULL + || !EVP_DigestInit_ex(md, EVP_md5(), NULL) + || !EVP_DigestUpdate(md, passwd, passwd_len)) + goto err; + + if (magic_len > 0) + if (!EVP_DigestUpdate(md, ascii_dollar, 1) + || !EVP_DigestUpdate(md, ascii_magic, magic_len) + || !EVP_DigestUpdate(md, ascii_dollar, 1)) + goto err; + + if (!EVP_DigestUpdate(md, ascii_salt, salt_len)) + goto err; + + md2 = EVP_MD_CTX_new(); + if (md2 == NULL + || !EVP_DigestInit_ex(md2, EVP_md5(), NULL) + || !EVP_DigestUpdate(md2, passwd, passwd_len) + || !EVP_DigestUpdate(md2, ascii_salt, salt_len) + || !EVP_DigestUpdate(md2, passwd, passwd_len) + || !EVP_DigestFinal_ex(md2, buf, NULL)) + goto err; + + for (i = passwd_len; i > sizeof(buf); i -= sizeof(buf)) { + if (!EVP_DigestUpdate(md, buf, sizeof(buf))) + goto err; + } + if (!EVP_DigestUpdate(md, buf, i)) + goto err; + + n = passwd_len; + while (n) { + if (!EVP_DigestUpdate(md, (n & 1) ? "\0" : passwd, 1)) + goto err; + n >>= 1; + } + if (!EVP_DigestFinal_ex(md, buf, NULL)) + return NULL; + + for (i = 0; i < 1000; i++) { + if (!EVP_DigestInit_ex(md2, EVP_md5(), NULL)) + goto err; + if (!EVP_DigestUpdate(md2, + (i & 1) ? (unsigned const char *)passwd : buf, + (i & 1) ? passwd_len : sizeof(buf))) + goto err; + if (i % 3) { + if (!EVP_DigestUpdate(md2, ascii_salt, salt_len)) + goto err; + } + if (i % 7) { + if (!EVP_DigestUpdate(md2, passwd, passwd_len)) + goto err; + } + if (!EVP_DigestUpdate(md2, + (i & 1) ? buf : (unsigned const char *)passwd, + (i & 1) ? sizeof(buf) : passwd_len)) + goto err; + if (!EVP_DigestFinal_ex(md2, buf, NULL)) + goto err; + } + EVP_MD_CTX_free(md2); + EVP_MD_CTX_free(md); + md2 = NULL; + md = NULL; + + { + /* transform buf into output string */ + unsigned char buf_perm[sizeof(buf)]; + int dest, source; + char *output; + + /* silly output permutation */ + for (dest = 0, source = 0; dest < 14; + dest++, source = (source + 6) % 17) + buf_perm[dest] = buf[source]; + buf_perm[14] = buf[5]; + buf_perm[15] = buf[11]; +# ifndef PEDANTIC /* Unfortunately, this generates a "no + * effect" warning */ + assert(16 == sizeof(buf_perm)); +# endif + + output = salt_out + salt_len; + assert(output == out_buf + strlen(out_buf)); + + *output++ = ascii_dollar[0]; + + for (i = 0; i < 15; i += 3) { + *output++ = cov_2char[buf_perm[i + 2] & 0x3f]; + *output++ = cov_2char[((buf_perm[i + 1] & 0xf) << 2) | + (buf_perm[i + 2] >> 6)]; + *output++ = cov_2char[((buf_perm[i] & 3) << 4) | + (buf_perm[i + 1] >> 4)]; + *output++ = cov_2char[buf_perm[i] >> 2]; + } + assert(i == 15); + *output++ = cov_2char[buf_perm[i] & 0x3f]; + *output++ = cov_2char[buf_perm[i] >> 6]; + *output = 0; + assert(strlen(out_buf) < sizeof(out_buf)); +#ifdef CHARSET_EBCDIC + ascii2ebcdic(out_buf, out_buf, strlen(out_buf)); #endif - - output = salt_out + salt_len; - assert(output == out_buf + strlen(out_buf)); - - *output++ = '$'; - - for (i = 0; i < 15; i += 3) - { - *output++ = cov_2char[buf_perm[i+2] & 0x3f]; - *output++ = cov_2char[((buf_perm[i+1] & 0xf) << 2) | - (buf_perm[i+2] >> 6)]; - *output++ = cov_2char[((buf_perm[i] & 3) << 4) | - (buf_perm[i+1] >> 4)]; - *output++ = cov_2char[buf_perm[i] >> 2]; - } - assert(i == 15); - *output++ = cov_2char[buf_perm[i] & 0x3f]; - *output++ = cov_2char[buf_perm[i] >> 6]; - *output = 0; - assert(strlen(out_buf) < sizeof(out_buf)); - } - - return out_buf; - } + } + + return out_buf; + + err: + OPENSSL_free(ascii_passwd); + EVP_MD_CTX_free(md2); + EVP_MD_CTX_free(md); + return NULL; +} + +/* + * SHA based password algorithm, describe by Ulrich Drepper here: + * https://www.akkadia.org/drepper/SHA-crypt.txt + * (note that it's in the public domain) + */ +static char *shacrypt(const char *passwd, const char *magic, const char *salt) +{ + /* Prefix for optional rounds specification. */ + static const char rounds_prefix[] = "rounds="; + /* Maximum salt string length. */ +# define SALT_LEN_MAX 16 + /* Default number of rounds if not explicitly specified. */ +# define ROUNDS_DEFAULT 5000 + /* Minimum number of rounds. */ +# define ROUNDS_MIN 1000 + /* Maximum number of rounds. */ +# define ROUNDS_MAX 999999999 + + /* "$6$rounds=$......salt......$...shahash(up to 86 chars)...\0" */ + static char out_buf[3 + 17 + 17 + 86 + 1]; + unsigned char buf[SHA512_DIGEST_LENGTH]; + unsigned char temp_buf[SHA512_DIGEST_LENGTH]; + size_t buf_size = 0; + char ascii_magic[2]; + char ascii_salt[17]; /* Max 16 chars plus '\0' */ + char *ascii_passwd = NULL; + size_t n; + EVP_MD_CTX *md = NULL, *md2 = NULL; + const EVP_MD *sha = NULL; + size_t passwd_len, salt_len, magic_len; + unsigned int rounds = 5000; /* Default */ + char rounds_custom = 0; + char *p_bytes = NULL; + char *s_bytes = NULL; + char *cp = NULL; + + passwd_len = strlen(passwd); + magic_len = strlen(magic); + + /* assert it's "5" or "6" */ + if (magic_len != 1) + return NULL; + + switch (magic[0]) { + case '5': + sha = EVP_sha256(); + buf_size = 32; + break; + case '6': + sha = EVP_sha512(); + buf_size = 64; + break; + default: + return NULL; + } + + if (strncmp(salt, rounds_prefix, sizeof(rounds_prefix) - 1) == 0) { + const char *num = salt + sizeof(rounds_prefix) - 1; + char *endp; + unsigned long int srounds = strtoul (num, &endp, 10); + if (*endp == '$') { + salt = endp + 1; + if (srounds > ROUNDS_MAX) + rounds = ROUNDS_MAX; + else if (srounds < ROUNDS_MIN) + rounds = ROUNDS_MIN; + else + rounds = (unsigned int)srounds; + rounds_custom = 1; + } else { + return NULL; + } + } + + OPENSSL_strlcpy(ascii_magic, magic, sizeof(ascii_magic)); +#ifdef CHARSET_EBCDIC + if ((magic[0] & 0x80) != 0) /* High bit is 1 in EBCDIC alnums */ + ebcdic2ascii(ascii_magic, ascii_magic, magic_len); #endif + /* The salt gets truncated to 16 chars */ + OPENSSL_strlcpy(ascii_salt, salt, sizeof(ascii_salt)); + salt_len = strlen(ascii_salt); +#ifdef CHARSET_EBCDIC + ebcdic2ascii(ascii_salt, ascii_salt, salt_len); +#endif -static int do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p, - char *passwd, BIO *out, int quiet, int table, int reverse, - size_t pw_maxlen, int usecrypt, int useapr1) - { - char *hash = NULL; - - assert(salt_p != NULL); - assert(salt_malloc_p != NULL); - - /* first make sure we have a salt */ - if (!passed_salt) - { -#ifndef NO_DES - if (usecrypt) - { - if (*salt_malloc_p == NULL) - { - *salt_p = *salt_malloc_p = OPENSSL_malloc(3); - if (*salt_malloc_p == NULL) - goto err; - } - if (RAND_pseudo_bytes((unsigned char *)*salt_p, 2) < 0) - goto err; - (*salt_p)[0] = cov_2char[(*salt_p)[0] & 0x3f]; /* 6 bits */ - (*salt_p)[1] = cov_2char[(*salt_p)[1] & 0x3f]; /* 6 bits */ - (*salt_p)[2] = 0; #ifdef CHARSET_EBCDIC - ascii2ebcdic(*salt_p, *salt_p, 2); /* des_crypt will convert - * back to ASCII */ + ascii_passwd = OPENSSL_strdup(passwd); + if (ascii_passwd == NULL) + return NULL; + ebcdic2ascii(ascii_passwd, ascii_passwd, passwd_len); + passwd = ascii_passwd; #endif - } -#endif /* !NO_DES */ - -#ifndef NO_APR1 - if (useapr1) - { - int i; - - if (*salt_malloc_p == NULL) - { - *salt_p = *salt_malloc_p = OPENSSL_malloc(9); - if (*salt_malloc_p == NULL) - goto err; - } - if (RAND_pseudo_bytes((unsigned char *)*salt_p, 8) < 0) - goto err; - - for (i = 0; i < 8; i++) - (*salt_p)[i] = cov_2char[(*salt_p)[i] & 0x3f]; /* 6 bits */ - (*salt_p)[8] = 0; - } -#endif /* !NO_APR1 */ - } - - assert(*salt_p != NULL); - - /* truncate password if necessary */ - if ((strlen(passwd) > pw_maxlen)) - { - if (!quiet) - BIO_printf(bio_err, "Warning: truncating password to %u characters\n", pw_maxlen); - passwd[pw_maxlen] = 0; - } - assert(strlen(passwd) <= pw_maxlen); - - /* now compute password hash */ -#ifndef NO_DES - if (usecrypt) - hash = des_crypt(passwd, *salt_p); + + out_buf[0] = 0; + OPENSSL_strlcat(out_buf, ascii_dollar, sizeof(out_buf)); + OPENSSL_strlcat(out_buf, ascii_magic, sizeof(out_buf)); + OPENSSL_strlcat(out_buf, ascii_dollar, sizeof(out_buf)); + if (rounds_custom) { + char tmp_buf[80]; /* "rounds=999999999" */ + sprintf(tmp_buf, "rounds=%u", rounds); +#ifdef CHARSET_EBCDIC + /* In case we're really on a ASCII based platform and just pretend */ + if (tmp_buf[0] != 0x72) /* ASCII 'r' */ + ebcdic2ascii(tmp_buf, tmp_buf, strlen(tmp_buf)); #endif -#ifndef NO_APR1 - if (useapr1) - hash = apr1_crypt(passwd, *salt_p); + OPENSSL_strlcat(out_buf, tmp_buf, sizeof(out_buf)); + OPENSSL_strlcat(out_buf, ascii_dollar, sizeof(out_buf)); + } + OPENSSL_strlcat(out_buf, ascii_salt, sizeof(out_buf)); + + /* assert "$5$rounds=999999999$......salt......" */ + if (strlen(out_buf) > 3 + 17 * rounds_custom + salt_len ) + goto err; + + md = EVP_MD_CTX_new(); + if (md == NULL + || !EVP_DigestInit_ex(md, sha, NULL) + || !EVP_DigestUpdate(md, passwd, passwd_len) + || !EVP_DigestUpdate(md, ascii_salt, salt_len)) + goto err; + + md2 = EVP_MD_CTX_new(); + if (md2 == NULL + || !EVP_DigestInit_ex(md2, sha, NULL) + || !EVP_DigestUpdate(md2, passwd, passwd_len) + || !EVP_DigestUpdate(md2, ascii_salt, salt_len) + || !EVP_DigestUpdate(md2, passwd, passwd_len) + || !EVP_DigestFinal_ex(md2, buf, NULL)) + goto err; + + for (n = passwd_len; n > buf_size; n -= buf_size) { + if (!EVP_DigestUpdate(md, buf, buf_size)) + goto err; + } + if (!EVP_DigestUpdate(md, buf, n)) + goto err; + + n = passwd_len; + while (n) { + if (!EVP_DigestUpdate(md, + (n & 1) ? buf : (unsigned const char *)passwd, + (n & 1) ? buf_size : passwd_len)) + goto err; + n >>= 1; + } + if (!EVP_DigestFinal_ex(md, buf, NULL)) + return NULL; + + /* P sequence */ + if (!EVP_DigestInit_ex(md2, sha, NULL)) + goto err; + + for (n = passwd_len; n > 0; n--) + if (!EVP_DigestUpdate(md2, passwd, passwd_len)) + goto err; + + if (!EVP_DigestFinal_ex(md2, temp_buf, NULL)) + return NULL; + + if ((p_bytes = OPENSSL_zalloc(passwd_len)) == NULL) + goto err; + for (cp = p_bytes, n = passwd_len; n > buf_size; n -= buf_size, cp += buf_size) + memcpy(cp, temp_buf, buf_size); + memcpy(cp, temp_buf, n); + + /* S sequence */ + if (!EVP_DigestInit_ex(md2, sha, NULL)) + goto err; + + for (n = 16 + buf[0]; n > 0; n--) + if (!EVP_DigestUpdate(md2, ascii_salt, salt_len)) + goto err; + + if (!EVP_DigestFinal_ex(md2, temp_buf, NULL)) + return NULL; + + if ((s_bytes = OPENSSL_zalloc(salt_len)) == NULL) + goto err; + for (cp = s_bytes, n = salt_len; n > buf_size; n -= buf_size, cp += buf_size) + memcpy(cp, temp_buf, buf_size); + memcpy(cp, temp_buf, n); + + for (n = 0; n < rounds; n++) { + if (!EVP_DigestInit_ex(md2, sha, NULL)) + goto err; + if (!EVP_DigestUpdate(md2, + (n & 1) ? (unsigned const char *)p_bytes : buf, + (n & 1) ? passwd_len : buf_size)) + goto err; + if (n % 3) { + if (!EVP_DigestUpdate(md2, s_bytes, salt_len)) + goto err; + } + if (n % 7) { + if (!EVP_DigestUpdate(md2, p_bytes, passwd_len)) + goto err; + } + if (!EVP_DigestUpdate(md2, + (n & 1) ? buf : (unsigned const char *)p_bytes, + (n & 1) ? buf_size : passwd_len)) + goto err; + if (!EVP_DigestFinal_ex(md2, buf, NULL)) + goto err; + } + EVP_MD_CTX_free(md2); + EVP_MD_CTX_free(md); + md2 = NULL; + md = NULL; + OPENSSL_free(p_bytes); + OPENSSL_free(s_bytes); + p_bytes = NULL; + s_bytes = NULL; + + cp = out_buf + strlen(out_buf); + *cp++ = ascii_dollar[0]; + +# define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int i = (N); \ + while (i-- > 0) \ + { \ + *cp++ = cov_2char[w & 0x3f]; \ + w >>= 6; \ + } \ + } while (0) + + switch (magic[0]) { + case '5': + b64_from_24bit (buf[0], buf[10], buf[20], 4); + b64_from_24bit (buf[21], buf[1], buf[11], 4); + b64_from_24bit (buf[12], buf[22], buf[2], 4); + b64_from_24bit (buf[3], buf[13], buf[23], 4); + b64_from_24bit (buf[24], buf[4], buf[14], 4); + b64_from_24bit (buf[15], buf[25], buf[5], 4); + b64_from_24bit (buf[6], buf[16], buf[26], 4); + b64_from_24bit (buf[27], buf[7], buf[17], 4); + b64_from_24bit (buf[18], buf[28], buf[8], 4); + b64_from_24bit (buf[9], buf[19], buf[29], 4); + b64_from_24bit (0, buf[31], buf[30], 3); + break; + case '6': + b64_from_24bit (buf[0], buf[21], buf[42], 4); + b64_from_24bit (buf[22], buf[43], buf[1], 4); + b64_from_24bit (buf[44], buf[2], buf[23], 4); + b64_from_24bit (buf[3], buf[24], buf[45], 4); + b64_from_24bit (buf[25], buf[46], buf[4], 4); + b64_from_24bit (buf[47], buf[5], buf[26], 4); + b64_from_24bit (buf[6], buf[27], buf[48], 4); + b64_from_24bit (buf[28], buf[49], buf[7], 4); + b64_from_24bit (buf[50], buf[8], buf[29], 4); + b64_from_24bit (buf[9], buf[30], buf[51], 4); + b64_from_24bit (buf[31], buf[52], buf[10], 4); + b64_from_24bit (buf[53], buf[11], buf[32], 4); + b64_from_24bit (buf[12], buf[33], buf[54], 4); + b64_from_24bit (buf[34], buf[55], buf[13], 4); + b64_from_24bit (buf[56], buf[14], buf[35], 4); + b64_from_24bit (buf[15], buf[36], buf[57], 4); + b64_from_24bit (buf[37], buf[58], buf[16], 4); + b64_from_24bit (buf[59], buf[17], buf[38], 4); + b64_from_24bit (buf[18], buf[39], buf[60], 4); + b64_from_24bit (buf[40], buf[61], buf[19], 4); + b64_from_24bit (buf[62], buf[20], buf[41], 4); + b64_from_24bit (0, 0, buf[63], 2); + break; + default: + goto err; + } + *cp = '\0'; +#ifdef CHARSET_EBCDIC + ascii2ebcdic(out_buf, out_buf, strlen(out_buf)); #endif - assert(hash != NULL); - - if (table && !reverse) - BIO_printf(out, "%s\t%s\n", passwd, hash); - else if (table && reverse) - BIO_printf(out, "%s\t%s\n", hash, passwd); - else - BIO_printf(out, "%s\n", hash); - return 1; - -err: - return 0; - } -#else - -int MAIN(int argc, char **argv) - { - fputs("Program not available.\n", stderr) - EXIT(1); - } + + return out_buf; + + err: + EVP_MD_CTX_free(md2); + EVP_MD_CTX_free(md); + OPENSSL_free(p_bytes); + OPENSSL_free(s_bytes); + OPENSSL_free(ascii_passwd); + return NULL; +} + +static int do_passwd(int passed_salt, char **salt_p, char **salt_malloc_p, + char *passwd, BIO *out, int quiet, int table, + int reverse, size_t pw_maxlen, passwd_modes mode) +{ + char *hash = NULL; + + assert(salt_p != NULL); + assert(salt_malloc_p != NULL); + + /* first make sure we have a salt */ + if (!passed_salt) { + size_t saltlen = 0; + size_t i; + +#ifndef OPENSSL_NO_DES + if (mode == passwd_crypt) + saltlen = 2; +#endif /* !OPENSSL_NO_DES */ + + if (mode == passwd_md5 || mode == passwd_apr1 || mode == passwd_aixmd5) + saltlen = 8; + + if (mode == passwd_sha256 || mode == passwd_sha512) + saltlen = 16; + + assert(saltlen != 0); + + if (*salt_malloc_p == NULL) + *salt_p = *salt_malloc_p = app_malloc(saltlen + 1, "salt buffer"); + if (RAND_bytes((unsigned char *)*salt_p, saltlen) <= 0) + goto end; + + for (i = 0; i < saltlen; i++) + (*salt_p)[i] = cov_2char[(*salt_p)[i] & 0x3f]; /* 6 bits */ + (*salt_p)[i] = 0; +# ifdef CHARSET_EBCDIC + /* The password encryption funtion will convert back to ASCII */ + ascii2ebcdic(*salt_p, *salt_p, saltlen); +# endif + } + + assert(*salt_p != NULL); + + /* truncate password if necessary */ + if ((strlen(passwd) > pw_maxlen)) { + if (!quiet) + /* + * XXX: really we should know how to print a size_t, not cast it + */ + BIO_printf(bio_err, + "Warning: truncating password to %u characters\n", + (unsigned)pw_maxlen); + passwd[pw_maxlen] = 0; + } + assert(strlen(passwd) <= pw_maxlen); + + /* now compute password hash */ +#ifndef OPENSSL_NO_DES + if (mode == passwd_crypt) + hash = DES_crypt(passwd, *salt_p); #endif + if (mode == passwd_md5 || mode == passwd_apr1) + hash = md5crypt(passwd, (mode == passwd_md5 ? "1" : "apr1"), *salt_p); + if (mode == passwd_aixmd5) + hash = md5crypt(passwd, "", *salt_p); + if (mode == passwd_sha256 || mode == passwd_sha512) + hash = shacrypt(passwd, (mode == passwd_sha256 ? "5" : "6"), *salt_p); + assert(hash != NULL); + + if (table && !reverse) + BIO_printf(out, "%s\t%s\n", passwd, hash); + else if (table && reverse) + BIO_printf(out, "%s\t%s\n", hash, passwd); + else + BIO_printf(out, "%s\n", hash); + return 1; + + end: + return 0; +}