mirror of
https://github.com/openssl/openssl.git
synced 2024-12-21 06:09:35 +08:00
0f3ffbd158
This mimics the behaviour of DES_crypt, where the salt and password are expected to be in EBCDIC when CHARSET_EBCDIC is defined, and are converted internally to ASCII. This is also done with the magic ID string if it's not already ASCII. The resulting output is entirely built up of ASCII components and is converted back to EBCDIC at the end. Reviewed-by: Andy Polyakov <appro@openssl.org> Reviewed-by: Paul Dale <paul.dale@oracle.com> (Merged from https://github.com/openssl/openssl/pull/4229)
853 lines
26 KiB
C
853 lines
26 KiB
C
/*
|
|
* Copyright 2000-2017 The OpenSSL Project Authors. All Rights Reserved.
|
|
*
|
|
* Licensed under the OpenSSL license (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 <string.h>
|
|
|
|
#include "apps.h"
|
|
|
|
#include <openssl/bio.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/rand.h>
|
|
#ifndef OPENSSL_NO_DES
|
|
# include <openssl/des.h>
|
|
#endif
|
|
#include <openssl/md5.h>
|
|
#include <openssl/sha.h>
|
|
|
|
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 };
|
|
|
|
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[] = {
|
|
{"help", OPT_HELP, '-', "Display this summary"},
|
|
{"in", OPT_IN, '<', "Read passwords from file"},
|
|
{"noverify", OPT_NOVERIFY, '-',
|
|
"Never verify when reading password from terminal"},
|
|
{"quiet", OPT_QUIET, '-', "No warnings"},
|
|
{"table", OPT_TABLE, '-', "Format output as table"},
|
|
{"reverse", OPT_REVERSE, '-', "Switch table columns"},
|
|
{"salt", OPT_SALT, 's', "Use provided salt"},
|
|
{"stdin", OPT_STDIN, '-', "Read passwords from stdin"},
|
|
{"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
|
|
OPT_R_OPTIONS,
|
|
{NULL}
|
|
};
|
|
|
|
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
|
|
|
|
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
|
|
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
|
|
|
|
/* 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
|
|
|
|
#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
|
|
|
|
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
|
|
}
|
|
|
|
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=<N>$......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
|
|
|
|
#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
|
|
|
|
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
|
|
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
|
|
|
|
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;
|
|
}
|