buildroot/package/mkpasswd/mkpasswd.c
Stefan Becker 3c514c2dc5 host-mkpasswd: fix crash on Fedora 28 build host
crypt() is an optional glibc feature. Some distros, like Fedora 28, are
phasing it out to be replaced with libxcrypt [1]. Unfortunately this
change is only ABI compatible, not source code compatible, i.e. the code
will compile with warnings about undefined crypt(), but the resulting
binary will crash.

Follow the guidance in the Fedora bug and include crypt.h when
_XOPEN_CRYPT is not defined.

[1] https://bugzilla.redhat.com/show_bug.cgi?id=1566464

Signed-off-by: Stefan Becker <chemobejk@gmail.com>
Reviewed-by: "Yann E. MORIN" <yann.morin.1998@free.fr>
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
2018-05-03 21:56:20 +02:00

462 lines
12 KiB
C

/*
* Copyright (C) 2001-2008 Marco d'Itri
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* for crypt, snprintf and strcasecmp */
#define _XOPEN_SOURCE
#define _BSD_SOURCE
/* System library */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "config.h"
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#endif
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#ifdef HAVE_XCRYPT
#include <xcrypt.h>
#include <sys/stat.h>
#endif
#ifdef HAVE_LINUX_CRYPT_GENSALT
#define _OW_SOURCE
#include <crypt.h>
#endif
#ifdef HAVE_GETTIMEOFDAY
#include <sys/time.h>
#endif
/* glibc without crypt() */
#ifndef _XOPEN_CRYPT
#include <crypt.h>
#endif
/* Application-specific */
#include "utils.h"
/* Global variables */
#ifdef HAVE_GETOPT_LONG
static const struct option longopts[] = {
{"method", optional_argument, NULL, 'm'},
/* for backward compatibility with versions < 4.7.25 (< 20080321): */
{"hash", optional_argument, NULL, 'H'},
{"help", no_argument, NULL, 'h'},
{"password-fd", required_argument, NULL, 'P'},
{"stdin", no_argument, NULL, 's'},
{"salt", required_argument, NULL, 'S'},
{"rounds", required_argument, NULL, 'R'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0 }
};
#else
extern char *optarg;
extern int optind;
#endif
static const char valid_salts[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
struct crypt_method {
const char *method; /* short name used by the command line option */
const char *prefix; /* salt prefix */
const unsigned int minlen; /* minimum salt length */
const unsigned int maxlen; /* maximum salt length */
const unsigned int rounds; /* supports a variable number of rounds */
const char *desc; /* long description for the methods list */
};
static const struct crypt_method methods[] = {
/* method prefix minlen, maxlen rounds description */
{ "des", "", 2, 2, 0,
N_("standard 56 bit DES-based crypt(3)") },
{ "md5", "$1$", 8, 8, 0, "MD5" },
#if defined OpenBSD || defined FreeBSD || (defined __SVR4 && defined __sun)
{ "bf", "$2a$", 22, 22, 1, "Blowfish" },
#endif
#if defined HAVE_LINUX_CRYPT_GENSALT
{ "bf", "$2a$", 22, 22, 1, "Blowfish, system-specific on 8-bit chars" },
/* algorithm 2y fixes CVE-2011-2483 */
{ "bfy", "$2y$", 22, 22, 1, "Blowfish, correct handling of 8-bit chars" },
#endif
#if defined FreeBSD
{ "nt", "$3$", 0, 0, 0, "NT-Hash" },
#endif
#if defined HAVE_SHA_CRYPT
/* http://people.redhat.com/drepper/SHA-crypt.txt */
{ "sha-256", "$5$", 8, 16, 1, "SHA-256" },
{ "sha-512", "$6$", 8, 16, 1, "SHA-512" },
#endif
/* http://www.crypticide.com/dropsafe/article/1389 */
/*
* Actually the maximum salt length is arbitrary, but Solaris by default
* always uses 8 characters:
* http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/ \
* usr/src/lib/crypt_modules/sunmd5/sunmd5.c#crypt_gensalt_impl
*/
#if defined __SVR4 && defined __sun
{ "sunmd5", "$md5$", 8, 8, 1, "SunMD5" },
#endif
{ NULL, NULL, 0, 0, 0, NULL }
};
void generate_salt(char *const buf, const unsigned int len);
void *get_random_bytes(const int len);
void display_help(int error);
void display_version(void);
void display_methods(void);
int main(int argc, char *argv[])
{
int ch, i;
int password_fd = -1;
unsigned int salt_minlen = 0;
unsigned int salt_maxlen = 0;
unsigned int rounds_support = 0;
const char *salt_prefix = NULL;
const char *salt_arg = NULL;
unsigned int rounds = 0;
char *salt = NULL;
char rounds_str[30];
char *password = NULL;
#ifdef ENABLE_NLS
setlocale(LC_ALL, "");
bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
textdomain(NLS_CAT_NAME);
#endif
/* prepend options from environment */
argv = merge_args(getenv("MKPASSWD_OPTIONS"), argv, &argc);
while ((ch = GETOPT_LONGISH(argc, argv, "hH:m:5P:R:sS:V", longopts, 0))
> 0) {
switch (ch) {
case '5':
optarg = (char *) "md5";
/* fall through */
case 'm':
case 'H':
if (!optarg || strcaseeq("help", optarg)) {
display_methods();
exit(0);
}
for (i = 0; methods[i].method != NULL; i++)
if (strcaseeq(methods[i].method, optarg)) {
salt_prefix = methods[i].prefix;
salt_minlen = methods[i].minlen;
salt_maxlen = methods[i].maxlen;
rounds_support = methods[i].rounds;
break;
}
if (!salt_prefix) {
fprintf(stderr, _("Invalid method '%s'.\n"), optarg);
exit(1);
}
break;
case 'P':
{
char *p;
password_fd = strtol(optarg, &p, 10);
if (p == NULL || *p != '\0' || password_fd < 0) {
fprintf(stderr, _("Invalid number '%s'.\n"), optarg);
exit(1);
}
}
break;
case 'R':
{
char *p;
rounds = strtol(optarg, &p, 10);
if (p == NULL || *p != '\0' || rounds < 0) {
fprintf(stderr, _("Invalid number '%s'.\n"), optarg);
exit(1);
}
}
break;
case 's':
password_fd = 0;
break;
case 'S':
salt_arg = optarg;
break;
case 'V':
display_version();
exit(0);
case 'h':
display_help(EXIT_SUCCESS);
default:
fprintf(stderr, _("Try '%s --help' for more information.\n"),
argv[0]);
exit(1);
}
}
argc -= optind;
argv += optind;
if (argc == 2 && !salt_arg) {
password = argv[0];
salt_arg = argv[1];
} else if (argc == 1) {
password = argv[0];
} else if (argc == 0) {
} else {
display_help(EXIT_FAILURE);
}
/* default: DES password */
if (!salt_prefix) {
salt_minlen = methods[0].minlen;
salt_maxlen = methods[0].maxlen;
salt_prefix = methods[0].prefix;
}
if (streq(salt_prefix, "$2a$") || streq(salt_prefix, "$2y$")) {
/* OpenBSD Blowfish and derivatives */
if (rounds <= 5)
rounds = 5;
/* actually for 2a/2y it is the logarithm of the number of rounds */
snprintf(rounds_str, sizeof(rounds_str), "%02u$", rounds);
} else if (rounds_support && rounds)
snprintf(rounds_str, sizeof(rounds_str), "rounds=%u$", rounds);
else
rounds_str[0] = '\0';
if (salt_arg) {
unsigned int c = strlen(salt_arg);
if (c < salt_minlen || c > salt_maxlen) {
if (salt_minlen == salt_maxlen)
fprintf(stderr, ngettext(
"Wrong salt length: %d byte when %d expected.\n",
"Wrong salt length: %d bytes when %d expected.\n", c),
c, salt_maxlen);
else
fprintf(stderr, ngettext(
"Wrong salt length: %d byte when %d <= n <= %d"
" expected.\n",
"Wrong salt length: %d bytes when %d <= n <= %d"
" expected.\n", c),
c, salt_minlen, salt_maxlen);
exit(1);
}
while (c-- > 0) {
if (strchr(valid_salts, salt_arg[c]) == NULL) {
fprintf(stderr, _("Illegal salt character '%c'.\n"),
salt_arg[c]);
exit(1);
}
}
salt = NOFAIL(malloc(strlen(salt_prefix) + strlen(rounds_str)
+ strlen(salt_arg) + 1));
*salt = '\0';
strcat(salt, salt_prefix);
strcat(salt, rounds_str);
strcat(salt, salt_arg);
} else {
#ifdef HAVE_SOLARIS_CRYPT_GENSALT
#error "This code path is untested on Solaris. Please send a patch."
salt = crypt_gensalt(salt_prefix, NULL);
if (!salt)
perror(stderr, "crypt_gensalt");
#elif defined HAVE_LINUX_CRYPT_GENSALT
void *entropy = get_random_bytes(64);
salt = crypt_gensalt(salt_prefix, rounds, entropy, 64);
if (!salt) {
fprintf(stderr, "crypt_gensalt failed.\n");
exit(2);
}
free(entropy);
#else
unsigned int salt_len = salt_maxlen;
if (salt_minlen != salt_maxlen) { /* salt length can vary */
srand(time(NULL) + getpid());
salt_len = rand() % (salt_maxlen - salt_minlen + 1) + salt_minlen;
}
salt = NOFAIL(malloc(strlen(salt_prefix) + strlen(rounds_str)
+ salt_len + 1));
*salt = '\0';
strcat(salt, salt_prefix);
strcat(salt, rounds_str);
generate_salt(salt + strlen(salt), salt_len);
#endif
}
if (password) {
} else if (password_fd != -1) {
FILE *fp;
char *p;
if (isatty(password_fd))
fprintf(stderr, _("Password: "));
password = NOFAIL(malloc(128));
fp = fdopen(password_fd, "r");
if (!fp) {
perror("fdopen");
exit(2);
}
if (!fgets(password, 128, fp)) {
perror("fgets");
exit(2);
}
p = strpbrk(password, "\n\r");
if (p)
*p = '\0';
} else {
password = getpass(_("Password: "));
if (!password) {
perror("getpass");
exit(2);
}
}
{
const char *result;
result = crypt(password, salt);
/* xcrypt returns "*0" on errors */
if (!result || result[0] == '*') {
fprintf(stderr, "crypt failed.\n");
exit(2);
}
/* yes, using strlen(salt_prefix) on salt. It's not
* documented whether crypt_gensalt may change the prefix */
if (!strneq(result, salt, strlen(salt_prefix))) {
fprintf(stderr, _("Method not supported by crypt(3).\n"));
exit(2);
}
printf("%s\n", result);
}
exit(0);
}
#ifdef RANDOM_DEVICE
void* get_random_bytes(const int count)
{
char *buf;
int fd;
buf = NOFAIL(malloc(count));
fd = open(RANDOM_DEVICE, O_RDONLY);
if (fd < 0) {
perror("open(" RANDOM_DEVICE ")");
exit(2);
}
if (read(fd, buf, count) != count) {
if (count < 0)
perror("read(" RANDOM_DEVICE ")");
else
fprintf(stderr, "Short read of %s.\n", RANDOM_DEVICE);
exit(2);
}
close(fd);
return buf;
}
#endif
#ifdef RANDOM_DEVICE
void generate_salt(char *const buf, const unsigned int len)
{
unsigned int i;
unsigned char *entropy = get_random_bytes(len * sizeof(unsigned char));
for (i = 0; i < len; i++)
buf[i] = valid_salts[entropy[i] % (sizeof valid_salts - 1)];
buf[i] = '\0';
}
#else /* RANDOM_DEVICE */
void generate_salt(char *const buf, const unsigned int len)
{
unsigned int i;
# ifdef HAVE_GETTIMEOFDAY
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_sec ^ tv.tv_usec);
# else /* HAVE_GETTIMEOFDAY */
# warning "This system lacks a strong enough random numbers generator!"
/*
* The possible values of time over one year are 31536000, which is
* two orders of magnitude less than the allowed entropy range (2^32).
*/
srand(time(NULL) + getpid());
# endif /* HAVE_GETTIMEOFDAY */
for (i = 0; i < len; i++)
buf[i] = valid_salts[rand() % (sizeof valid_salts - 1)];
buf[i] = '\0';
}
#endif /* RANDOM_DEVICE */
void display_help(int error)
{
fprintf((EXIT_SUCCESS == error) ? stdout : stderr,
_("Usage: mkpasswd [OPTIONS]... [PASSWORD [SALT]]\n"
"Crypts the PASSWORD using crypt(3).\n\n"));
fprintf(stderr, _(
" -m, --method=TYPE select method TYPE\n"
" -5 like --method=md5\n"
" -S, --salt=SALT use the specified SALT\n"
" -R, --rounds=NUMBER use the specified NUMBER of rounds\n"
" -P, --password-fd=NUM read the password from file descriptor NUM\n"
" instead of /dev/tty\n"
" -s, --stdin like --password-fd=0\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
"\n"
"If PASSWORD is missing then it is asked interactively.\n"
"If no SALT is specified, a random one is generated.\n"
"If TYPE is 'help', available methods are printed.\n"
"\n"
"Report bugs to %s.\n"), "<md+whois@linux.it>");
exit(error);
}
void display_version(void)
{
printf("mkpasswd %s\n\n", VERSION);
puts("Copyright (C) 2001-2008 Marco d'Itri\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
}
void display_methods(void)
{
unsigned int i;
printf(_("Available methods:\n"));
for (i = 0; methods[i].method != NULL; i++)
printf("%s\t%s\n", methods[i].method, methods[i].desc);
}